@foir/sdk 0.21.0 → 0.22.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 (58) hide show
  1. package/CHANGELOG.md +84 -1
  2. package/README.md +9 -9
  3. package/dist/app/client.d.ts +90 -84
  4. package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +14 -13
  5. package/dist/app/dispatch.d.ts +5 -5
  6. package/dist/app/errors.d.ts +11 -11
  7. package/dist/app/index.d.ts +4 -4
  8. package/dist/app/naming.d.ts +1 -1
  9. package/dist/app/ticket.d.ts +2 -2
  10. package/dist/app/vocabulary.generated.d.ts +2 -2
  11. package/dist/app-contract.js +3 -3
  12. package/dist/app.js +92 -90
  13. package/dist/auth/access.d.ts +19 -19
  14. package/dist/auth/auth.d.ts +216 -114
  15. package/dist/auth/hosted.d.ts +14 -14
  16. package/dist/auth/index.d.ts +7 -7
  17. package/dist/auth/operations.d.ts +128 -32
  18. package/dist/auth/ops.d.ts +39 -21
  19. package/dist/auth/session-client.d.ts +3 -3
  20. package/dist/auth-session.js +3 -3
  21. package/dist/auth.js +234 -135
  22. package/dist/client.js +4 -4
  23. package/dist/data/builtins.d.ts +26 -26
  24. package/dist/data/client.d.ts +2 -2
  25. package/dist/data/decimal.d.ts +1 -1
  26. package/dist/data/index.d.ts +1 -1
  27. package/dist/data/manifest.d.ts +1 -1
  28. package/dist/data/schema.d.ts +1 -1
  29. package/dist/data/transport.d.ts +10 -10
  30. package/dist/data/ws-transport.d.ts +1 -1
  31. package/dist/data.js +6 -6
  32. package/dist/graphql/content-client.d.ts +13 -13
  33. package/dist/live/drift.d.ts +1 -1
  34. package/dist/media/screen-capture.d.ts +1 -1
  35. package/dist/next/index.d.ts +2 -2
  36. package/dist/next/session.d.ts +10 -10
  37. package/dist/next.js +77 -56
  38. package/dist/placement/index.d.ts +13 -13
  39. package/dist/placement.js +9 -6
  40. package/dist/push.js +61 -40
  41. package/dist/react/FoirFeedback.d.ts +2 -2
  42. package/dist/react/FoirProvider.d.ts +23 -23
  43. package/dist/react/SignIn.d.ts +4 -4
  44. package/dist/react/UserButton.d.ts +1 -1
  45. package/dist/react/feedback-notes.d.ts +4 -4
  46. package/dist/react/index.d.ts +3 -3
  47. package/dist/react.js +119 -95
  48. package/dist/realtime/index.d.ts +1 -1
  49. package/dist/server/callback-client.d.ts +4 -4
  50. package/dist/server/events-client.d.ts +16 -16
  51. package/dist/server/operation-types.d.ts +8 -8
  52. package/dist/server/require-scoped-token.d.ts +3 -3
  53. package/dist/server/types.d.ts +1 -1
  54. package/dist/server/verify-scoped-token.d.ts +3 -3
  55. package/dist/server.js +16 -13
  56. package/dist/transport/foir-http.d.ts +13 -13
  57. package/dist/types/index.d.ts +2 -2
  58. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -108,6 +108,89 @@ nobody asking for `@foir/sdk` gets it early. The push that carries the commit to
108
108
 
109
109
  Changes on `dev` that have not been published to npm.
110
110
 
