@foir/sdk 0.7.0 → 0.9.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 (61) hide show
  1. package/CHANGELOG.md +857 -4
  2. package/README.md +46 -15
  3. package/dist/auth/auth.d.ts +104 -1
  4. package/dist/auth/hosted.d.ts +22 -0
  5. package/dist/auth/index.d.ts +13 -7
  6. package/dist/auth/operations.d.ts +85 -64
  7. package/dist/auth/ops.d.ts +99 -0
  8. package/dist/auth.js +786 -106
  9. package/dist/chunk-IGNLWT3H.js +35 -0
  10. package/dist/chunk-OHYVKTGB.js +45 -0
  11. package/dist/chunk-TAL5H6VA.js +39 -0
  12. package/dist/client.js +25 -31
  13. package/dist/codegen/block-registry.d.ts +13 -19
  14. package/dist/codegen/block-registry.js +3 -31
  15. package/dist/codegen/block-scaffold.d.ts +12 -21
  16. package/dist/codegen/block-scaffold.js +3 -41
  17. package/dist/codegen/index.d.ts +9 -4
  18. package/dist/codegen/index.js +7 -11
  19. package/dist/codegen/item-registry.d.ts +28 -0
  20. package/dist/codegen/item-registry.js +7 -0
  21. package/dist/codegen/item-scaffold.d.ts +29 -0
  22. package/dist/codegen/item-scaffold.js +7 -0
  23. package/dist/codegen/preset.d.ts +14 -7
  24. package/dist/codegen/shared.d.ts +20 -9
  25. package/dist/content/content-renderer.d.ts +90 -24
  26. package/dist/content/index.d.ts +13 -7
  27. package/dist/content/richtext.d.ts +66 -18
  28. package/dist/content.js +227 -80
  29. package/dist/data/builtins.d.ts +369 -92
  30. package/dist/data/client.d.ts +97 -25
  31. package/dist/data/document.d.ts +251 -0
  32. package/dist/data/index.d.ts +38 -19
  33. package/dist/data/manifest.d.ts +290 -8
  34. package/dist/data/schema.d.ts +78 -8
  35. package/dist/data/selection.d.ts +231 -37
  36. package/dist/data/subjects.d.ts +221 -0
  37. package/dist/data/subscription-document.d.ts +75 -0
  38. package/dist/data/subscriptions.d.ts +181 -0
  39. package/dist/data/transport.d.ts +255 -37
  40. package/dist/data/ws-transport.d.ts +197 -0
  41. package/dist/data.js +1419 -45
  42. package/dist/graphql/cdn-route.d.ts +12 -28
  43. package/dist/media/index.d.ts +6 -2
  44. package/dist/media/media.d.ts +2 -2
  45. package/dist/media/screen-capture.d.ts +81 -0
  46. package/dist/media.js +97 -0
  47. package/dist/next.js +663 -182
  48. package/dist/push.js +700 -90
  49. package/dist/react/FoirFeedback.d.ts +62 -0
  50. package/dist/react/feedback-notes.d.ts +101 -0
  51. package/dist/react/index.d.ts +7 -1
  52. package/dist/react.js +1272 -194
  53. package/dist/realtime.js +1 -1
  54. package/dist/server/events-client.d.ts +279 -0
  55. package/dist/server/index.d.ts +4 -0
  56. package/dist/server.js +298 -0
  57. package/dist/transport/foir-http.d.ts +23 -6
  58. package/dist/transport/persisted-query.d.ts +68 -0
  59. package/dist/transport/retry.d.ts +1 -1
  60. package/package.json +13 -1
  61. 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 `POST /v1/query` — `createFoirClient`, per-key-shaped ops, `include`/`omit`, `fetchFoirManifest` | **none** |
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` is a second app-runtime transport. You write ops instead of
42
- `.graphql` documents, and the package needs **no `graphql` dependency at all** —
43
- no `graphql`, no `@graphql-typed-document-node/core`, no `@graphql-codegen/*`,
44
- and no new peer. The server compiles your op to a GraphQL document, so both
45
- transports run through the same executor, scope checks and response cache.
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 { type FoirSchema, foirSchemaVersion } from './foir.generated';
54
+ import * as generated from './foir.generated';
51
55
 
52
- const foir = createFoirClient<FoirSchema>({
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
- schemaVersion: foirSchemaVersion, // warns once if the server has moved on
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({ model: 'post', op: 'findMany', args: { first: 1 } });
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
- `/v1/query` takes `model` **or** `namespace` and the client carries no runtime
84
- schema to tell them apart. `ns` and `raw` are the only reserved names.
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<FoirSchema>({
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, including the
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
 
@@ -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 CustomerAuthResult, type ProviderLoginResult, type AuthProviderPublic, type ProviderLoginInput, type ProviderCallbackInput, type ProviderOTPVerifyInput, type SignupContext, type WaitlistJoinResult } from './operations';
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
@@ -19,12 +19,34 @@ export interface HostedLoginConfig {
19
19
  cookieName?: string;
20
20
  /** Session-exchange path on the front door. Default `/session/token`. */
