@foir/sdk 0.7.0 → 0.8.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/CHANGELOG.md +805 -4
- package/README.md +46 -15
- package/dist/auth/auth.d.ts +104 -1
- package/dist/auth/index.d.ts +12 -6
- package/dist/auth/operations.d.ts +85 -64
- package/dist/auth/ops.d.ts +99 -0
- package/dist/auth.js +775 -106
- package/dist/chunk-IGNLWT3H.js +35 -0
- package/dist/chunk-OHYVKTGB.js +45 -0
- package/dist/chunk-TAL5H6VA.js +39 -0
- package/dist/client.js +25 -31
- package/dist/codegen/block-registry.d.ts +13 -19
- package/dist/codegen/block-registry.js +3 -31
- package/dist/codegen/block-scaffold.d.ts +12 -21
- package/dist/codegen/block-scaffold.js +3 -41
- package/dist/codegen/index.d.ts +9 -4
- package/dist/codegen/index.js +7 -11
- package/dist/codegen/item-registry.d.ts +28 -0
- package/dist/codegen/item-registry.js +7 -0
- package/dist/codegen/item-scaffold.d.ts +29 -0
- package/dist/codegen/item-scaffold.js +7 -0
- package/dist/codegen/preset.d.ts +14 -7
- package/dist/codegen/shared.d.ts +20 -9
- package/dist/content/content-renderer.d.ts +90 -24
- package/dist/content/index.d.ts +13 -7
- package/dist/content/richtext.d.ts +66 -18
- package/dist/content.js +227 -80
- package/dist/data/builtins.d.ts +302 -92
- package/dist/data/client.d.ts +97 -25
- package/dist/data/document.d.ts +251 -0
- package/dist/data/index.d.ts +38 -19
- package/dist/data/manifest.d.ts +266 -3
- package/dist/data/schema.d.ts +78 -8
- package/dist/data/selection.d.ts +231 -37
- package/dist/data/subjects.d.ts +221 -0
- package/dist/data/subscription-document.d.ts +75 -0
- package/dist/data/subscriptions.d.ts +181 -0
- package/dist/data/transport.d.ts +255 -37
- package/dist/data/ws-transport.d.ts +197 -0
- package/dist/data.js +1419 -45
- package/dist/graphql/cdn-route.d.ts +12 -28
- package/dist/media/index.d.ts +6 -2
- package/dist/media/media.d.ts +2 -2
- package/dist/media/screen-capture.d.ts +81 -0
- package/dist/media.js +97 -0
- package/dist/next.js +663 -182
- package/dist/push.js +700 -90
- package/dist/react/FoirFeedback.d.ts +62 -0
- package/dist/react/feedback-notes.d.ts +101 -0
- package/dist/react/index.d.ts +7 -1
- package/dist/react.js +1272 -194
- package/dist/realtime.js +1 -1
- package/dist/server/events-client.d.ts +279 -0
- package/dist/server/index.d.ts +4 -0
- package/dist/server.js +298 -0
- package/dist/transport/foir-http.d.ts +23 -6
- package/dist/transport/persisted-query.d.ts +68 -0
- package/dist/transport/retry.d.ts +1 -1
- package/package.json +13 -1
- package/dist/chunk-ADO5FJNM.js +0 -30
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ pnpm add yjs y-websocket # for @foir/sdk/realtime
|
|
|
24
24
|
| --- | --- | --- |
|
|
25
25
|
| `@foir/sdk` | Editor iframe client — `EditorProvider`, `useEditor`, `useAutoResize`, `createPlatformClient`, host↔editor message protocol | `react` |
|
|
26
26
|
| `@foir/sdk/client` | Client-agnostic GraphQL transport for SSR/edge loaders — `foirRequest`, `isFoirPreview`, `absolutiseFoirMediaUrl` | `graphql` |
|
|
27
|
-
| `@foir/sdk/data` | Typed data client over `
|
|
27
|
+
| `@foir/sdk/data` | Typed data client over `/graphql` — `createFoirClient`, per-key-shaped ops, `include`/`omit`, `fetchFoirManifest`; prints the document for you, so still no `graphql` | **none** |
|
|
28
28
|
| `@foir/sdk/auth` | Framework-agnostic customer auth — login/register/OTP/provider flows, token lifecycle (`SessionAdapter`), `createFoirSessionClient` (Foir-hosted cookie sessions), `authedRequest`, guards | `graphql` |
|
|
29
29
|
| `@foir/sdk/react` | Drop-in React auth — `<FoirProvider>` (token or `hostedSession` mode), `useFoirSession`/`useFoirUser`/`useFoirAuth`, `<SignIn>`, `<OAuthButtons>`, `<UserButton>` | `react`, `graphql` |
|
|
30
30
|
| `@foir/sdk/next` | Next.js App Router BFF auth — `createFoirCookieAdapter`, `getFoirSession`, `foirSignIn*`, `completeFoirProviderLogin`, `createFoirMiddleware` | `graphql` |
|
|
@@ -38,21 +38,25 @@ pnpm add yjs y-websocket # for @foir/sdk/realtime
|
|
|
38
38
|
|
|
39
39
|
### Typed data client (no GraphQL, no codegen step)
|
|
40
40
|
|
|
41
|
-
`@foir/sdk/data`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
`@foir/sdk/data` executes over the same `/graphql` endpoint every other consumer
|
|
42
|
+
uses — one execution surface, one executor, one set of scope checks, one response
|
|
43
|
+
cache. What you skip is writing the documents: the client loads the selection
|
|
44
|
+
tree `GET /v1/manifest` publishes for that op, applies your `include`/`omit`, and
|
|
45
|
+
prints one.
|
|
46
|
+
|
|
47
|
+
Printing a pre-resolved tree is string building, so the package still needs **no
|
|
48
|
+
`graphql` dependency at all** — no `graphql`, no
|
|
49
|
+
`@graphql-typed-document-node/core`, no `@graphql-codegen/*`, and no new peer.
|
|
46
50
|
|
|
47
51
|
```ts
|
|
48
52
|
import { createFoirClient } from '@foir/sdk/data';
|
|
49
53
|
// generated by `foir types` from GET /v1/manifest
|
|
50
|
-
import
|
|
54
|
+
import * as generated from './foir.generated';
|
|
51
55
|
|
|
52
|
-
const foir = createFoirClient
|
|
56
|
+
const foir = createFoirClient({
|
|
53
57
|
apiUrl: process.env.PLATFORM_API_URL!, // a bare origin or the /graphql URL
|
|
54
58
|
apiKey: process.env.PLATFORM_API_KEY!,
|
|
55
|
-
|
|
59
|
+
generated, // the whole module: types, trees, version
|
|
56
60
|
});
|
|
57
61
|
|
|
58
62
|
const post = await foir.post.findUnique({
|
|
@@ -63,9 +67,20 @@ const post = await foir.post.findUnique({
|
|
|
63
67
|
|
|
64
68
|
const posts = await foir.post.findMany({ first: 10 }); // a Relay connection
|
|
65
69
|
const hits = await foir.ns.search.searchRecords({ query: 'shoes' });
|
|
66
|
-
const other = await foir.raw({
|
|
70
|
+
const other = await foir.raw('{ designTokens { colors } }'); // any document, any root field
|
|
67
71
|
```
|
|
68
72
|
|
|
73
|
+
**One import, no type parameter.** `generated` carries both halves of what
|
|
74
|
+
`foir types` writes — the `FoirSchema` type and the runtime selection trees the
|
|
75
|
+
documents are printed from — so `FoirSchema` is inferred rather than restated,
|
|
76
|
+
and an artifact added to the generator later costs your call site nothing. A
|
|
77
|
+
module missing one of those exports is a compile error naming it, not a quiet
|
|
78
|
+
degrade: pass it whole, or do not pass it.
|
|
79
|
+
|
|
80
|
+
The four fields it replaces (`schemaVersion`, `selections`,
|
|
81
|
+
`namespaceSelections`, `selectionUnions`) still work and still win where both
|
|
82
|
+
are given, so an existing client keeps compiling and behaving exactly as it did.
|
|
83
|
+
|
|
69
84
|
**Scalars are always complete**, so `post.title` is typed present and *is*
|
|
70
85
|
present — there is no partial-`select` footgun. `include` adds a relation key,
|
|
71
86
|
`omit` removes a scalar, and polymorphic fields are discriminated unions on
|
|
@@ -79,24 +94,40 @@ call:
|
|
|
79
94
|
foir.post.create({ data }); // ✗ compile error under a pk_ key — not in its manifest
|
|
80
95
|
```
|
|
81
96
|
|
|
97
|
+
`generated` is optional. Without it — and without `selections` — the client
|
|
98
|
+
fetches `GET /v1/manifest` once at first use and reads the same trees off it. A
|
|
99
|
+
fallback, so a zero-codegen start works, at the cost of a round trip in front of
|
|
100
|
+
the first query on every cold invocation. It says so, once, when it is built
|
|
101
|
+
that way; silence it with `announceManifestFallback: false`.
|
|
102
|
+
|
|
103
|
+
Reads that can be shared are sent to `GET /cdn/graphql` with only the
|
|
104
|
+
persisted-query hash in the URL, so a CDN in front of you can serve the next
|
|
105
|
+
visitor without reaching origin. That is a query, on a `pk_` key, with no
|
|
106
|
+
customer bearer, on the published channel; everything else is a `POST /graphql`.
|
|
107
|
+
|
|
108
|
+
`foir.raw()` is the escape hatch, and it takes a **GraphQL document** — it
|
|
109
|
+
reaches anything the credential can, including root fields no model or namespace
|
|
110
|
+
claims. It returns the whole `data` object.
|
|
111
|
+
|
|
82
112
|
Namespaces (`search`, `customers`, `notifications`) live under `foir.ns` because
|
|
83
|
-
|
|
84
|
-
|
|
113
|
+
the client carries no runtime schema to tell a namespace from a model. `ns` and
|
|
114
|
+
`raw` are the only reserved names.
|
|
85
115
|
|
|
86
116
|
Session-bound reads take a `bearerToken`, as a string or a function resolved per
|
|
87
117
|
request:
|
|
88
118
|
|
|
89
119
|
```ts
|
|
90
|
-
const foir = createFoirClient
|
|
120
|
+
const foir = createFoirClient({
|
|
91
121
|
apiUrl,
|
|
92
122
|
apiKey,
|
|
123
|
+
generated,
|
|
93
124
|
bearerToken: () => session.get('foir_access_token'),
|
|
94
125
|
});
|
|
95
126
|
```
|
|
96
127
|
|
|
97
128
|
Already have `PLATFORM_API_URL` / `PLATFORM_API_KEY` / `FOIR_PREVIEW` wired for
|
|
98
|
-
the GraphQL path? `createFoirClientFromEnv(env)` reuses them,
|
|
99
|
-
preview `sk_` key and the draft schema channel.
|
|
129
|
+
the GraphQL path? `createFoirClientFromEnv(env, { generated })` reuses them,
|
|
130
|
+
including the preview `sk_` key and the draft schema channel.
|
|
100
131
|
|
|
101
132
|
### Typed GraphQL from a server loader
|
|
102
133
|
|
package/dist/auth/auth.d.ts
CHANGED
|
@@ -6,9 +6,17 @@
|
|
|
6
6
|
* post-login reads add the customer bearer. The project-specific profile (e.g.
|
|
7
7
|
* `isAdmin`) is resolved by a consumer-supplied `resolveUser` — the SDK never
|
|
8
8
|
* hardcodes the per-project customer profile shape.
|
|
9
|
+
*
|
|
10
|
+
* TRANSPORT. Every flow below addresses the server's generated `auth` namespace
|
|
11
|
+
* through `./ops`, which is `@foir/sdk/data`'s op transport. This module holds
|
|
12
|
+
* no GraphQL documents and issues no requests of its own. What it does hold is
|
|
13
|
+
* the part a generated namespace does not give you and never could: where the
|
|
14
|
+
* tokens live, when they rotate, what a 401 means, and what to clear when a
|
|
15
|
+
* session ends.
|
|
9
16
|
*/
|
|
10
17
|
import { type FoirEnv } from '../graphql/foir-request';
|
|
11
|
-
import { type
|
|
18
|
+
import { type AuthOpName, type AuthOpOptions } from './ops';
|
|
19
|
+
import { type AuthConfig, type AuthProviderPublic, type CurrentUser, type CustomerAuthResult, type DefaultAuthProvider, type DeleteAccountResult, type ProviderCallbackInput, type ProviderLoginInput, type ProviderLoginResult, type ProviderOTPVerifyInput, type ProviderToken, type SignupContext, type WaitlistJoinResult } from './operations';
|
|
12
20
|
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
13
21
|
/** An access token + its rotating refresh token. */
|
|
14
22
|
export interface FoirAuthTokens {
|
|
@@ -65,6 +73,17 @@ export declare function customerProviderCallback(env: FoirEnv, input: ProviderCa
|
|
|
65
73
|
export declare function customerProviderVerifyOTP(env: FoirEnv, input: ProviderOTPVerifyInput): Promise<CustomerAuthResult>;
|
|
66
74
|
/** List the project's configured auth providers (for a login UI). */
|
|
67
75
|
export declare function getAuthProviders(env: FoirEnv): Promise<AuthProviderPublic[]>;
|
|
76
|
+
/**
|
|
77
|
+
* Read the project's public auth configuration — which built-in methods are
|
|
78
|
+
* enabled and which external providers to offer. This is what a login screen
|
|
79
|
+
* renders itself from, before any customer token exists.
|
|
80
|
+
*/
|
|
81
|
+
export declare function getAuthConfig(env: FoirEnv): Promise<AuthConfig>;
|
|
82
|
+
/**
|
|
83
|
+
* The provider a login screen should default to, or `null` when the project has
|
|
84
|
+
* configured none.
|
|
85
|
+
*/
|
|
86
|
+
export declare function getDefaultAuthProvider(env: FoirEnv): Promise<DefaultAuthProvider | null>;
|
|
68
87
|
/**
|
|
69
88
|
* Read the project's customer signup mode (public/restricted/waitlist/closed)
|
|
70
89
|
* so a self-hosted frontend can render the right signup surface proactively.
|
|
@@ -72,6 +91,57 @@ export declare function getAuthProviders(env: FoirEnv): Promise<AuthProviderPubl
|
|
|
72
91
|
export declare function customerSignupContext(env: FoirEnv): Promise<SignupContext>;
|
|
73
92
|
/** Join the project's signup waitlist. The project must be in waitlist mode. */
|
|
74
93
|
export declare function customerJoinWaitlist(env: FoirEnv, email: string): Promise<WaitlistJoinResult>;
|
|
94
|
+
/**
|
|
95
|
+
* Send a password-reset email. Resolves `true` whether or not the address has
|
|
96
|
+
* an account: the server answers identically either way, on purpose, so this
|
|
97
|
+
* endpoint cannot be used to test whether someone has signed up.
|
|
98
|
+
*/
|
|
99
|
+
export declare function customerRequestPasswordReset(env: FoirEnv, email: string): Promise<boolean>;
|
|
100
|
+
/**
|
|
101
|
+
* Redeem an emailed reset token and set a new password. Returns a fresh session
|
|
102
|
+
* — resetting a password signs the customer in — so pair it with
|
|
103
|
+
* {@link saveSession}, or use {@link resetPasswordAndSignIn} which does both.
|
|
104
|
+
*/
|
|
105
|
+
export declare function customerResetPassword(env: FoirEnv, vars: {
|
|
106
|
+
token: string;
|
|
107
|
+
newPassword: string;
|
|
108
|
+
}): Promise<CustomerAuthResult>;
|
|
109
|
+
/** Redeem an emailed verification token. */
|
|
110
|
+
export declare function customerVerifyEmail(env: FoirEnv, token: string): Promise<boolean>;
|
|
111
|
+
/**
|
|
112
|
+
* Change the calling customer's password. Requires the current password as well
|
|
113
|
+
* as the bearer, so a stolen token alone cannot take the account over.
|
|
114
|
+
*/
|
|
115
|
+
export declare function customerUpdatePassword(env: FoirEnv, vars: {
|
|
116
|
+
currentPassword: string;
|
|
117
|
+
newPassword: string;
|
|
118
|
+
}, options?: AuthOpOptions): Promise<boolean>;
|
|
119
|
+
/**
|
|
120
|
+
* The calling customer, or `null` when the request carried no customer token.
|
|
121
|
+
* An absent session is an answer rather than a failure, so this does not throw.
|
|
122
|
+
*/
|
|
123
|
+
export declare function currentUser(env: FoirEnv, options?: AuthOpOptions): Promise<CurrentUser | null>;
|
|
124
|
+
/**
|
|
125
|
+
* The stored upstream provider token for the calling customer, refreshed
|
|
126
|
+
* transparently by the server when it has expired. `null` when there is no
|
|
127
|
+
* customer token on the request or none stored for that provider.
|
|
128
|
+
*/
|
|
129
|
+
export declare function myProviderToken(env: FoirEnv, providerKey: string, options?: AuthOpOptions): Promise<ProviderToken | null>;
|
|
130
|
+
/**
|
|
131
|
+
* End the session server-side, revoking stored provider tokens. This does NOT
|
|
132
|
+
* clear local session state — {@link signOut} is the pairing that does both.
|
|
133
|
+
*/
|
|
134
|
+
export declare function customerLogout(env: FoirEnv, options?: AuthOpOptions): Promise<boolean>;
|
|
135
|
+
/**
|
|
136
|
+
* Delete the calling customer's account and their owned records and files.
|
|
137
|
+
* Irreversible. `password` is required for password accounts and omitted for
|
|
138
|
+
* provider-only ones; the server enforces the actual re-auth gate and fails
|
|
139
|
+
* closed, so omitting it is refused rather than allowed.
|
|
140
|
+
*/
|
|
141
|
+
export declare function deleteMyAccount(env: FoirEnv, vars: {
|
|
142
|
+
confirm: boolean;
|
|
143
|
+
password?: string;
|
|
144
|
+
}, options?: AuthOpOptions): Promise<DeleteAccountResult>;
|
|
75
145
|
/** Persist an auth result to the session; returns the customer identity. */
|
|
76
146
|
export declare function saveSession(adapter: SessionAdapter, result: CustomerAuthResult): Promise<FoirCustomer>;
|
|
77
147
|
/** Clear all Foir auth keys from the session. */
|
|
@@ -95,6 +165,26 @@ export declare function loginWithOTP(adapter: SessionAdapter, env: FoirEnv, vars
|
|
|
95
165
|
email: string;
|
|
96
166
|
otp: string;
|
|
97
167
|
}): Promise<FoirCustomer>;
|
|
168
|
+
/**
|
|
169
|
+
* Complete a forgot-password flow: redeem the emailed token, set the new
|
|
170
|
+
* password, and persist the session the server mints. The peer of
|
|
171
|
+
* {@link loginWithPassword} for the branch where the customer cannot log in.
|
|
172
|
+
*/
|
|
173
|
+
export declare function resetPasswordAndSignIn(adapter: SessionAdapter, env: FoirEnv, vars: {
|
|
174
|
+
token: string;
|
|
175
|
+
newPassword: string;
|
|
176
|
+
}): Promise<FoirCustomer>;
|
|
177
|
+
/**
|
|
178
|
+
* End the session on both sides: revoke it server-side, then clear local state.
|
|
179
|
+
*
|
|
180
|
+
* The local clear happens even when the server call fails. A customer who
|
|
181
|
+
* pressed sign-out and got a network error must not be left holding a session
|
|
182
|
+
* on the device in front of them — the token expires on its own, and the
|
|
183
|
+
* failure mode of clearing anyway (one orphaned provider token) is strictly
|
|
184
|
+
* better than the failure mode of not (a signed-in browser the user believes is
|
|
185
|
+
* signed out).
|
|
186
|
+
*/
|
|
187
|
+
export declare function signOut(adapter: SessionAdapter, env: FoirEnv): Promise<void>;
|
|
98
188
|
/**
|
|
99
189
|
* Refresh the access token from the stored refresh token, persisting the
|
|
100
190
|
* rotated pair. Returns the new tokens, or null when there's no refresh token
|
|
@@ -110,6 +200,19 @@ export declare function refreshSession(adapter: SessionAdapter, env: FoirEnv): P
|
|
|
110
200
|
* no refresh path.
|
|
111
201
|
*/
|
|
112
202
|
export declare function authedRequest<TData, TVariables>(adapter: SessionAdapter, env: FoirEnv, document: TypedDocumentNode<TData, TVariables>, variables?: TVariables): Promise<TData>;
|
|
203
|
+
/**
|
|
204
|
+
* {@link authedRequest}'s twin for the generated `auth` namespace: run a
|
|
205
|
+
* post-credential auth op with the stored access token, refreshing once and
|
|
206
|
+
* retrying once on a 401-shaped failure.
|
|
207
|
+
*
|
|
208
|
+
* The two exist side by side because they serve different documents, not
|
|
209
|
+
* different transports. `authedRequest` runs the CONSUMER's document — the
|
|
210
|
+
* project's own `myProfile` and friends, whose shape the SDK cannot know — and
|
|
211
|
+
* this runs a root field the server generated. Refresh-with-retry is the same
|
|
212
|
+
* behaviour in both, and it is the reason either is worth having: it is session
|
|
213
|
+
* management, which is the half a generated namespace does not give you.
|
|
214
|
+
*/
|
|
215
|
+
export declare function authedOp<T>(adapter: SessionAdapter, env: FoirEnv, op: AuthOpName, args?: Record<string, unknown>): Promise<T>;
|
|
113
216
|
export interface RequireAuthOptions {
|
|
114
217
|
/**
|
|
115
218
|
* Resolve the full {@link FoirUser} (including the project-specific
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@foir/sdk/auth` — framework-agnostic Foir customer auth.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* The whole customer auth surface — login, register, OTP, external providers,
|
|
5
|
+
* refresh, password management, email verification and account deletion — over
|
|
6
|
+
* the server's generated `auth` namespace, plus token lifecycle with an
|
|
7
|
+
* injected `SessionAdapter`, guards, and the optional hosted-login bridge.
|
|
8
|
+
*
|
|
9
|
+
* There are no operation documents here any more. The flows address root fields
|
|
10
|
+
* the caller's own manifest advertises, through the same op transport
|
|
11
|
+
* `@foir/sdk/data` uses, so `graphql` is not on this module's path at all.
|
|
7
12
|
*/
|
|
8
|
-
export { customerLogin, customerRegister, customerRequestOTP, customerLoginOTP, customerRefreshToken, customerLoginWithProvider, customerProviderCallback, customerProviderVerifyOTP, getAuthProviders, customerSignupContext, customerJoinWaitlist, saveSession, clearSession, getSessionToken, getSessionCustomer, loginWithPassword, registerWithPassword, loginWithOTP, refreshSession, authedRequest, requireAuth, requireAdmin, } from './auth';
|
|
13
|
+
export { customerLogin, customerRegister, customerRequestOTP, customerLoginOTP, customerRefreshToken, customerLoginWithProvider, customerProviderCallback, customerProviderVerifyOTP, getAuthProviders, getAuthConfig, getDefaultAuthProvider, customerSignupContext, customerJoinWaitlist, customerRequestPasswordReset, customerResetPassword, customerUpdatePassword, customerVerifyEmail, currentUser, myProviderToken, customerLogout, deleteMyAccount, saveSession, clearSession, getSessionToken, getSessionCustomer, loginWithPassword, registerWithPassword, loginWithOTP, resetPasswordAndSignIn, refreshSession, signOut, authedRequest, authedOp, requireAuth, requireAdmin, } from './auth';
|
|
9
14
|
export type { FoirAuthTokens, FoirCustomer, FoirUser, SessionAdapter, RequireAuthOptions, RequireAdminOptions, } from './auth';
|
|
15
|
+
export { AUTH_OPS } from './ops';
|
|
16
|
+
export type { AuthOpName, AuthOpOptions } from './ops';
|
|
10
17
|
export { hostedLoginUrl, fetchHostedSession } from './hosted';
|
|
11
18
|
export type { HostedLoginConfig, HostedSession } from './hosted';
|
|
12
19
|
export { createFoirSessionClient } from './session-client';
|
|
13
20
|
export type { FoirSessionClient, FoirSessionConfig, FoirSessionUser, } from './session-client';
|
|
14
|
-
export type { CustomerAuthResult, ProviderLoginResult, AuthProviderPublic, ProviderLoginInput, ProviderCallbackInput, ProviderOTPVerifyInput, SignupContext, SignupMode, WaitlistJoinResult, } from './operations';
|
|
15
|
-
export { CustomerLoginDocument, CustomerRegisterDocument, CustomerRefreshTokenDocument, CustomerRequestOTPDocument, CustomerLoginOTPDocument, CustomerLoginWithProviderDocument, CustomerProviderCallbackDocument, CustomerProviderVerifyOTPDocument, AuthProvidersDocument, } from './operations';
|
|
21
|
+
export type { CustomerAuthResult, ProviderLoginResult, AuthProviderPublic, ProviderLoginInput, ProviderCallbackInput, ProviderOTPVerifyInput, SignupContext, SignupMode, WaitlistJoinResult, AuthConfig, AuthConfigProvider, DefaultAuthProvider, CurrentUser, ProviderToken, DeleteAccountResult, } from './operations';
|
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Foir customer-auth
|
|
2
|
+
* Foir customer-auth result and input types.
|
|
3
3
|
*
|
|
4
|
-
* These are PLATFORM-STABLE primitives —
|
|
5
|
-
*
|
|
6
|
-
* by api-public for every project (gated per-project by the
|
|
7
|
-
* provider flags, but identical in shape when present). Unlike
|
|
8
|
-
* model types
|
|
9
|
-
*
|
|
4
|
+
* These are PLATFORM-STABLE primitives — login / register / refresh / OTP /
|
|
5
|
+
* provider login / password management / account lifecycle are fixed-shape
|
|
6
|
+
* operations served by api-public for every project (gated per-project by the
|
|
7
|
+
* password / OTP / provider flags, but identical in shape when present). Unlike
|
|
8
|
+
* per-project model types they do not vary by configured models, so the SDK
|
|
9
|
+
* owns these declarations directly rather than relying on the consumer's
|
|
10
|
+
* codegen.
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* WHAT USED TO BE HERE. This file also held eleven hand-written GraphQL
|
|
13
|
+
* documents, `parse()`d at module load. They are gone. Every flow in `./auth`
|
|
14
|
+
* now addresses the server's generated `auth` namespace through the same op
|
|
15
|
+
* transport `@foir/sdk/data` uses, so the SDK has one execution surface rather
|
|
16
|
+
* than a private one for auth and a shared one for everything else. Two
|
|
17
|
+
* consequences worth knowing:
|
|
18
|
+
*
|
|
19
|
+
* - the shapes below are no longer a document's selection set. They describe
|
|
20
|
+
* what the server's own payload writer selects for these fields, which is
|
|
21
|
+
* every scalar the type has. `authNamespaceOpShapes` in `./ops` is the
|
|
22
|
+
* machine-checked version of that sentence.
|
|
23
|
+
* - `graphql` is no longer imported here, or anywhere under `./auth`'s flow
|
|
24
|
+
* path. The top-level `parse()` calls were what no bundler could shake out,
|
|
25
|
+
* and they are the reason `@foir/sdk/auth/session` had to exist as a
|
|
26
|
+
* separate graphql-free door.
|
|
27
|
+
*
|
|
28
|
+
* The project-specific customer PROFILE (e.g. `myProfile.isAdmin`) is still NOT
|
|
29
|
+
* here — that shape varies per project, so it stays consumer-injected (see
|
|
13
30
|
* `requireAuth`'s `resolveUser`).
|
|
14
31
|
*/
|
|
15
|
-
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
16
32
|
/** The common auth result every login/register/refresh mutation returns. */
|
|
17
33
|
export interface CustomerAuthResult {
|
|
18
34
|
token: string;
|
|
@@ -55,7 +71,7 @@ export interface ProviderOTPVerifyInput {
|
|
|
55
71
|
otp: string;
|
|
56
72
|
}
|
|
57
73
|
/** The project's customer signup mode. */
|
|
58
|
-
export type SignupMode =
|
|
74
|
+
export type SignupMode = 'PUBLIC' | 'RESTRICTED' | 'WAITLIST' | 'CLOSED';
|
|
59
75
|
/** The signup surface a self-hosted frontend reads to render the right UI. */
|
|
60
76
|
export interface SignupContext {
|
|
61
77
|
signupMode: SignupMode;
|
|
@@ -67,57 +83,62 @@ export interface WaitlistJoinResult {
|
|
|
67
83
|
/** Waitlist lifecycle status; PENDING on a fresh join. */
|
|
68
84
|
status: string;
|
|
69
85
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
export
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}>;
|
|
103
|
-
export declare const CustomerProviderCallbackDocument: TypedDocumentNode<{
|
|
104
|
-
customerProviderCallback: CustomerAuthResult;
|
|
105
|
-
}, {
|
|
106
|
-
input: ProviderCallbackInput;
|
|
107
|
-
}>;
|
|
108
|
-
export declare const CustomerProviderVerifyOTPDocument: TypedDocumentNode<{
|
|
109
|
-
customerProviderVerifyOTP: CustomerAuthResult;
|
|
110
|
-
}, {
|
|
111
|
-
input: ProviderOTPVerifyInput;
|
|
112
|
-
}>;
|
|
113
|
-
export declare const AuthProvidersDocument: TypedDocumentNode<{
|
|
114
|
-
authProviders: AuthProviderPublic[];
|
|
115
|
-
}, Record<string, never>>;
|
|
116
|
-
export declare const CustomerJoinWaitlistDocument: TypedDocumentNode<{
|
|
117
|
-
customerJoinWaitlist: WaitlistJoinResult;
|
|
118
|
-
}, {
|
|
86
|
+
/** One provider as it appears inside {@link AuthConfig}. */
|
|
87
|
+
export interface AuthConfigProvider {
|
|
88
|
+
key: string;
|
|
89
|
+
name: string;
|
|
90
|
+
/** OAUTH2 | TOKEN_SSO | SAML | OIDC | EXTERNAL */
|
|
91
|
+
type: string;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The project's public auth configuration: which built-in methods are on, and
|
|
95
|
+
* which external providers a login screen should offer. Readable before any
|
|
96
|
+
* customer token exists — it is what a login screen renders itself from.
|
|
97
|
+
*/
|
|
98
|
+
export interface AuthConfig {
|
|
99
|
+
passwordEnabled: boolean;
|
|
100
|
+
otpEnabled: boolean;
|
|
101
|
+
providers: AuthConfigProvider[] | null;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* The provider a login screen should default to, or `null` when the project has
|
|
105
|
+
* configured none.
|
|
106
|
+
*/
|
|
107
|
+
export interface DefaultAuthProvider {
|
|
108
|
+
key: string | null;
|
|
109
|
+
name: string | null;
|
|
110
|
+
type: string | null;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* The calling customer, resolved from the bearer token. `null` when the request
|
|
114
|
+
* carried no customer token — an absent session is an answer, not a failure.
|
|
115
|
+
*/
|
|
116
|
+
export interface CurrentUser {
|
|
117
|
+
id: string;
|
|
119
118
|
email: string;
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
/** ACTIVE | PENDING | SUSPENDED | … */
|
|
120
|
+
status: string;
|
|
121
|
+
/** ISO-8601, or null when the address is unverified. */
|
|
122
|
+
emailVerifiedAt: string | null;
|
|
123
|
+
/** ISO-8601. */
|
|
124
|
+
createdAt: string;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* A stored upstream provider token for the calling customer. `null` when there
|
|
128
|
+
* is no customer token on the request, or none stored for that provider.
|
|
129
|
+
*/
|
|
130
|
+
export interface ProviderToken {
|
|
131
|
+
providerKey: string;
|
|
132
|
+
accessToken: string;
|
|
133
|
+
refreshToken: string | null;
|
|
134
|
+
/** ISO-8601. */
|
|
135
|
+
expiresAt: string | null;
|
|
136
|
+
/** Space-separated provider scopes, as the provider issued them. */
|
|
137
|
+
scopes: string | null;
|
|
138
|
+
}
|
|
139
|
+
/** What a self-serve account deletion removed. */
|
|
140
|
+
export interface DeleteAccountResult {
|
|
141
|
+
success: boolean;
|
|
142
|
+
recordsDeleted: number;
|
|
143
|
+
filesDeleted: number;
|
|
144
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The auth client's execution path: the server's generated `auth` namespace.
|
|
3
|
+
*
|
|
4
|
+
* `@foir/sdk/auth` used to carry its own transport — eleven hand-written
|
|
5
|
+
* GraphQL documents `parse()`d at module load and posted to `/graphql`. That was
|
|
6
|
+
* a second wire protocol for one substrate inside one package, and it had the
|
|
7
|
+
* cost such things always have: the hand-written set covered nine of the
|
|
8
|
+
* surface's root fields and the other eleven — the whole of password
|
|
9
|
+
* management, email verification and account deletion — were reachable nowhere
|
|
10
|
+
* in the SDK, so "forgot password" meant dropping to hand-written GraphQL.
|
|
11
|
+
*
|
|
12
|
+
* Now every flow addresses a root field the caller's own manifest advertises,
|
|
13
|
+
* printed and executed by the same functions `@foir/sdk/data` runs every model
|
|
14
|
+
* op through. ONE transport, and the auth client's remaining job is the half
|
|
15
|
+
* that was always the hard part: token storage, refresh, redirects and the
|
|
16
|
+
* React context.
|
|
17
|
+
*
|
|
18
|
+
* WHAT THIS FILE IS NOT. It is not a schema. It names root fields and nothing
|
|
19
|
+
* else — no types, no selection sets, no argument coercion. The SHAPE of what an
|
|
20
|
+
* op selects comes from `namespaces.auth.ops.<op>.payload` in the caller's own
|
|
21
|
+
* manifest, and the argument types from `argTypes` beside it; if this file ever
|
|
22
|
+
* needed to know a field's type itself, the op vocabulary would be
|
|
23
|
+
* under-specified and the fix would belong on the server.
|
|
24
|
+
*
|
|
25
|
+
* ORDERING, and it is load-bearing: these ops resolve against the manifest the
|
|
26
|
+
* deployed api-public serves. A server that does not publish per-op payloads for
|
|
27
|
+
* the `auth` namespace answers every op below with a refusal naming the op. The
|
|
28
|
+
* server half ships first.
|
|
29
|
+
*/
|
|
30
|
+
import { type FoirEnv } from '../transport/foir-http';
|
|
31
|
+
/**
|
|
32
|
+
* The `auth` namespace's root field names, as the manifest lists them.
|
|
33
|
+
*
|
|
34
|
+
* Written out rather than derived so a typo is a compile error at the call site
|
|
35
|
+
* instead of a 400 at runtime, and so this file is the one place to read what
|
|
36
|
+
* the auth client addresses. The names are the server's, unmodified — the op
|
|
37
|
+
* vocabulary for every namespace except `operations` IS the root field name, so
|
|
38
|
+
* nothing here is constructed and nothing can drift into a different name.
|
|
39
|
+
*/
|
|
40
|
+
export declare const AUTH_OPS: {
|
|
41
|
+
readonly authConfig: "authConfig";
|
|
42
|
+
readonly authProviders: "authProviders";
|
|
43
|
+
readonly defaultAuthProvider: "defaultAuthProvider";
|
|
44
|
+
readonly customerSignupContext: "customerSignupContext";
|
|
45
|
+
readonly currentUser: "currentUser";
|
|
46
|
+
readonly myProviderToken: "myProviderToken";
|
|
47
|
+
readonly customerLogin: "customerLogin";
|
|
48
|
+
readonly customerRegister: "customerRegister";
|
|
49
|
+
readonly customerRequestOTP: "customerRequestOTP";
|
|
50
|
+
readonly customerLoginOTP: "customerLoginOTP";
|
|
51
|
+
readonly customerRefreshToken: "customerRefreshToken";
|
|
52
|
+
readonly customerLogout: "customerLogout";
|
|
53
|
+
readonly customerJoinWaitlist: "customerJoinWaitlist";
|
|
54
|
+
readonly customerRequestPasswordReset: "customerRequestPasswordReset";
|
|
55
|
+
readonly customerResetPassword: "customerResetPassword";
|
|
56
|
+
readonly customerUpdatePassword: "customerUpdatePassword";
|
|
57
|
+
readonly customerVerifyEmail: "customerVerifyEmail";
|
|
58
|
+
readonly deleteMyAccount: "deleteMyAccount";
|
|
59
|
+
readonly customerLoginWithProvider: "customerLoginWithProvider";
|
|
60
|
+
readonly customerProviderCallback: "customerProviderCallback";
|
|
61
|
+
readonly customerProviderVerifyOTP: "customerProviderVerifyOTP";
|
|
62
|
+
};
|
|
63
|
+
/** One of the auth namespace's root field names. */
|
|
64
|
+
export type AuthOpName = (typeof AUTH_OPS)[keyof typeof AUTH_OPS];
|
|
65
|
+
/** Per-call overrides for an auth op. */
|
|
66
|
+
export interface AuthOpOptions {
|
|
67
|
+
/**
|
|
68
|
+
* Customer bearer token. api-public still needs the `x-api-key` to resolve
|
|
69
|
+
* the project; the bearer adds the customer identity the post-credential ops
|
|
70
|
+
* (`currentUser`, `customerUpdatePassword`, `deleteMyAccount`, …) are
|
|
71
|
+
* authorised by. Ignored by the pre-credential ops, which mint it.
|
|
72
|
+
*/
|
|
73
|
+
bearerToken?: string;
|
|
74
|
+
/**
|
|
75
|
+
* `fetch` implementation. Defaults to `globalThis.fetch`. Injectable for
|
|
76
|
+
* tests and for runtimes that hand you a bound `fetch` (Hydrogen/Oxygen).
|
|
77
|
+
*/
|
|
78
|
+
fetch?: typeof globalThis.fetch;
|
|
79
|
+
/** Extra headers, applied last so they can override the ones above. */
|
|
80
|
+
headers?: Readonly<Record<string, string>>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Forget every memoised descriptor.
|
|
84
|
+
*
|
|
85
|
+
* For tests and for a long-lived process that has rotated a key. Not part of a
|
|
86
|
+
* normal flow — the descriptor is refetched on schema change by the drift check
|
|
87
|
+
* on the data client, and auth ops are not where that is noticed.
|
|
88
|
+
*/
|
|
89
|
+
export declare function clearAuthManifestCache(): void;
|
|
90
|
+
/**
|
|
91
|
+
* Execute one auth op and return the root field's value.
|
|
92
|
+
*
|
|
93
|
+
* Arguments are sent as GraphQL variables, typed from the op's published
|
|
94
|
+
* `argTypes`. `undefined` entries are dropped rather than sent as null: a
|
|
95
|
+
* GraphQL argument that is absent and one that is explicitly null mean different
|
|
96
|
+
* things to the resolvers below (`deleteMyAccount`'s optional `password` is the
|
|
97
|
+
* live example).
|
|
98
|
+
*/
|
|
99
|
+
export declare function authOp<T>(env: FoirEnv, op: AuthOpName, args?: Record<string, unknown>, options?: AuthOpOptions): Promise<T>;
|