111
+ ## [0.22.0] — 2026-10-15
112
+
113
+ ### Added
114
+
115
+ - **A signed-in user manages their own account** (`@foir/sdk/auth`), in any
116
+ pool. `updateMyProfile` changes their name, picture or language and asks no
117
+ permission. `mySessions`, `endMySession` and `signOutEverywhere` list and end
118
+ their sessions. `createPersonalAccessToken` makes a token that begins
119
+ `foir_pat_`, answered once (`NewPersonalAccessToken`). `mySignInMethods`, `connectSignInMethod` and
120
+ `disconnectSignInMethod` list, add and remove the ways they sign in.
121
+ `myAuthorizedApps` and `revokeAuthorizedApp` list the apps they have let in
122
+ and take one back. New types: `MyProfile`, `MyProfileChange`, `MySession`,
123
+ `MySignInMethod`, `AuthorizedApp`.
124
+ - **Second factors and recovery codes** (`@foir/sdk/auth`). `myFactors` lists
125
+ what a user holds. `beginStepUp` and `completeStepUp` prove a factor and
126
+ answer an elevated handle for one change in the next ten minutes, which
127
+ `beginFactorEnrollment`, `completeFactorEnrollment`, `removeFactor` and
128
+ `makeRecoveryCodes` take. A factor is `totp`, `webauthn` or `passkey`. New
129
+ types: `MyFactor`, `FactorEnrollment`, `StepUp`.
130
+ - **An organisation that signs its own people in** (`@foir/sdk/app`).
131
+ `organizations.provision({ ..., ownPool: true })` starts a pool of its own
132
+ with the organisation, apart from its parent's and the project's; its
133
+ descendants share it unless one starts another.
134
+ - All of the above need the platform release that serves them.
135
+
136
+ ### Changed
137
+
138
+ - **Breaking: everyone who signs in is a user, and nothing says customer or
139
+ admin as a kind.** It needs the platform release that serves the same
140
+ names; an earlier platform refuses every call below by name.
141
+ - `@foir/sdk/auth`: the raw calls take the server's op names: `login`,
142
+ `register`, `requestOTP`, `loginWithOTP`, `refreshTokens` (the
143
+ `refreshToken` op), `logout`, `joinWaitlist`, `signupContext`,
144
+ `requestPasswordReset`, `resetPassword`, `updatePassword`, `verifyEmail`,
145
+ `loginWithProvider`, `providerCallback`, `providerVerifyOTP` (were
146
+ `customerLogin` and the rest), and `registerOrganization` founds an
147
+ organization over the `registerOrganization` op (was
148
+ `registerOperatorOrganization`). The helpers that save a session are
149
+ `signInWithPassword`, `signUpWithPassword` and `signInWithOTP` (were
150
+ `loginWithPassword`, `registerWithPassword`, `loginWithOTP`), beside
151
+ `signOut`. `getSessionCustomer` is `getSessionUser`, `customerAccess` is
152
+ `userAccess`, `CustomerAuthResult` is `AuthResult` and carries `userId`,
153
+ `FoirCustomer` is `FoirUser` (`{ id, email }`), and `FoirAccess.customerId`
154
+ is `userId`. A session is stored under `foir:userId`, so one saved by an
155
+ earlier version reads as signed out once.
156
+ - `@foir/sdk/auth`: a provider sign-in that names an address somebody
157
+ already holds waits for the code sent to it: `providerSignInRefusalOf`
158
+ reads the refusal (`link_code_sent` with its `pending`), and
159
+ `confirmProviderLink` and `resendProviderLinkCode` finish it.
160
+ - `@foir/sdk/auth`: the hosted session answers `userId`
161
+ (`HostedSession.userId`, `FoirSessionUser.userId`), from the platform's
162
+ `user_id`, and its cookie is `foir_session`.
163
+ - `@foir/sdk/react`: `useFoirSession()` answers `user` (was `customer`).
164
+ - `@foir/sdk/app`: `records.user` and `users` (were `records.customer` and
165
+ `customers`), over the platform's `saveUserRecord`, `userRecordByIdentity`,
166
+ `eraseUserRecord`, `createUser`, `user`, `users`, `userByIdentity` and
167
+ `userIdentitiesById`. `users.list()` answers `{ users }`. `AppUser`,
168
+ `AppUserList`, `AppUserSaveResult` (`userCreated`), `AppUserEraseResult`
169
+ (`userDeleted`, `userKeptBecause`), `AppUserEmailUnavailableReason`,
170
+ `appUserUnlinkedOf` and `appUserEmailUnavailableOf` replace the
171
+ `AppCustomer` names. A notification, a consent change and an
172
+ organization's first member name `userId`.
173
+ - `@foir/sdk/server`: an operation's context names who started the run as
174
+ `actorId` and the user it acts for as `userId` (was `userId` and
175
+ `customerId`). `ScopedTokenIngress.userId` was `customerId`.
176
+ - `@foir/sdk/data`: `FoirGranteeKind` is `'USER'`, a note's subject may be
177
+ `'user'`, `FoirUserNote` and `FoirUserNoteAttachment` replace the
178
+ `FoirCustomerNote` names, and `FoirAuthDowngrade.userToken` was
179
+ `customerToken`.
180
+
181
+ ### Removed
182
+
183
+ - **Breaking: `selectOrganization` and `SelectedOrganization`**
184
+ (`@foir/sdk/auth`). Nothing stores where a user stands: a request names the
185
+ organization it acts in (`orgId` or `orgUrl`), and the platform checks they
186
+ stand there on that request. `myOrganizations` still lists where they may.
187
+ - **Breaking: `requireAdmin` and `RequireAdminOptions`, and `isAdmin` on
188
+ `FoirUser`** (`@foir/sdk/auth`). What a user may do is a role or a scope:
189
+ guard on it with `requireRole` or `requireScope`. `requireAuth` takes the
190
+ type `resolveUser` answers, for a project that reads its own profile.
191
+
192
+ ## [0.21.0] — 2026-10-13
193
+
111
194
  ### Added
112
195
 
113
196
  - **An operation provisions an organization** (`@foir/sdk/app`).
