@foir/sdk 0.21.0 → 0.23.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 (62) hide show
  1. package/CHANGELOG.md +135 -1
  2. package/README.md +15 -15
  3. package/dist/app/client.d.ts +93 -86
  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 +17 -15
  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 +251 -117
  15. package/dist/auth/grant-table.generated.d.ts +5 -4
  16. package/dist/auth/hosted.d.ts +38 -14
  17. package/dist/auth/index.d.ts +9 -9
  18. package/dist/auth/operations.d.ts +153 -33
  19. package/dist/auth/ops.d.ts +39 -21
  20. package/dist/auth/session-client.d.ts +3 -3
  21. package/dist/auth-session.js +3 -3
  22. package/dist/auth.js +400 -250
  23. package/dist/client.js +5 -8
  24. package/dist/codegen/preset.d.ts +1 -1
  25. package/dist/data/builtins.d.ts +26 -26
  26. package/dist/data/client.d.ts +3 -3
  27. package/dist/data/decimal.d.ts +1 -1
  28. package/dist/data/index.d.ts +1 -1
  29. package/dist/data/manifest.d.ts +1 -1
  30. package/dist/data/schema.d.ts +2 -2
  31. package/dist/data/transport.d.ts +14 -13
  32. package/dist/data/ws-transport.d.ts +1 -1
  33. package/dist/data.js +8 -11
  34. package/dist/graphql/content-client.d.ts +13 -13
  35. package/dist/index.js +1 -4
  36. package/dist/live/drift.d.ts +1 -1
  37. package/dist/media/screen-capture.d.ts +1 -1
  38. package/dist/next/index.d.ts +2 -2
  39. package/dist/next/session.d.ts +10 -10
  40. package/dist/next.js +78 -60
  41. package/dist/placement/index.d.ts +13 -13
  42. package/dist/placement.js +127 -116
  43. package/dist/push.js +62 -44
  44. package/dist/react/FoirFeedback.d.ts +2 -2
  45. package/dist/react/FoirProvider.d.ts +24 -24
  46. package/dist/react/SignIn.d.ts +4 -4
  47. package/dist/react/UserButton.d.ts +1 -1
  48. package/dist/react/feedback-notes.d.ts +8 -6
  49. package/dist/react/index.d.ts +3 -3
  50. package/dist/react.js +238 -209
  51. package/dist/realtime/index.d.ts +1 -1
  52. package/dist/server/callback-client.d.ts +4 -4
  53. package/dist/server/events-client.d.ts +21 -21
  54. package/dist/server/operation-types.d.ts +8 -8
  55. package/dist/server/require-scoped-token.d.ts +3 -3
  56. package/dist/server/types.d.ts +2 -2
  57. package/dist/server/verify-scoped-token.d.ts +3 -3
  58. package/dist/server.js +130 -126
  59. package/dist/transport/foir-http.d.ts +14 -14
  60. package/dist/transport/key-kind.d.ts +1 -12
  61. package/dist/types/index.d.ts +2 -2
  62. package/package.json +1 -1
@@ -11,7 +11,7 @@
11
11
  * provider's url params current for whenever it next reconnects.
12
12
  *
13
13
  * Agnostic by design: token-agnostic (caller supplies `getToken` — an internal
14
- * mint for admin, a customer access token for an app), persistence-agnostic
14
+ * mint for the console, a user access token for an app), persistence-agnostic
15
15
  * (never persists), doc-schema-agnostic (caller owns the `Y.Doc` + shared types).
16
16
  *
17
17
  * `yjs` + `y-websocket` are OPTIONAL peer deps gated to this subpath, so a
@@ -12,8 +12,8 @@
12
12
  * pushes a DLQ entry.
13
13
  * - `progress({pct, message, metadata})` — non-terminal update; the
14
14
  * monotonic guard at the platform DB layer drops out-of-order packets.