21
21
  sessionPath?: string;
22
+ /** RP-initiated logout path on the front door. Default `/session/end-session`. */
23
+ endSessionPath?: string;
22
24
  }
23
25
  /**
24
26
  * Build the redirect URL to the hosted front door, forwarding a best-effort
25
27
  * absolute `returnTo` (honoured if the front door supports it).
26
28
  */
27
29
  export declare function hostedLoginUrl(config: HostedLoginConfig, returnTo?: string): string;
30
+ /**
31
+ * Build the redirect URL that ENDS the hosted session — the mirror of
32
+ * `hostedLoginUrl`, and the only thing that actually signs a customer out on
33
+ * this flow.
34
+ *
35
+ * The cookie is the durable credential. Disposing of a bearer, clearing a
36
+ * local session, or calling the `customerLogout` mutation (which clears
37
+ * upstream provider tokens and nothing else) all leave it in place, and the
38
+ * next request re-establishes the customer from it. This URL points at the
39
+ * front door's end-session endpoint, which revokes the session row and clears
40
+ * the cookie before returning the browser to `returnTo`.
41
+ *
42
+ * A URL builder, deliberately: no transport, no CORS surface, no CSRF header,
43
+ * and no JavaScript required at the consumer. Server-side logout is
44
+ * `return redirect(hostedLogoutUrl(config, clientId, returnTo))`.
45
+ *
46
+ * `returnTo` must be an absolute URL on an origin registered for `clientId`;
47
+ * the platform refuses anything else. Omit it for a bare 204 with no redirect.
48
+ */
49
+ export declare function hostedLogoutUrl(config: HostedLoginConfig, clientId: string, returnTo?: string): string;
28
50
  /** The identity + bearer returned by a successful hosted-session exchange. */
29
51
  export interface HostedSession {
30
52
  token: string;
@@ -1,15 +1,21 @@
1
1
  /**
2
2
  * `@foir/sdk/auth` — framework-agnostic Foir customer auth.
3
3
  *
4
- * Platform-stable login/register/OTP/provider/refresh flows over api-public,
5
- * token lifecycle with an injected `SessionAdapter`, guards, and the optional
6
- * hosted-login bridge. `graphql` is an optional peer (the operation documents).
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';
10
- export { hostedLoginUrl, fetchHostedSession } from './hosted';
15
+ export { AUTH_OPS } from './ops';
16
+ export type { AuthOpName, AuthOpOptions } from './ops';
17
+ export { hostedLoginUrl, hostedLogoutUrl, 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 GraphQL operations + result types.
2
+ * Foir customer-auth result and input types.
3
3
  *
4
- * These are PLATFORM-STABLE primitives — `customerLogin`/`customerRegister`/
5
- * `customerRefreshToken`/OTP/provider login are fixed-shape operations served
6
- * by api-public for every project (gated per-project by the password/OTP/
7
- * provider flags, but identical in shape when present). Unlike per-project
8
- * model types, they don't vary by configured models, so the SDK owns the
9
- * documents directly rather than relying on the consumer's codegen.
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
- * The project-specific customer PROFILE (e.g. `myProfile.isAdmin`) is NOT here
12
- * — that shape varies per project, so it stays consumer-injected (see
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 = "PUBLIC" | "RESTRICTED" | "WAITLIST" | "CLOSED";
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
- export declare const CustomerLoginDocument: TypedDocumentNode<{
71
- customerLogin: CustomerAuthResult;
72
- }, {
73
- email: string;
74
- password: string;
75
- }>;
76
- export declare const CustomerRegisterDocument: TypedDocumentNode<{
77
- customerRegister: CustomerAuthResult;
78
- }, {
79
- email: string;
80
- password: string;
81
- }>;
82
- export declare const CustomerRefreshTokenDocument: TypedDocumentNode<{
83
- customerRefreshToken: CustomerAuthResult;
84
- }, {
85
- refreshToken: string;
86
- }>;
87
- export declare const CustomerRequestOTPDocument: TypedDocumentNode<{
88
- customerRequestOTP: boolean;
89
- }, {
90
- email: string;
91
- }>;
92
- export declare const CustomerLoginOTPDocument: TypedDocumentNode<{
93
- customerLoginOTP: CustomerAuthResult;
94
- }, {
95
- email: string;
96
- otp: string;
97
- }>;
98
- export declare const CustomerLoginWithProviderDocument: TypedDocumentNode<{
99
- customerLoginWithProvider: ProviderLoginResult;
100
- }, {
101
- input: ProviderLoginInput;
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
- export declare const CustomerSignupContextDocument: TypedDocumentNode<{
122
- customerSignupContext: SignupContext;
123
- }, Record<string, never>>;
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>;