@@ -1125,7 +1208,7 @@ returns a result object rather than `HostedSession | null`, and
1125
1208
  silent wrong answer for a thrown one that discards the answer. Nothing changes
1126
1209
  for a caller that does not pass it.
1127
1210
 
1128
- - `registerOrganization(env, { key, name }, { bearerToken })` on `@foir/sdk/auth`: a customer founds an organization of their own and becomes its first member, holding the role the project's `operatorSignupRole` setting names. Returns the same `MyOrganization` entry `myOrganizations` lists. Present on the wire only while the project's `operatorSignupEnabled` setting is on.
1211
+ - `registerOrganization(env, { key, name }, { bearerToken })` on `@foir/sdk/auth`: a customer founds an organization of their own and becomes its first member, holding the role the project's `organizationSignupRole` setting names. Returns the same `MyOrganization` entry `myOrganizations` lists. Present on the wire only while the project's `organizationSignupEnabled` setting is on.
1129
1212
  - **The operator's organization context, on `@foir/sdk/auth`** — `myOrganizations`
1130
1213
  and `selectOrganization`, plus the types `MyOrganization` and
1131
1214
  `SelectedOrganization`, and two entries on `AUTH_OPS`.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @foir/sdk
2
2
 
3
- Official Foir SDK — client-agnostic typed GraphQL transport, customer auth,
3
+ Official Foir SDK — client-agnostic typed GraphQL transport, sign-in for your app's users,
4
4
  progressive media, content rendering, realtime collaboration, and editor/server