15
- * - `cancel()` — extension self-cancels (rare; admins normally drive
16
- * cancel via the admin app's ConnectRPC path).
15
+ * - `cancel()`: the extension cancels itself (rare; a run is normally
16
+ * cancelled from the console).
17
17
  *
18
18
  * Each call carries:
19
19
  * - `Authorization: Bearer <callback.token>` — Ed25519 scoped token.
@@ -42,7 +42,7 @@ export interface CallbackResult {
42
42
  */
43
43
  status: string;
44
44
  /**
45
- * True when the admin cancelled the execution while the extension was
45
+ * True when someone cancelled the execution while the extension was
46
46
  * processing — the platform refused to overwrite the cancelled state.
47
47
  * Extensions should stop work and (optionally) call `fail('CANCELLED')`.
48
48
  */
@@ -66,7 +66,7 @@ export interface CallbackClient<TResult = unknown> {
66
66
  details?: Record<string, unknown>;
67
67
  }): Promise<CallbackResult>;
68
68
  /**
69
- * Returns `{cancelled: true}` when the admin has cancelled — extension
69
+ * Returns `{cancelled: true}` when someone has cancelled it: the extension
70
70
  * must stop work. `applied: false` means the monotonic guard dropped
71
71
  * the update because pct was lower than the persisted value.
72
72
  */
@@ -7,7 +7,7 @@
7
7
  *
8
8
  * const events = createEventsClient({
9
9
  * apiUrl: process.env.PLATFORM_API_URL!,
10
- * apiKey: process.env.FOIR_SECRET_KEY!, // sk_…, holding events:read
10
+ * apiKey: process.env.FOIR_SECRET_KEY!, // an admin key, holding events:read
11
11
  * });
12
12
  *
13
13
  * const page = await events.list({ after: lastSeq, limit: 500 });
@@ -18,12 +18,12 @@
18
18
  * ```
19
19
  *
20
20
  * ---------------------------------------------------------------------------
21
- * OPERATOR SURFACE. This is not a customer-facing capability.
21
+ * PROJECT SURFACE. Your app's users do not reach it.
22
22
  * ---------------------------------------------------------------------------
23
23
  *
24
- * DECIDED 2026-08-04. The event log is reachable by an `sk_` api key
25
- * and by an admin session. It is NOT reachable by a customer token, there is no
26
- * customer lane for it on api-public, and a `pk_` key can never be minted
24
+ * DECIDED 2026-08-04. The event log is reachable by an admin key
25
+ * and by a console session. It is NOT reachable by a user token, there is no
26
+ * user lane for it on api-public, and a publishable key can never be minted
27
27
  * holding `events:read` at all.
28
28
  *
29
29
  * The reason is a predicate asymmetry rather than a policy preference.
@@ -31,7 +31,7 @@
31
31
  * owner, admin_owner, access_mode, grantee, comp_parent, read_borrow and
32
32
  * slice_parent terms on top of it. A containment-only read over the log
33
33
  * therefore describes rows whose underlying records the same caller could not
34
- * read — and every row carries `actor`, naming which OTHER customer acted. In a
34
+ * read — and every row carries `actor`, naming which OTHER user acted. In a
35
35
  * project with owner confinement that is a per-record activity feed of other
36
36
  * people's actions.
37
37
  *
@@ -39,25 +39,25 @@
39
39
  *
40
40
  * 1. **It lives on `@foir/sdk/server` and nowhere else.** Not `./data`, not
41
41
  * `./client`, not `./react`. Those subpaths are the ones a storefront
42
- * bundles, and `./data` in particular is routinely constructed with a `pk_`
42
+ * bundles, and `./data` in particular is routinely constructed with a publishable key
43
43
  * key in browser-reachable code. `src/server/__tests__/events-client.test.ts`
44
44
  * walks the import graph of every published subpath and fails if this
45
45
  * module becomes reachable from one that ships to a browser — because
46
46
  * "we only documented it on the server" is not a property anything checks.
47
47
  *
48
- * 2. **A `pk_` key is refused at construction**, before a request is made. The
48
+ * 2. **A publishable key is refused at construction**, before a request is made. The
49
49
  * server would refuse it anyway (403), but a 403 at 3am in a deploy log is
50
50
  * a worse teacher than a thrown error naming the key type at the line that
51
51
  * built the client.
52
52
  *
53
53
  * 3. **There is no `bearerToken` option, and an `Authorization` header is
54
- * refused.** On api-public a request presenting `sk_` plus a customer
55
- * bearer resolves the CUSTOMER as the authority, and `events` is stripped
56
- * from every customer authority upstream — so attaching a signed-in
57
- * shopper's token to an operator read turns a working call into a 403 that
54
+ * refused.** On api-public a request presenting an admin key plus a user
55
+ * bearer resolves the USER as the authority, and `events` is stripped
56
+ * from every user authority upstream — so attaching a signed-in
57
+ * shopper's token to a project read turns a working call into a 403 that
58
58
  * reads like a scope problem. Refusing it here says what actually happened.
59
59
  *
60
- * Widening this later is easy. Teaching customers a capability they do not have
60
+ * Widening this later is easy. Teaching users a capability they do not have
61
61
  * is a support burden that cannot be retracted.
62
62
  *
63
63
  * ---------------------------------------------------------------------------
@@ -77,9 +77,9 @@ import { type FoirReconnectPolicy, type FoirSubscription, type FoirSubscriptionH
77
77
  /** The path `GET /v1/events` is mounted on, inside api-public's api-key group. */
78
78
  export declare const FOIR_EVENTS_PATH = "/v1/events";
79
79
  /**
80
- * The scope an `sk_` key must hold. Never mintable on a `pk_` — `events` is
80
+ * The scope an admin key must hold. Never mintable on a publishable key: `events` is
81
81
  * absent from the platform's `publicSafeScopeResources` — and never present in a
82
- * customer's resolved authority.
82
+ * user's resolved authority.
83
83
  */
84
84
  export declare const FOIR_EVENTS_SCOPE = "events:read";
85
85
  /**
@@ -91,9 +91,9 @@ export declare const FOIR_EVENTS_SCOPE = "events:read";
91
91
  export declare const FOIR_EVENTS_RETENTION_DAYS = 90;
92
92
  /** Who caused an event. `null` when the emit site attributed nobody. */
93
93
  export interface FoirEventActor {
94
- /** e.g. `"customer"`, `"admin"`, `"api_key"`, `"system"`. */
94
+ /** e.g. `"user"`, `"api_key"`, `"system"`. */
95
95
  kind?: string;
96
- /** e.g. `"customer:cus_123"`. */
96
+ /** e.g. `"user:usr_123"`. */
97
97
  ref?: string;
98
98
  [key: string]: unknown;
99
99
  }
@@ -119,7 +119,7 @@ export interface FoirEvent {
119
119
  * The event's own flat keys, plus `state` and `stateSchema`.
120
120
  *
121
121
  * `state` is the resource AS OF the event — self-contained, not a pointer to
122
- * current state — for the customer, session, operation and workspace-plane
122
+ * current state — for the user, session, operation and workspace-plane
123
123
  * families. It is null for `record.*` events, deliberately and permanently:
124
124
  * `events.view` is plain tenant/project containment while `record.view`
125
125
  * carries owner, grant, borrow and per-field terms, so a copy of record data
@@ -203,12 +203,12 @@ export interface FoirEventsClientConfig {
203
203
  * An admin key holding `events:read`.
204
204
  *
205
205
  * A publishable or a server key is refused here rather than at the server:
206
- * see the operator note at the top of this file.
206
+ * see the note at the top of this file.
207
207
  */
208
208
  apiKey: string;
209
209
  /**
210
- * Extra headers. An `Authorization` header is refused: a customer bearer
211
- * flips api-public's resolved authority to the customer, and no customer
210
+ * Extra headers. An `Authorization` header is refused: a user bearer
211
+ * flips api-public's resolved authority to the user, and no user
212
212
  * authority can contain `events:read`.
213
213
  */
214
214
  headers?: Readonly<Record<string, string>>;
@@ -147,17 +147,17 @@ export interface OperationContext {
147
147
  /** Project ID */
148
148
  projectId: string;
149
149
  /**
150
- * The principal that started the run. Always present, and the empty string
151
- * when the run has none, as a hook's run does.
150
+ * Who started the run. Always present, and the empty string when nobody
151
+ * did, as for a hook's run.
152
152
  */
153
- userId: string;
153
+ actorId: string;
154
154
  /**
155
- * The customer the run acts for, when it acts for one: the signed-in
156
- * customer who ran it, or the owner of the record whose event fired the
157
- * hook. The dispatch token carries the same customer, so a client built from
158
- * it acts for them too.
155
+ * The user the run acts for, when it acts for one: the signed-in user who
156
+ * ran it, or the owner of the record whose event fired the hook. The
157
+ * dispatch token carries the same user, so a client built from it acts for
158
+ * them too.
159
159
  */
160
- customerId?: string;
160
+ userId?: string;
161
161
  /** When the platform built this body, RFC 3339 in UTC to the second. */
162
162
  timestamp: string;
163
163
  }
@@ -120,12 +120,12 @@ export interface ScopedTokenIngress {
120
120
  */
121
121
  orgId: string | null;
122
122
  /**
123
- * The customer the run acts for, when it acts for one: the signed-in
124
- * customer who ran the operation, or the owner of the record whose event
123
+ * The user the run acts for, when it acts for one: the signed-in
124
+ * user who ran the operation, or the owner of the record whose event
125
125
  * fired it. Verified with the rest of the token, so it is the one to trust
126
126
  * over anything in the input.
127
127
  */
128
- customerId?: string;
128
+ userId?: string;
129
129
  /** When the token stops being accepted. There is no refresh: finish, or report, before it. */
130
130
  expiresAt: Date;
131
131
  /** The token's own id (`jti`). The run's id on a dispatch that carries no other. */
@@ -7,7 +7,7 @@
7
7
  export interface ServerClientOptions {
8
8
  /** Base URL for the platform API (e.g., 'https://api.example.com'). */
9
9
  baseUrl: string;
10
- /** Project-scoped API key (sk_* format). */
10
+ /** A project's admin key (foir_adm_). */
11
11
  apiKey: string;
12
12
  /**
13
13
  * Optional config key. Retained for backwards compatibility with older
@@ -16,7 +16,7 @@ export interface ServerClientOptions {
16
16
  configKey?: string;
17
17
  }
18
18
  /**
19
- * Where an editor iframe should appear in the admin record editor.
19
+ * Where an editor iframe should appear in the console's record editor.
20
20
  */
21
21
  export interface EditorPlacement {
22
22
  /** 'main-editor' replaces / augments the content tab; 'sidebar' renders in the sidebar. */
@@ -38,9 +38,9 @@ export interface ScopedTokenClaims {
38
38
  iat: number;
39
39
  ctx?: Record<string, string>;
40
40
  /**
41
- * The customer the token acts for, when it acts for one: the signed-in
42
- * customer who ran the operation, or the owner of the record whose event
43
- * fired it. `requireScopedToken` answers it as `customerId`.
41
+ * The user the token acts for, when it acts for one: the signed-in
42
+ * user who ran the operation, or the owner of the record whose event
43
+ * fired it. `requireScopedToken` answers it as `userId`.
44
44
  */
45
45
  cid?: string;
46
46
  }
package/dist/server.js CHANGED
@@ -74,120 +74,123 @@ async function verifyWebhookSignature(payload, signature, secret) {
74
74
 
75
75
  // src/auth/grant-table.generated.ts
76
76
  var GRANT_TABLE = [
77
- ["records:read", true, ["own", "all"], []],
78
- ["records:create", true, [], []],
79
- ["records:write", true, ["own", "all"], []],
80
- ["records:delete", true, ["own", "all"], []],
81
- ["records:publish", true, ["own", "all"], []],
82
- ["records:attribute", true, [], []],
83
- ["search:read", true, [], []],
84
- ["embeddings:read", false, [], []],
85
- ["embeddings:write", false, [], []],
86
- ["schemas:read", false, [], []],
87
- ["drafts:read", false, [], []],
88
- ["files:read", false, ["own", "all"], []],
89
- ["files:write", false, ["own", "all"], []],
90
- ["files:delete", false, ["own", "all"], []],
91
- ["notifications:read", false, ["own", "all"], []],
92
- ["notifications:write", false, ["own", "named", "all"], []],
93
- ["notifications:consent", false, [], []],
94
- ["pushcredentials:read", false, [], []],
95
- ["pushcredentials:write", false, [], []],
96
- ["notes:read", false, ["own", "all"], []],
97
- ["notes:write", false, ["own", "all"], []],
98
- ["notes:create", false, [], []],
99
- ["approvals:read", false, [], []],
100
- ["approvals:write", false, [], []],
101
- ["rooms:read", true, [], []],
102
- ["rooms:write", true, [], []],
103
- ["presence:read", true, [], []],
104
- ["operations:read", false, [], []],
105
- ["operations:execute", true, [], []],
106
- ["operations:write", false, [], []],
107
- ["operations:complete", false, [], []],
108
- ["status:write", false, [], []],
109
- ["appconfig:read", false, [], []],
110
- ["schedules:read", false, [], []],
111
- ["schedules:write", false, [], []],
112
- ["hooks:read", false, [], []],
113
- ["hooks:write", false, [], []],
114
- ["relyingparties:read", false, [], []],
115
- ["relyingparties:write", false, [], []],
116
- ["domains:read", false, [], []],
117
- ["domains:write", false, [], []],
118
- ["secrets:read", false, [], []],
119
- ["secrets:list", false, [], []],
120
- ["secrets:put", false, [], []],
121
- ["secrets:delete", false, [], []],
122
- ["appsecrets:read", false, [], []],
123
- ["appsecrets:list", false, [], []],
124
- ["appsecrets:put", false, [], []],
125
- ["appsecrets:delete", false, [], []],
126
- ["opsecrets:read", false, [], []],
127
- ["opsecrets:write", false, [], []],
128
- ["settings:read", false, [], []],
129
- ["settings:write", false, [], []],
130
- ["designtokens:read", false, [], []],
131
- ["designtokens:write", false, [], []],
132
- ["models:read", false, [], []],
133
- ["models:write", false, [], []],
134
- ["configs:read", false, [], []],
135
- ["configs:write", false, [], []],
136
- ["apps:read", false, [], []],
137
- ["apps:write", false, [], []],
138
- ["events:read", false, [], []],
139
- ["targeting:debug", false, [], []],
140
- ["visitors:forward", false, [], []],
141
- ["apikeys:read", false, [], []],
142
- ["apikeys:write", false, [], []],
143
- ["organizations:read", false, [], []],
144
- ["organizations:write", false, [], []],
145
- ["organizations:create", false, [], []],
146
- ["organizations:delete", false, [], []],
147
- ["organizations:reach", false, [], []],
148
- ["members:read", false, [], []],
149
- ["members:write", false, [], []],
150
- ["roles:read", false, [], []],
151
- ["roles:write", false, [], []],
152
- ["projects:read", false, [], []],
153
- ["projects:write", false, [], []],
154
- ["project:manage", false, [], ["records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
155
- ["projects:create", false, [], []],
156
- ["projects:delete", false, [], []],
157
- ["tenants:read", false, [], []],
158
- ["tenants:write", false, [], []],
159
- ["tenants:delete", false, [], []],
160
- ["audit:read", false, [], []],
161
- ["export:run", false, [], []],
162
- ["billing:read", false, [], []],
163
- ["billing:manage", false, [], []],
164
- ["usage:read", false, [], []],
165
- ["controlkeys:read", false, [], []],
166
- ["controlkeys:write", false, [], []],
167
- ["tenanthooks:read", false, [], []],
168
- ["tenanthooks:write", false, [], []],
169
- ["apps:install", false, [], []],
170
- ["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "apps:install"]],
171
- ["platform:read", false, [], []],
172
- ["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace", "users:setup"]],
173
- ["platform:create_workspace", false, [], []],
174
- ["users:read", false, ["own", "named", "all"], []],
175
- ["users:write", false, ["own", "all"], []],
176
- ["users:setup", false, [], []],
177
- ["users:delete", false, ["own", "all"], []],
178
- ["users:create", false, [], []],
179
- ["identities:read", false, [], []],
180
- ["identities:delete", false, [], []],
181
- ["usersecrets:read", false, [], []],
182
- ["usersecrets:list", false, [], []],
183
- ["usersecrets:put", false, [], []],
184
- ["usersecrets:delete", false, [], []],
185
- ["auth:login_with_foir", false, [], []],
186
- ["signinproviders:read", false, [], []],
187
- ["signinproviders:write", false, [], []]
77
+ ["records:read", "model", ["own", "all"], []],
78
+ ["records:create", "model", [], []],
79
+ ["records:write", "model", ["own", "all"], []],
80
+ ["records:delete", "model", ["own", "all"], []],
81
+ ["records:publish", "model", ["own", "all"], []],
82
+ ["records:attribute", "model", [], []],
83
+ ["search:read", "model", [], []],
84
+ ["embeddings:read", "none", [], []],
85
+ ["embeddings:write", "none", [], []],
86
+ ["schemas:read", "none", [], []],
87
+ ["drafts:read", "none", [], []],
88
+ ["files:read", "none", ["own", "all"], []],
89
+ ["files:write", "none", ["own", "all"], []],
90
+ ["files:delete", "none", ["own", "all"], []],
91
+ ["notifications:read", "none", ["own", "all"], []],
92
+ ["notifications:write", "none", ["own", "named", "all"], []],
93
+ ["notifications:consent", "none", [], []],
94
+ ["pushcredentials:read", "none", [], []],
95
+ ["pushcredentials:write", "none", [], []],
96
+ ["notes:read", "none", ["own", "all"], []],
97
+ ["notes:write", "none", ["own", "all"], []],
98
+ ["notes:create", "none", [], []],
99
+ ["approvals:read", "none", [], []],
100
+ ["approvals:write", "none", [], []],
101
+ ["rooms:read", "model", [], []],
102
+ ["rooms:write", "model", [], []],
103
+ ["presence:read", "model", [], []],
104
+ ["operations:read", "none", [], []],
105
+ ["operations:execute", "operation", [], []],
106
+ ["operations:write", "none", [], []],
107
+ ["operations:complete", "none", [], []],
108
+ ["status:write", "none", [], []],
109
+ ["appconfig:read", "none", [], []],
110
+ ["schedules:read", "none", [], []],
111
+ ["schedules:write", "none", [], []],
112
+ ["hooks:read", "none", [], []],
113
+ ["hooks:write", "none", [], []],
114
+ ["authclients:read", "none", [], []],
115
+ ["authclients:write", "none", [], []],
116
+ ["domains:read", "none", [], []],
117
+ ["domains:write", "none", [], []],
118
+ ["secrets:read", "none", [], []],
119
+ ["secrets:list", "none", [], []],
120
+ ["secrets:put", "none", [], []],
121
+ ["secrets:delete", "none", [], []],
122
+ ["appsecrets:read", "none", [], []],
123
+ ["appsecrets:list", "none", [], []],
124
+ ["appsecrets:put", "none", [], []],
125
+ ["appsecrets:delete", "none", [], []],
126
+ ["opsecrets:read", "none", [], []],
127
+ ["opsecrets:write", "none", [], []],
128
+ ["settings:read", "none", [], []],
129
+ ["settings:write", "none", [], []],
130
+ ["designtokens:read", "none", [], []],
131
+ ["designtokens:write", "none", [], []],
132
+ ["models:read", "none", [], []],
133
+ ["models:write", "none", [], []],
134
+ ["configs:read", "none", [], []],
135
+ ["configs:write", "none", [], []],
136
+ ["apps:read", "none", [], []],
137
+ ["apps:write", "none", [], []],
138
+ ["events:read", "none", [], []],
139
+ ["targeting:debug", "none", [], []],
140
+ ["visitors:forward", "none", [], []],
141
+ ["apikeys:read", "none", [], []],
142
+ ["apikeys:write", "none", [], []],
143
+ ["organizations:read", "none", [], []],
144
+ ["organizations:write", "none", [], []],
145
+ ["organizations:create", "none", [], []],
146
+ ["organizations:delete", "none", [], []],
147
+ ["organizations:reach", "none", [], []],
148
+ ["members:read", "none", [], []],
149
+ ["members:write", "none", [], []],
150
+ ["roles:read", "none", [], []],
151
+ ["roles:write", "none", [], []],
152
+ ["projects:read", "none", [], []],
153
+ ["projects:write", "none", [], []],
154
+ ["project:stand", "none", [], []],
155
+ ["project:manage", "none", [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "authclients:read", "authclients:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
156
+ ["projects:create", "none", [], []],
157
+ ["projects:delete", "none", [], []],
158
+ ["tenants:read", "none", [], []],
159
+ ["tenants:write", "none", [], []],
160
+ ["tenants:delete", "none", [], []],
161
+ ["audit:read", "none", [], []],
162
+ ["export:run", "none", [], []],
163
+ ["billing:read", "none", [], []],
164
+ ["billing:manage", "none", [], []],
165
+ ["usage:read", "none", [], []],
166
+ ["controlkeys:read", "none", [], []],
167
+ ["controlkeys:write", "none", [], []],
168
+ ["tenanthooks:read", "none", [], []],
169
+ ["tenanthooks:write", "none", [], []],
170
+ ["apps:install", "none", [], []],
171
+ ["tenant:manage", "none", [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "apps:install"]],
172
+ ["platform:read", "none", [], []],
173
+ ["platform:manage", "none", [], ["tenant:manage", "platform:read", "platform:create_workspace"]],
174
+ ["platform:create_workspace", "none", [], []],
175
+ ["users:read", "none", ["own", "named", "all"], []],
176
+ ["users:write", "none", ["own", "all"], []],
177
+ ["users:setup", "none", [], []],
178
+ ["users:delete", "none", ["own", "all"], []],
179
+ ["users:create", "none", [], []],
180
+ ["identities:read", "none", [], []],
181
+ ["identities:delete", "none", [], []],
182
+ ["usersecrets:read", "none", [], []],
183
+ ["usersecrets:list", "none", [], []],
184
+ ["usersecrets:put", "none", [], []],
185
+ ["usersecrets:delete", "none", [], []],
186
+ ["auth:login_with_foir", "none", [], []],
187
+ ["signinproviders:read", "none", [], []],
188
+ ["signinproviders:write", "none", [], []]
188
189
  ];
189
190
 
190
191
  // src/auth/grants.ts
192
+ var FIELD_PERMISSIONS = ["records:read", "records:write"];
193
+ var FIELD_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
191
194
  var index;
192
195
  function catalogue() {
193
196
  if (!index) {
@@ -223,8 +226,12 @@ function parseGrant(s) {
223
226
  let target = "";
224
227
  if (rest.startsWith(":")) {
225
228
  target = rest.slice(1);
226
- if (!permission[1]) return null;
229
+ if (permission[1] === "none") return null;
227
230
  if (target === "" || /[: \t\n*]/.test(target)) return null;
231
+ const dot = target.indexOf(".");
232
+ if (permission[1] === "model" && dot >= 0) {
233
+ if (!FIELD_PERMISSIONS.includes(name) || dot === 0 || !FIELD_KEY.test(target.slice(dot + 1))) return null;
234
+ }
228
235
  }
229
236
  return { permission, reach, target };
230
237
  }
@@ -577,7 +584,7 @@ async function requireScopedToken(request, options) {
577
584
  // header, so the client presents exactly what this guard accepted.
578
585
  dispatch: { token: () => token, context: rawContext },
579
586
  orgId: typeof level === "string" ? level : null,
580
- ...typeof claims.cid === "string" && claims.cid !== "" ? { customerId: claims.cid } : {},
587
+ ...typeof claims.cid === "string" && claims.cid !== "" ? { userId: claims.cid } : {},
581
588
  expiresAt: new Date(claims.exp * 1e3),
582
589
  tokenId: claims.jti
583
590
  };
@@ -639,9 +646,9 @@ async function readDispatch(request) {
639
646
  if (!isObject(context)) refuse(`body.context is ${describe(context)}, expected an object`);
640
647
  requireString(context, "tenantId", "body.context");
641
648
  requireString(context, "projectId", "body.context");
642
- requireString(context, "userId", "body.context", true);
649
+ requireString(context, "actorId", "body.context", true);
643
650
  requireString(context, "timestamp", "body.context");
644
- if (context.customerId !== void 0) requireString(context, "customerId", "body.context");
651
+ if (context.userId !== void 0) requireString(context, "userId", "body.context");
645
652
  const input = body.input;
646
653
  if (input !== null && !isObject(input)) {
647
654
  refuse(`body.input is ${describe(input)}, expected an object`);
@@ -862,8 +869,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
862
869
  downgrade;
863
870
  constructor(downgrade) {
864
871
  super(
865
- `[foir] your customer token was ${downgrade.customerToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
866
- { graphQLErrors: [{ message: "customer token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
872
+ `[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
873
+ { graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
867
874
  );
868
875
  this.name = "FoirAuthDowngradedError";
869
876
  this.downgrade = downgrade;
@@ -871,7 +878,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
871
878
  };
872
879
  var FoirSessionExpiredError = class extends FoirRequestError {
873
880
  constructor(options) {
874
- super("[foir] the customer session has expired and could not be refreshed: sign in again", {
881
+ super("[foir] the user session has expired and could not be refreshed: sign in again", {
875
882
  cause: options?.cause,
876
883
  graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
877
884
  });
@@ -1037,10 +1044,7 @@ async function hmacSha256Hex(secret, body) {
1037
1044
  var KEY_PREFIXES = [
1038
1045
  ["foir_pub_", "publishable"],
1039
1046
  ["foir_srv_", "server"],
1040
- ["foir_adm_", "admin"],
1041
- ["pk_", "publishable"],
1042
- ["sk_", "older"],
1043
- ["tc_", "older"]
1047
+ ["foir_adm_", "admin"]
1044
1048
  ];
1045
1049
  function foirKeyKind(apiKey) {
1046
1050
  if (typeof apiKey !== "string") return void 0;
@@ -1131,7 +1135,7 @@ var FoirSocketTransport = class {
1131
1135
  constructor(config) {
1132
1136
  if (config.dispatch !== void 0) {
1133
1137
  throw new FoirRequestError(
1134
- "[foir] a client built from a dispatch token cannot subscribe. The subscription socket authenticates an api key, an admin token or a customer bearer, and has no path for a dispatch token. Queries and mutations made as the operation go to /graphql and work as they are."
1138
+ "[foir] a client built from a dispatch token cannot subscribe. The subscription socket authenticates an api key, a console token or a user bearer, and has no path for a dispatch token. Queries and mutations made as the operation go to /graphql and work as they are."
1135
1139
  );
1136
1140
  }
1137
1141
  this.config = config;
@@ -1495,12 +1499,12 @@ function assertOperatorKey(apiKey) {
1495
1499
  const kind = foirKeyKind(key);
1496
1500
  if (kind === "publishable" || kind === "server") {
1497
1501
  throw new FoirRequestError(
1498
- `[foir] createEventsClient: the event log is an OPERATOR surface and a ${kind} key can never hold ${FOIR_EVENTS_SCOPE}: it is not mintable on an app's key. Use an admin key from a machine of your own. The log names which customer acted on records that whoever reads an app's key cannot themselves read.`
1502
+ `[foir] createEventsClient: the event log is a project surface and a ${kind} key can never hold ${FOIR_EVENTS_SCOPE}: it is not mintable on an app's key. Use an admin key from a machine of your own. The log names which user acted on records that whoever reads an app's key cannot themselves read.`
1499
1503
  );
1500
1504
  }
1501
1505
  if (kind === void 0) {
1502
1506
  throw new FoirRequestError(
1503
- `[foir] createEventsClient: apiKey must be an admin key, got a key starting "${key.slice(0, 4)}". The event log is reachable by an admin key or an admin session only.`
1507
+ `[foir] createEventsClient: apiKey must be an admin key, got a key starting "${key.slice(0, 4)}". The event log is reachable by an admin key or a console session only.`
1504
1508
  );
1505
1509
  }
1506
1510
  return key;
@@ -1510,7 +1514,7 @@ function assertNoBearer(headers) {
1510
1514
  for (const name of Object.keys(headers)) {
1511
1515
  if (name.toLowerCase() === "authorization") {
1512
1516
  throw new FoirRequestError(
1513
- `[foir] createEventsClient: an Authorization header is not accepted. A customer bearer makes the customer the resolved authority on api-public, and no customer authority can contain ${FOIR_EVENTS_SCOPE}. The sk_ key alone is the credential for this surface.`
1517
+ `[foir] createEventsClient: an Authorization header is not accepted. A user bearer makes the user the resolved authority on api-public, and no user authority can contain ${FOIR_EVENTS_SCOPE}. The admin key alone is the credential for this surface.`
1514
1518
  );
1515
1519
  }
1516
1520
  }