5
5
  helpers for building apps on the [Foir](https://foir.dev) platform.
6
6
 
@@ -25,7 +25,7 @@ pnpm add yjs y-websocket # for @foir/sdk/realtime
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
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
- | `@foir/sdk/auth` | Framework-agnostic customer auth — login/register/OTP/provider flows, token lifecycle (`SessionAdapter`), `createFoirSessionClient` (Foir-hosted cookie sessions), `authedRequest`, guards | `graphql` |
28
+ | `@foir/sdk/auth` | Framework-agnostic sign-in for your app's users: 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` |
31
31
  | `@foir/sdk/server` | Server-side helpers for extension/config API backends — `createServerClient`, `verifyWebhookSignature`, `verifyScopedToken`, callback client | — |
@@ -120,13 +120,13 @@ that way; silence it with `announceManifestFallback: false`.
120
120
  Reads that can be shared are sent to `GET /cdn/graphql` with only the
121
121
  persisted-query hash in the URL, so a CDN in front of you can serve the next
122
122
  visitor without reaching origin. That is a query, on a `pk_` key, with no
123
- customer bearer, on the published channel; everything else is a `POST /graphql`.
123
+ user bearer, on the published channel; everything else is a `POST /graphql`.
124
124
 
125
125
  `foir.raw()` is the escape hatch, and it takes a **GraphQL document** — it
126
126
  reaches anything the credential can, including root fields no model or namespace
127
127
  claims. It returns the whole `data` object.
128
128
 
129
- Namespaces (`search`, `customers`, `notifications`) live under `foir.ns` because
129
+ Namespaces (`search`, `users`, `notifications`) live under `foir.ns` because
130
130
  the client carries no runtime schema to tell a namespace from a model. `ns` and
131
131
  `raw` are the only reserved names.
132
132
 
@@ -178,18 +178,18 @@ export async function loader({ context }: LoaderArgs) {
178
178
  }
179
179
  ```
180
180
 
181
- ### Customer auth
181
+ ### Signing your users in
182
182
 
183
183
  ```ts
184
184
  import {
185
- loginWithPassword,
185
+ signInWithPassword,
186
186
  authedRequest,
187
187
  type SessionAdapter,
188
188
  } from '@foir/sdk/auth';
189
189
  import { MyProfileDocument } from './generated/graphql';
190
190
 
191
191
  // SessionAdapter = { get, set, delete } over your session store (cookie, KV…).
192
- const customer = await loginWithPassword(adapter, env, { email, password });
192
+ const user = await signInWithPassword(adapter, env, { email, password });
193
193
 
194
194
  // Bearer-authed request with automatic refresh-and-retry on expired sessions.
195
195
  const { myProfile } = await authedRequest(adapter, env, MyProfileDocument);
@@ -282,8 +282,8 @@ import { cookies } from 'next/headers';
282
282
  import { getFoirSession } from '@foir/sdk/next';
283
283
 
284
284
  export default async function Dashboard() {
285
- const customer = await getFoirSession(await cookies());
286
- return <p>Signed in as {customer?.email}</p>;
285
+ const user = await getFoirSession(await cookies());
286
+ return <p>Signed in as {user?.email}</p>;
287
287
  }
288
288
  ```
289
289
 
@@ -2,7 +2,7 @@
2
2
  * The client an installed app calls Foir with.
3
3
  *
4
4
  * An app is installed in many projects, and each install maps the app's
5
- * sources onto whichever models that project's operator chose. So an app has
5
+ * sources onto whichever models that project chose. So an app has
6
6
  * no schema to generate a client from: it learns the model keys at request
7
7
  * time, from the install's own configuration, and names them in each call.
8
8
  * `createFoirClient` is the client for an endpoint that knows its project;
@@ -28,7 +28,7 @@
28
28
  */
29
29
  import type { FoirDispatchCredential } from '../data/transport';
30
30
  import { type FoirRetryPolicy } from '../transport/retry';
31
- import type { AppCustomerEmailUnavailableReason } from './errors';
31
+ import type { AppUserEmailUnavailableReason } from './errors';
32
32
  type Json = null | boolean | number | string | Json[] | {
33
33
  [key: string]: Json;
34
34
  };
@@ -65,18 +65,18 @@ export interface AppFieldMapping {
65
65
  /** The project model's field key. */
66
66
  modelField: string;
67
67
  /**
68
- * SINK_ONLY means the operator has said Foir owns the field: a value from
68
+ * SINK_ONLY means the project has said Foir owns the field: a value from
69
69
  * the app's side must not be written over it. An unrecognised direction
70
70
  * reads as SINK_ONLY, which withholds the field rather than writing on a
71
71
  * guess.
72
72
  */
73
73
  direction: AppSyncDirection;
74
74
  }
75
- /** One of the operator's mappings: a source or sink of the app onto a model. */
75
+ /** One of the install's mappings: a source or sink of the app onto a model. */
76
76
  export interface AppBinding {
77
77
  /** The app's source or sink name. */
78
78
  key: string;
79
- /** The project model the operator mapped it onto. */
79
+ /** The project model it was mapped onto. */
80
80
  model: string;
81
81
  /** The model field the app's records are keyed on. Empty when the mapping names none. */
82
82
  naturalKey: string;
@@ -84,7 +84,7 @@ export interface AppBinding {
84
84
  /** REFERENCE: Foir reads the source's fields live. SYNC: it holds a copy. A sink is a copy. */
85
85
  mode: 'SYNC' | 'REFERENCE';
86
86
  }
87
- /** An install's own configuration, as the operator left it. */
87
+ /** An install's own configuration, as the project left it. */
88
88
  export interface AppInstallConfig {
89
89
  name: string;
90
90
  enabled: boolean;
@@ -92,25 +92,25 @@ export interface AppInstallConfig {
92
92
  targetModels: string[];
93
93
  /** The address the app's vendor is told to deliver webhooks to. Empty when the app declares none. */
94
94
  webhookUrl: string;
95
- /** The operator's answers to the manifest's settings schema. */
95
+ /** The project's answers to the manifest's settings schema. */
96
96
  settings: JsonObject;
97
97
  sources: AppBinding[];
98
98
  sinks: AppBinding[];
99
99
  /**
100
- * Why the project cannot send a customer an email at the level this install
100
+ * Why the project cannot send a user an email at the level this install
101
101
  * sits at, or null when it can. While it is set, a notification that carries
102
102
  * an email and an opt-in to a category confirmed by email are refused
103
- * (`appCustomerEmailUnavailableOf`), so say so before one is.
103
+ * (`appUserEmailUnavailableOf`), so say so before one is.
104
104
  */
105
- customerEmailUnavailable: AppCustomerEmailUnavailableReason | null;
105
+ userEmailUnavailable: AppUserEmailUnavailableReason | null;
106
106
  /**
107
- * The customer sign-in this install pairs with the app, by key, for an app
108
- * whose own system has customers; null when it pairs none. Every identity
107
+ * The sign-in this install pairs with the app, by key, for an app whose own
108
+ * system has its own users; null when it pairs none. Every identity
109
109
  * the app names is resolved through it, so while it is null nothing finds a
110
- * customer by identity: `customers.byIdentity` and `customers.identities`
110
+ * user by identity: `users.byIdentity` and `users.identities`
111
111
  * are refused, and an operation that needs them is not registered.
112
112
  */
113
- customerSignIn: string | null;
113
+ userSignIn: string | null;
114
114
  }
115
115
  /**
116
116
  * The status an install reports. Leaving out `subscriptionHealth`,
@@ -136,7 +136,7 @@ export interface AppRecord {
136
136
  /** The platform's own id. What `records.delete` takes. */
137
137
  id: string;
138
138
  naturalKey: string | null;
139
- /** The customer the record belongs to, or null. */
139
+ /** The user the record belongs to, or null. */
140
140
  ownerId: string | null;
141
141
  /** The fields that were asked for and could be read, by model field key. */
142
142
  fields: JsonObject;
@@ -163,21 +163,21 @@ export interface AppUpsertResult {
163
163
  /** True when nothing was written because Foir already holds a newer copy (`sourceModifiedAt`). */
164
164
  stale: boolean;
165
165
  }
166
- export type AppCustomerSaveResult = (AppUpsertResult & {
166
+ export type AppUserSaveResult = (AppUpsertResult & {
167
167
  unlinked: false;
168
- customerCreated: boolean;
168
+ userCreated: boolean;
169
169
  }) | {
170
170
  unlinked: true;
171
171
  };
172
- export interface AppCustomerEraseResult {
172
+ export interface AppUserEraseResult {
173
173
  recordDeleted: boolean;
174
174
  identityRemoved: boolean;
175
- customerDeleted: boolean;
176
- /** Another sign-in method, or something else they own; null when the customer went or none was found. */
177
- customerKeptBecause: string | null;
175
+ userDeleted: boolean;
176
+ /** Another sign-in method, or something else they own; null when the user went or none was found. */
177
+ userKeptBecause: string | null;
178
178
  }
179
179
  export interface AppNotification {
180
- customerId: string;
180
+ userId: string;
181
181
  /** The in-app title, and the email's subject when `email` names none. */
182
182
  title: string;
183
183
  message?: string;
@@ -194,10 +194,10 @@ export interface AppNotification {
194
194
  url: string;
195
195
  }>;
196
196
  };
197
- /** Makes the send safe to repeat: the same key from the same operation tells the customer once. */
197
+ /** Makes the send safe to repeat: the same key from the same operation tells the user once. */
198
198
  idempotencyKey?: string;
199
199
  }
200
- /** An organisation an operation makes for one of its customers. */
200
+ /** An organisation an operation makes for one of its users. */
201
201
  export interface AppOrganizationProvision {
202
202
  /**
203
203
  * Yours to choose and to keep. Sending the same one again with the same
@@ -221,30 +221,36 @@ export interface AppOrganizationProvision {
221
221
  data: Record<string, unknown>;
222
222
  };
223
223
  /**
224
- * The customer who becomes its first member. They are given the role the
224
+ * The user who becomes its first member. They are given the role the
225
225
  * project names for an organisation's founder, in its settings: the
226
226
  * operation chooses who, and the project decides what they hold.
227
227
  */
228
228
  firstMember?: {
229
- customerId: string;
229
+ userId: string;
230
230
  };
231
+ /**
232
+ * The organisation signs its own people in, to a pool of its own, apart
233
+ * from its parent's and the project's. Its descendants share that pool
234
+ * unless one of them starts another. Chosen now, when it is made.
235
+ */
236
+ ownPool?: boolean;
231
237
  }
232
238
  /**
233
- * Who a consent change is for: a customer whose id the app holds, or an
239
+ * Who a consent change is for: a user whose id the app holds, or an
234
240
  * address.
235
241
  *
236
- * By `email`, the customer holding the address at the operation's level is
242
+ * By `email`, the user holding the address at the operation's level is
237
243
  * found, or made, together with the change, so a change the platform refuses
238
- * leaves no customer behind. It is for an opt-in from somebody who may have no
239
- * customer yet (a sign-up form), and the operation must declare
244
+ * leaves no user behind. It is for an opt-in from somebody who may not be a
245
+ * user yet (a sign-up form), and the operation must declare
240
246
  * `users:create` as well as `notifications:consent`.
241
247
  */
242
248
  export type AppConsentSubject = {
243
- customerId: string;
249
+ userId: string;
244
250
  email?: undefined;
245
251
  } | {
246
252
  email: string;
247
- customerId?: undefined;
253
+ userId?: undefined;
248
254
  };
249
255
  export type AppConsentChange = AppConsentSubject & {
250
256
  /** A notification category the project makes opt-in. */
@@ -259,19 +265,19 @@ export type AppConsentChange = AppConsentSubject & {
259
265
  */
260
266
  sourceModifiedAt?: string;
261
267
  /**
262
- * True when the customer gave this consent in another system and the app is
268
+ * True when the user gave this consent in another system and the app is
263
269
  * carrying it over (a mirror). It is applied as it stands.
264
270
  *
265
271
  * Left out, the app collected the consent itself (a sign-up form), and an
266
272
  * opt-in is applied the way its category says: at once, or, in a category
267
- * that confirms by email, only when the customer follows the link the
273
+ * that confirms by email, only when the user follows the link the
268
274
  * platform sends them. Until then nothing is written and the answer says
269
275
  * `confirmationPending`.
270
276
  */
271
277
  givenElsewhere?: boolean;
272
278
  /**
273
279
  * The language of the page the consent was collected on. The confirmation
274
- * email is written in it when the customer has stated none of their own.
280
+ * email is written in it when the user has stated none of their own.
275
281
  */
276
282
  locale?: string;
277
283
  };
@@ -308,7 +314,7 @@ export interface AppClient {
308
314
  whoami(): Promise<AppWhoami>;
309
315
  /** The install this token acts for: its configuration, its credential, its status. */
310
316
  install: {
311
- /** The operator's settings and mappings. Needs `appconfig:read`. */
317
+ /** The install's settings and mappings. Needs `appconfig:read`. */
312
318
  config(): Promise<AppInstallConfig>;
313
319
  /**
314
320
  * The install's credential, by the manifest's credential field names.
@@ -330,7 +336,7 @@ export interface AppClient {
330
336
  * Write the record at a natural key, making it when there is none.
331
337
  * Changing the record there needs `records:write:<model>` and making one
332
338
  * needs `records:create:<model>`, so an operation that does both declares
333
- * both. `ownerId` names the customer it belongs to (needs
339
+ * both. `ownerId` names the user it belongs to (needs
334
340
  * `records:attribute:<model>`); `sourceModifiedAt` is when the object this
335
341
  * mirrors last changed at its source, so an older copy arriving late is
336
342
  * not written.
@@ -365,7 +371,7 @@ export interface AppClient {
365
371
  * other is refused, naming it. `hasMore` says the filter matched more than
366
372
  * were answered. Like `read`, it sees the PUBLISHED version.
367
373
  *
368
- * The name of a model's list is the operator's to set, so the first list a
374
+ * The name of a model's list is the project's to set, so the first list a
369
375
  * client makes asks the data API what this token is served (`GET
370
376
  * /v1/manifest`), once.
371
377
  */
@@ -420,16 +426,16 @@ export interface AppClient {
420
426
  truncated: boolean;
421
427
  }>;
422
428
  /**
423
- * The record of a customer the app names by identity: their id at the
429
+ * The record of a user the app names by identity: their id at the
424
430
  * external system, which is also the subject of their sign-in with it. For
425
- * a source that declares an identity. The platform finds the customer
431
+ * a source that declares an identity. The platform finds the user
426
432
  * through the sign-in the install pairs with the source, at the install's
427
433
  * level, and never by email.
428
434
  */
429
- customer: {
435
+ user: {
430
436
  /**
431
- * Merge `data` into the customer's record, making the record when they
432
- * have none, and the customer when `email` is given and nobody holds the
437
+ * Merge `data` into the user's record, making the record when they
438
+ * have none, and the user when `email` is given and nobody holds the
433
439
  * identity. An identity nobody holds, with no email, is `{ unlinked:
434
440
  * true }`: nothing was written, and they are known once they sign in.
435
441
  */
@@ -439,29 +445,29 @@ export interface AppClient {
439
445
  data: JsonObject;
440
446
  email?: string;
441
447
  sourceModifiedAt?: string;
442
- }): Promise<AppCustomerSaveResult>;
443
- /** The customer's record, or null when nobody holds the identity or they hold no record. */
448
+ }): Promise<AppUserSaveResult>;
449
+ /** The user's record, or null when nobody holds the identity or they hold no record. */
444
450
  read(model: string, identity: string): Promise<AppRecord | null>;
445
451
  /**
446
- * Erase what the app holds for the customer, as a privacy request asks:
447
- * their record and the identity's link, and the customer when nothing
452
+ * Erase what the app holds for the user, as a privacy request asks:
453
+ * their record and the identity's link, and the user when nothing
448
454
  * else is attached to them. It removes a record, so it needs
449
455
  * `records:delete` and `records:read` for the model, and
450
456
  * `identities:delete`. The grant to save the record is not enough.
451
457
  */
452
- erase(model: string, identity: string): Promise<AppCustomerEraseResult>;
458
+ erase(model: string, identity: string): Promise<AppUserEraseResult>;
453
459
  };
454
460
  };
455
- customers: {
461
+ users: {
456
462
  /**
457
- * The customer who has this email at the install's level, made when there
458
- * is none. The same customer every time for one email. Needs
463
+ * The user who has this email at the install's level, made when there
464
+ * is none. The same user every time for one email. Needs
459
465
  * `users:create`.
460
466
  *
461
- * `verified` says whether that address is one the customer has proved is
462
- * theirs. A customer this call makes has not, and neither has one an
467
+ * `verified` says whether that address is one the user has proved is
468
+ * theirs. A user this call makes has not, and neither has one an
463
469
  * earlier call made: anybody can type an address. So what is written for
464
- * an unverified customer is written for whoever holds the address, and
470
+ * an unverified user is written for whoever holds the address, and
465
471
  * anything that should wait for proof asks `verified` first.
466
472
  */
467
473
  ensure(email: string): Promise<{
@@ -470,53 +476,53 @@ export interface AppClient {
470
476
  verified: boolean;
471
477
  }>;
472
478
  /**
473
- * One customer by id, or null when there is none at the install's level.
479
+ * One user by id, or null when there is none at the install's level.
474
480
  * Needs `users:read.named`, or `users:read.all`.
475
481
  */
476
- get(id: string): Promise<AppCustomer | null>;
482
+ get(id: string): Promise<AppUser | null>;
477
483
  /**
478
- * The customer who has this email at the install's level, or null. The
484
+ * The user who has this email at the install's level, or null. The
479
485
  * exact address, as `ensure` matches it, and nothing wider: it finds one
480
- * customer and never searches. Needs `users:read.named`, or
486
+ * user and never searches. Needs `users:read.named`, or
481
487
  * `users:read.all`.
482
488
  */
483
- byEmail(email: string): Promise<AppCustomer | null>;
489
+ byEmail(email: string): Promise<AppUser | null>;
484
490
  /**
485
- * The id of the customer who holds this identity at the app's own system,
491
+ * The id of the user who holds this identity at the app's own system,
486
492
  * or null when nobody does: for an app hearing from its system about one
487
- * of its customers. The identity is the subject of the customer's sign-in
488
- * with that system, and it is resolved through the customer sign-in the
489
- * install pairs (`install.config().customerSignIn`) and no other. Never
493
+ * of its own users. The identity is the subject of the user's sign-in
494
+ * with that system, and it is resolved through the sign-in the
495
+ * install pairs (`install.config().userSignIn`) and no other. Never
490
496
  * matched by email. Needs `identities:read`, declared by name:
491
497
  * `users:read.all` does not include it.
492
498
  */
493
499
  byIdentity(identity: string): Promise<string | null>;
494
500
  /**
495
- * The other direction: the identities a customer the app names by id
501
+ * The other direction: the identities a user the app names by id
496
502
  * holds at the app's own system, most recently seen first, through the
497
503
  * same pairing. Empty when they have never signed in that way. For an app
498
- * acting on an event that names the Foir customer. Needs
504
+ * acting on an event that names the Foir user. Needs
499
505
  * `identities:read`, declared by name.
500
506
  */
501
- identities(customerId: string): Promise<string[]>;
507
+ identities(userId: string): Promise<string[]>;
502
508
  /**
503
- * The customers at the install's level, a page at a time, newest first.
509
+ * The users at the install's level, a page at a time, newest first.
504
510
  * Needs `users:read.all`: an operation declaring only
505
- * `users:read.named` reads the customers it names and is refused the
511
+ * `users:read.named` reads the users it names and is refused the
506
512
  * list.
507
513
  */
508
- list(query?: AppCustomerList): Promise<{
509
- customers: AppCustomer[];
514
+ list(query?: AppUserList): Promise<{
515
+ users: AppUser[];
510
516
  hasMore: boolean;
511
517
  endCursor: string | null;
512
518
  }>;
513
519
  };
514
520
  organizations: {
515
521
  /**
516
- * Make an organisation, with its settings record and a customer as its
522
+ * Make an organisation, with its settings record and a user as its
517
523
  * first member. Needs `organizations:create`, and for `settings` the write
518
524
  * of the settings model. Sign-up is the use: one operation creates the
519
- * customer (`customers.ensure`) and then the organisation that is theirs.
525
+ * user (`users.ensure`) and then the organisation that is theirs.
520
526
  *
521
527
  * The first member is given the role the project names for an
522
528
  * organisation's founder. A project that names none refuses it, saying
@@ -551,20 +557,20 @@ export interface AppClient {
551
557
  }>;
552
558
  };
553
559
  notifications: {
554
- /** Tell a customer something. Needs `notifications:write.named`. */
560
+ /** Tell a user something. Needs `notifications:write.named`. */
555
561
  send(notification: AppNotification): Promise<void>;
556
562
  /**
557
- * Set a named customer's consent in an opt-in category. Needs
563
+ * Set a named user's consent in an opt-in category. Needs
558
564
  * `notifications:consent`, which only an operation's token carries.
559
565
  *
560
566
  * `confirmationPending` is true when an opt-in the app collected was not
561
- * applied because its category confirms by email first: the customer has
567
+ * applied because its category confirms by email first: the user has
562
568
  * been sent a link, or was sent one recently, and the preference turns on
563
- * when they follow it. The answer is the same whether or not the address
564
- * already had a customer.
569
+ * when they follow it. The answer is the same whether or not anybody
570
+ * held the address already.
565
571
  *
566
- * Name the customer by `customerId`, or by `email` for somebody who may
567
- * have no customer yet: the platform finds or makes them with the change,
572
+ * Name the user by `userId`, or by `email` for somebody who may not be
573
+ * a user yet: the platform finds or makes them with the change,
568
574
  * and that also needs `users:create`.
569
575
  */
570
576
  setConsent(change: AppConsentChange): Promise<{
@@ -580,7 +586,7 @@ export interface AppClient {
580
586
  *
581
587
  * For work a delivery cannot do in place. A delivery is not a run, so it
582
588
  * holds none of the authority the platform gives only inside one (setting
583
- * a customer's consent is one), and it has the few seconds its vendor
589
+ * a user's consent is one), and it has the few seconds its vendor
584
590
  * waits. Hand the work to an operation and answer the vendor.
585
591
  *
586
592
  * Only the token a delivery arrived with may call it, for an operation
@@ -590,16 +596,16 @@ export interface AppClient {
590
596
  start(operationKey: string, options?: AppOperationStart): Promise<AppStartedOperation>;
591
597
  };
592
598
  }
593
- /** A customer, as an operation that declared a customer read sees them. */
594
- export interface AppCustomer {
599
+ /** A user, as an operation that declared a user read sees them. */
600
+ export interface AppUser {
595
601
  id: string;
596
602
  email: string;
597
- /** Whether the customer has proved the address is theirs. */
603
+ /** Whether the user has proved the address is theirs. */
598
604
  verified: boolean;
599
605
  /** `ACTIVE`, `SUSPENDED` or `PENDING`, as the platform holds it. */
600
606
  status: string;
601
607
  }
602
- export interface AppCustomerList {
608
+ export interface AppUserList {
603
609
  /** Part of an email address to look for. Absent lists everyone. */
604
610
  search?: string;
605
611
  /** How many to answer, at most. The platform's own page size when absent. */
@@ -121,9 +121,9 @@ export declare type AppConfig = Message<"apps.v1.AppConfig"> & {
121
121
  * OPTIONAL. The app's own system has customers who sign in to it. See
122
122
  * CustomerSignIn.
123
123
  *
124
- * @generated from field: apps.v1.CustomerSignIn customer_sign_in = 14;
124
+ * @generated from field: apps.v1.UserSignIn user_sign_in = 14;
125
125
  */
126
- customerSignIn?: CustomerSignIn | undefined;
126
+ userSignIn?: UserSignIn | undefined;
127
127
  };
128
128
 
129
129
  /**
@@ -150,9 +150,9 @@ export declare const AppConfigSchema: GenMessage<AppConfig>;
150
150
  * feature that finds customers through it (Feature.customer_sign_in), and an
151
151
  * operation that asks who holds an identity (customers:read:identity).
152
152
  *
153
- * @generated from message apps.v1.CustomerSignIn
153
+ * @generated from message apps.v1.UserSignIn
154
154
  */
155
- export declare type CustomerSignIn = Message<"apps.v1.CustomerSignIn"> & {
155
+ export declare type UserSignIn = Message<"apps.v1.UserSignIn"> & {
156
156
  /**
157
157
  * What the setup tells the operator they are choosing, in a sentence: which
158
158
  * customers these are and what identifies them.
@@ -163,10 +163,10 @@ export declare type CustomerSignIn = Message<"apps.v1.CustomerSignIn"> & {
163
163
  };
164
164
 
165
165
  /**
166
- * Describes the message apps.v1.CustomerSignIn.
167
- * Use `create(CustomerSignInSchema)` to create a new message.
166
+ * Describes the message apps.v1.UserSignIn.
167
+ * Use `create(UserSignInSchema)` to create a new message.
168
168
  */
169
- export declare const CustomerSignInSchema: GenMessage<CustomerSignIn>;
169
+ export declare const UserSignInSchema: GenMessage<UserSignIn>;
170
170
 
171
171
  /**
172
172
  * A feature of the app: something the operator turns on or off as a whole at
@@ -230,9 +230,9 @@ export declare type Feature = Message<"apps.v1.Feature"> & {
230
230
  * be on at an install that pairs none: the setup asks for the sign-in at the
231
231
  * switch, and the platform refuses the switch without one.
232
232
  *
233
- * @generated from field: bool customer_sign_in = 6;
233
+ * @generated from field: bool user_sign_in = 6;
234
234
  */
235
- customerSignIn: boolean;
235
+ userSignIn: boolean;
236
236
 
237
237
  /**
238
238
  * The notification category the feature sends under, when it sends
@@ -1520,7 +1520,7 @@ export declare const FieldSyncDirectionSchema: GenEnum<FieldSyncDirection>;
1520
1520
 
1521
1521
  /**
1522
1522
  * The level that owns each row of an app's storage: the three levels a model
1523
- * can be owned at, less the operator-private one, which no app writes.
1523
+ * can be owned at.
1524
1524
  *
1525
1525
  * @generated from enum apps.v1.StorageOwner
1526
1526
  */
@@ -1545,11 +1545,12 @@ export enum StorageOwner {
1545
1545
  ORGANIZATION = 2,
1546
1546
 
1547
1547
  /**
1548
- * Each row belongs to a customer, and only they and the project read it.
1548
+ * Each row belongs to a user, and only they and whoever holds every user's
1549
+ * rows read it.
1549
1550
  *
1550
- * @generated from enum value: STORAGE_OWNER_CUSTOMER = 3;
1551
+ * @generated from enum value: STORAGE_OWNER_USER = 4;
1551
1552
  */
1552
- CUSTOMER = 3,
1553
+ USER = 4,
1553
1554
  }
1554
1555
 
1555
1556
  /**