@fuzdev/fuz_app 0.57.2 → 0.59.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 (40) hide show
  1. package/dist/actions/CLAUDE.md +8 -3
  2. package/dist/auth/CLAUDE.md +70 -37
  3. package/dist/auth/account_action_specs.d.ts +9 -0
  4. package/dist/auth/account_action_specs.d.ts.map +1 -1
  5. package/dist/auth/account_action_specs.js +9 -0
  6. package/dist/auth/account_schema.d.ts +1 -1
  7. package/dist/auth/account_schema.js +1 -1
  8. package/dist/auth/admin_action_specs.d.ts +35 -0
  9. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  10. package/dist/auth/admin_action_specs.js +35 -0
  11. package/dist/auth/audit_log_ddl.d.ts +24 -0
  12. package/dist/auth/audit_log_ddl.d.ts.map +1 -0
  13. package/dist/auth/audit_log_ddl.js +42 -0
  14. package/dist/auth/audit_log_schema.d.ts +3 -3
  15. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  16. package/dist/auth/audit_log_schema.js +3 -34
  17. package/dist/auth/{ddl.d.ts → auth_ddl.d.ts} +7 -4
  18. package/dist/auth/auth_ddl.d.ts.map +1 -0
  19. package/dist/auth/{ddl.js → auth_ddl.js} +6 -3
  20. package/dist/auth/migrations.js +4 -4
  21. package/dist/auth/role_grant_offer_action_specs.d.ts +17 -0
  22. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  23. package/dist/auth/role_grant_offer_action_specs.js +17 -0
  24. package/dist/auth/role_grant_offer_ddl.d.ts +43 -0
  25. package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -0
  26. package/dist/auth/role_grant_offer_ddl.js +99 -0
  27. package/dist/auth/role_grant_offer_queries.d.ts +1 -1
  28. package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
  29. package/dist/auth/role_grant_offer_queries.js +1 -1
  30. package/dist/auth/role_grant_offer_schema.d.ts +3 -28
  31. package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
  32. package/dist/auth/role_grant_offer_schema.js +3 -80
  33. package/dist/auth/role_grant_queries.d.ts +1 -1
  34. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  35. package/dist/auth/role_grant_queries.js +1 -1
  36. package/dist/auth/self_service_role_action_specs.d.ts +8 -0
  37. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
  38. package/dist/auth/self_service_role_action_specs.js +8 -0
  39. package/package.json +1 -1
  40. package/dist/auth/ddl.d.ts.map +0 -1
@@ -1,9 +1,11 @@
1
1
  /**
2
- * Audit log database schema and types.
2
+ * Audit log types and client-safe Zod schemas.
3
3
  *
4
4
  * Records auth mutations (login, logout, grant, revoke, etc.) for
5
5
  * security monitoring and operational visibility.
6
6
  *
7
+ * Table DDL and indexes live in `auth/audit_log_ddl.ts`.
8
+ *
7
9
  * @module
8
10
  */
9
11
  import { z } from 'zod';
@@ -345,36 +347,3 @@ export const RoleGrantHistoryEventJson = AuditLogEventJson.extend({
345
347
  export const AdminSessionJson = AuthSessionJson.extend({
346
348
  username: z.string(),
347
349
  });
348
- // Schema DDL
349
- //
350
- // Multi-actor invariants the envelope columns assume:
351
- // - `actor_id` + `account_id`, when both populated, refer to the same
352
- // account (derivable via `actor.account_id`). Denormalized for
353
- // indexed audit queries; do not let them disagree.
354
- // - `target_actor_id` + `target_account_id`, same rule when both populated.
355
- // - `target_account_id` is the SSE/WS socket-close key — sessions stay
356
- // account-grain after multi-actor lands, so this column carries
357
- // the routing identity even on actor-bound events.
358
- // - `target_actor_id` is populated iff the event subject is actor-bound
359
- // (see `AuditLogEvent.target_actor_id` doc-comment for the rule).
360
- export const AUDIT_LOG_SCHEMA = `
361
- CREATE TABLE IF NOT EXISTS audit_log (
362
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
363
- seq SERIAL NOT NULL,
364
- event_type TEXT NOT NULL,
365
- outcome TEXT NOT NULL DEFAULT 'success',
366
- actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
367
- account_id UUID REFERENCES account(id) ON DELETE SET NULL,
368
- target_account_id UUID REFERENCES account(id) ON DELETE SET NULL,
369
- target_actor_id UUID REFERENCES actor(id) ON DELETE SET NULL,
370
- ip TEXT,
371
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
372
- metadata JSONB
373
- )`;
374
- export const AUDIT_LOG_INDEXES = [
375
- `CREATE INDEX IF NOT EXISTS idx_audit_log_seq ON audit_log(seq DESC)`,
376
- `CREATE INDEX IF NOT EXISTS idx_audit_log_account ON audit_log(account_id)`,
377
- `CREATE INDEX IF NOT EXISTS idx_audit_log_event_type ON audit_log(event_type)`,
378
- `CREATE INDEX IF NOT EXISTS idx_audit_log_target_account ON audit_log(target_account_id)`,
379
- `CREATE INDEX IF NOT EXISTS idx_audit_log_target_actor ON audit_log(target_actor_id)`,
380
- ];
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Auth table DDL — CREATE TABLE, index, and seed statements.
2
+ * Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
3
+ * core auth tables (account, actor, role_grant, auth_session, api_token,
4
+ * bootstrap_lock, invite, app_settings).
3
5
  *
4
- * Consumed by `auth/migrations.ts`. Separated from `auth/account_schema.ts`
5
- * to isolate DDL concerns from runtime types.
6
+ * Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
7
+ * (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
8
+ * in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
6
9
  *
7
10
  * @module
8
11
  */
@@ -24,4 +27,4 @@ export declare const INVITE_SCHEMA = "\nCREATE TABLE IF NOT EXISTS invite (\n i
24
27
  export declare const INVITE_INDEXES: string[];
25
28
  export declare const APP_SETTINGS_SCHEMA = "\nCREATE TABLE IF NOT EXISTS app_settings (\n id INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),\n open_signup BOOLEAN NOT NULL DEFAULT false,\n updated_at TIMESTAMPTZ,\n updated_by UUID\n)";
26
29
  export declare const APP_SETTINGS_SEED = "\nINSERT INTO app_settings (id) VALUES (1) ON CONFLICT DO NOTHING";
27
- //# sourceMappingURL=ddl.d.ts.map
30
+ //# sourceMappingURL=auth_ddl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/auth_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,eAAO,MAAM,cAAc,8WAWzB,CAAC;AAEH,eAAO,MAAM,YAAY,mUAQvB,CAAC;AAEH,eAAO,MAAM,WAAW,wEAC0C,CAAC;AAEnE,eAAO,MAAM,iBAAiB,2ZAU5B,CAAC;AAEH,eAAO,MAAM,kBAAkB,UAI9B,CAAC;AAEF,eAAO,MAAM,mBAAmB,0RAO9B,CAAC;AAEH,eAAO,MAAM,oBAAoB,UAGhC,CAAC;AAEF,eAAO,MAAM,gBAAgB,iUAU3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,4GACsE,CAAC;AAEvG,eAAO,MAAM,yBAAyB,6FACiD,CAAC;AAExF,eAAO,MAAM,eAAe,gFAC8C,CAAC;AAE3E,eAAO,MAAM,qBAAqB,wJAIhC,CAAC;AAEH,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,yHAGP,CAAC;AAE1B,eAAO,MAAM,aAAa,6ZAUxB,CAAC;AAEH,eAAO,MAAM,cAAc,UAI1B,CAAC;AAEF,eAAO,MAAM,mBAAmB,oMAM9B,CAAC;AAEH,eAAO,MAAM,iBAAiB,sEACkC,CAAC"}
@@ -1,8 +1,11 @@
1
1
  /**
2
- * Auth table DDL — CREATE TABLE, index, and seed statements.
2
+ * Identity-table DDL — `CREATE TABLE`, index, and seed statements for the
3
+ * core auth tables (account, actor, role_grant, auth_session, api_token,
4
+ * bootstrap_lock, invite, app_settings).
3
5
  *
4
- * Consumed by `auth/migrations.ts`. Separated from `auth/account_schema.ts`
5
- * to isolate DDL concerns from runtime types.
6
+ * Consumed by `auth/migrations.ts`. Paired with `auth/audit_log_ddl.ts`
7
+ * (audit table) and `auth/role_grant_offer_ddl.ts` (offer table) — DDL lives
8
+ * in `*_ddl.ts`, Zod schemas in `*_schema.ts`.
6
9
  *
7
10
  * @module
8
11
  */
@@ -36,9 +36,9 @@
36
36
  *
37
37
  * @module
38
38
  */
39
- import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './ddl.js';
40
- import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './audit_log_schema.js';
41
- import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './role_grant_offer_schema.js';
39
+ import { ACCOUNT_SCHEMA, ACCOUNT_EMAIL_INDEX, ACCOUNT_USERNAME_CI_INDEX, ACTOR_SCHEMA, ACTOR_INDEX, ROLE_GRANT_SCHEMA, ROLE_GRANT_INDEXES, AUTH_SESSION_SCHEMA, AUTH_SESSION_INDEXES, API_TOKEN_SCHEMA, API_TOKEN_INDEX, BOOTSTRAP_LOCK_SCHEMA, BOOTSTRAP_LOCK_SEED, INVITE_SCHEMA, INVITE_INDEXES, APP_SETTINGS_SCHEMA, APP_SETTINGS_SEED, } from './auth_ddl.js';
40
+ import { AUDIT_LOG_SCHEMA, AUDIT_LOG_INDEXES } from './audit_log_ddl.js';
41
+ import { ROLE_GRANT_OFFER_SCHEMA, ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX, ROLE_GRANT_OFFER_INBOX_INDEX, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, } from './role_grant_offer_ddl.js';
42
42
  /** Namespace identifier for fuz_app auth migrations. */
43
43
  export const AUTH_MIGRATION_NAMESPACE = 'fuz_auth';
44
44
  /**
@@ -105,7 +105,7 @@ export const AUTH_MIGRATIONS = [
105
105
  },
106
106
  // v1: consentful role_grants — role_grant_offer table + scoped role_grants
107
107
  {
108
- name: 'permit_offer_and_scoped_permits',
108
+ name: 'role_grant_offer_and_scoped_role_grants',
109
109
  up: async (db) => {
110
110
  await db.query(ROLE_GRANT_OFFER_SCHEMA);
111
111
  await db.query(ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX);
@@ -209,6 +209,16 @@ export declare const RoleGrantRevokeOutput: z.ZodObject<{
209
209
  revoked: z.ZodLiteral<true>;
210
210
  }, z.core.$strict>;
211
211
  export type RoleGrantRevokeOutput = z.infer<typeof RoleGrantRevokeOutput>;
212
+ /**
213
+ * `rate_limit: 'account'` throttles offer-spam at the authenticated
214
+ * grantor and bounds the account-existence oracle on `to_account_id` —
215
+ * the same shape as `invite_create_action_spec` upstream addresses, where
216
+ * a hostile authed caller iterates recipients to probe
217
+ * `ERROR_ACCOUNT_NOT_FOUND` (and the actor-binding via
218
+ * `ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH`) as an enumeration
219
+ * vector. Failure-outcome audit rows preserve the forensic trail; the
220
+ * rate cap closes the budget.
221
+ */
212
222
  export declare const role_grant_offer_create_action_spec: {
213
223
  method: string;
214
224
  kind: "request_response";
@@ -250,6 +260,7 @@ export declare const role_grant_offer_create_action_spec: {
250
260
  async: true;
251
261
  description: string;
252
262
  error_reasons: ("role_grant_offer_self_target" | "role_grant_offer_role_not_grantable" | "role_grant_offer_not_authorized" | "role_grant_offer_actor_account_mismatch")[];
263
+ rate_limit: "account";
253
264
  };
254
265
  export declare const role_grant_offer_accept_action_spec: {
255
266
  method: string;
@@ -405,6 +416,12 @@ export declare const role_grant_offer_history_action_spec: {
405
416
  async: true;
406
417
  description: string;
407
418
  };
419
+ /**
420
+ * `rate_limit: 'account'` bounds admin-side burn of `role_grant_revoke` —
421
+ * the action is admin-gated and audit-trailed, but the per-account cap
422
+ * keeps a single admin script from churning role_grants in a loop and
423
+ * obscuring audit context for unrelated activity.
424
+ */
408
425
  export declare const role_grant_revoke_action_spec: {
409
426
  method: string;
410
427
  kind: "request_response";
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"role_grant_offer_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_action_specs.ts"],"names":[],"mappings":"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"}
@@ -157,6 +157,16 @@ export const RoleGrantRevokeOutput = z.strictObject({
157
157
  revoked: z.literal(true),
158
158
  });
159
159
  // -- Action specs -----------------------------------------------------------
160
+ /**
161
+ * `rate_limit: 'account'` throttles offer-spam at the authenticated
162
+ * grantor and bounds the account-existence oracle on `to_account_id` —
163
+ * the same shape as `invite_create_action_spec` upstream addresses, where
164
+ * a hostile authed caller iterates recipients to probe
165
+ * `ERROR_ACCOUNT_NOT_FOUND` (and the actor-binding via
166
+ * `ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH`) as an enumeration
167
+ * vector. Failure-outcome audit rows preserve the forensic trail; the
168
+ * rate cap closes the budget.
169
+ */
160
170
  export const role_grant_offer_create_action_spec = {
161
171
  method: 'role_grant_offer_create',
162
172
  kind: 'request_response',
@@ -173,6 +183,7 @@ export const role_grant_offer_create_action_spec = {
173
183
  ERROR_ROLE_GRANT_OFFER_NOT_AUTHORIZED,
174
184
  ERROR_ROLE_GRANT_OFFER_ACTOR_ACCOUNT_MISMATCH,
175
185
  ],
186
+ rate_limit: 'account',
176
187
  };
177
188
  export const role_grant_offer_accept_action_spec = {
178
189
  method: 'role_grant_offer_accept',
@@ -237,6 +248,12 @@ export const role_grant_offer_history_action_spec = {
237
248
  async: true,
238
249
  description: 'List every offer involving the caller (either direction), including terminal rows, newest first. Admins may pass `account_id` to inspect another account.',
239
250
  };
251
+ /**
252
+ * `rate_limit: 'account'` bounds admin-side burn of `role_grant_revoke` —
253
+ * the action is admin-gated and audit-trailed, but the per-account cap
254
+ * keeps a single admin script from churning role_grants in a loop and
255
+ * obscuring audit context for unrelated activity.
256
+ */
240
257
  export const role_grant_revoke_action_spec = {
241
258
  method: 'role_grant_revoke',
242
259
  kind: 'request_response',
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
3
+ * sentinel constants the queries / migrations interpolate.
4
+ *
5
+ * Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
6
+ * Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
7
+ *
8
+ * An offer is a pending grant awaiting recipient consent. Lifecycle states
9
+ * are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
10
+ * at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
11
+ * On accept, the offer's `resulting_role_grant_id` links to the role_grant row
12
+ * produced by `query_accept_offer`.
13
+ *
14
+ * @module
15
+ */
16
+ /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
17
+ export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
18
+ /**
19
+ * Index-side token for the global case in the partial unique index. Uppercase
20
+ * so it cannot collide with consumer-declared `ScopeKindName` values (which
21
+ * are lowercase by regex). Never appears as a column value — column-level
22
+ * `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
23
+ */
24
+ export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
25
+ export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
26
+ /**
27
+ * At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
28
+ *
29
+ * Including `from_actor_id` in the tuple lets multiple grantors coexist —
30
+ * teacher A and teacher B can each have a pending `classroom_student` offer
31
+ * for the same student and scope. A same-grantor re-offer upserts the
32
+ * existing pending row. `COALESCE` collapses `NULL` scopes into the
33
+ * sentinel values so Postgres's NULL-in-unique-index quirk does not allow
34
+ * duplicate global pending offers; the `scope_kind` / `scope_id` pair is
35
+ * always either both null (global) or both non-null (scoped) per the
36
+ * `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
37
+ * always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
38
+ * match this expression literally.
39
+ */
40
+ export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
41
+ /** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
42
+ export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
43
+ //# sourceMappingURL=role_grant_offer_ddl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"role_grant_offer_ddl.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_ddl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,mHAAmH;AACnH,eAAO,MAAM,oCAAoC,yCAAyC,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,WAAW,CAAC;AAEjE,eAAO,MAAM,uBAAuB,qzCAkClC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qCAAqC,2XAYpB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,kOAMX,CAAC"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Role grant offer DDL — `CREATE TABLE` + index statements and the index-side
3
+ * sentinel constants the queries / migrations interpolate.
4
+ *
5
+ * Separated from `auth/role_grant_offer_schema.ts` so the schema module stays
6
+ * Zod-only (paired with `auth/auth_ddl.ts` and `auth/audit_log_ddl.ts`).
7
+ *
8
+ * An offer is a pending grant awaiting recipient consent. Lifecycle states
9
+ * are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
10
+ * at most one of `accepted_at` / `declined_at` / `retracted_at` may be set.
11
+ * On accept, the offer's `resulting_role_grant_id` links to the role_grant row
12
+ * produced by `query_accept_offer`.
13
+ *
14
+ * @module
15
+ */
16
+ /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
17
+ export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
18
+ /**
19
+ * Index-side token for the global case in the partial unique index. Uppercase
20
+ * so it cannot collide with consumer-declared `ScopeKindName` values (which
21
+ * are lowercase by regex). Never appears as a column value — column-level
22
+ * `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
23
+ */
24
+ export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
25
+ export const ROLE_GRANT_OFFER_SCHEMA = `
26
+ CREATE TABLE IF NOT EXISTS role_grant_offer (
27
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
28
+ from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
29
+ to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
30
+ to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
31
+ role TEXT NOT NULL,
32
+ scope_kind TEXT NULL,
33
+ scope_id UUID NULL,
34
+ message TEXT NULL,
35
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
36
+ expires_at TIMESTAMPTZ NOT NULL,
37
+ accepted_at TIMESTAMPTZ NULL,
38
+ declined_at TIMESTAMPTZ NULL,
39
+ decline_reason TEXT NULL,
40
+ retracted_at TIMESTAMPTZ NULL,
41
+ superseded_at TIMESTAMPTZ NULL,
42
+ resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
43
+ CONSTRAINT role_grant_offer_single_terminal CHECK (
44
+ (accepted_at IS NOT NULL)::int
45
+ + (declined_at IS NOT NULL)::int
46
+ + (retracted_at IS NOT NULL)::int
47
+ + (superseded_at IS NOT NULL)::int
48
+ <= 1
49
+ ),
50
+ CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
51
+ (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
52
+ ),
53
+ CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
54
+ decline_reason IS NULL OR declined_at IS NOT NULL
55
+ ),
56
+ CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
57
+ (scope_kind IS NULL) = (scope_id IS NULL)
58
+ )
59
+ )`;
60
+ /**
61
+ * At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
62
+ *
63
+ * Including `from_actor_id` in the tuple lets multiple grantors coexist —
64
+ * teacher A and teacher B can each have a pending `classroom_student` offer
65
+ * for the same student and scope. A same-grantor re-offer upserts the
66
+ * existing pending row. `COALESCE` collapses `NULL` scopes into the
67
+ * sentinel values so Postgres's NULL-in-unique-index quirk does not allow
68
+ * duplicate global pending offers; the `scope_kind` / `scope_id` pair is
69
+ * always either both null (global) or both non-null (scoped) per the
70
+ * `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
71
+ * always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
72
+ * match this expression literally.
73
+ */
74
+ export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
75
+ CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
76
+ ON role_grant_offer (
77
+ to_account_id,
78
+ role,
79
+ COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
80
+ COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
81
+ from_actor_id
82
+ )
83
+ WHERE accepted_at IS NULL
84
+ AND declined_at IS NULL
85
+ AND retracted_at IS NULL
86
+ AND superseded_at IS NULL`;
87
+ /** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
88
+ export const ROLE_GRANT_OFFER_INBOX_INDEX = `
89
+ CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
90
+ ON role_grant_offer (to_account_id, expires_at)
91
+ WHERE accepted_at IS NULL
92
+ AND declined_at IS NULL
93
+ AND retracted_at IS NULL
94
+ AND superseded_at IS NULL`;
95
+ // **Deferred**: a `role_grant_offer_to_actor` partial index belongs here once
96
+ // an actor-side inbox query (`query_role_grant_offer_list_for_actor`) lands —
97
+ // no current consumer filters on `to_actor_id`, and adding the index
98
+ // before the query is paying write-amp for nothing. Land the index in
99
+ // the same slice as the query.
@@ -14,7 +14,7 @@
14
14
  import type { Uuid } from '@fuzdev/fuz_util/id.js';
15
15
  import type { QueryDeps } from '../db/query_deps.js';
16
16
  import type { RoleGrant } from './account_schema.js';
17
- import { type CreateRoleGrantOfferInput, type RoleGrantOffer, type SupersededOffer } from './role_grant_offer_schema.js';
17
+ import type { CreateRoleGrantOfferInput, RoleGrantOffer, SupersededOffer } from './role_grant_offer_schema.js';
18
18
  import type { AuditLogEvent } from './audit_log_schema.js';
19
19
  /**
20
20
  * Error thrown by offer-lifecycle queries when the offer is in a non-pending
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"role_grant_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_queries.ts"],"names":[],"mappings":"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"}
@@ -12,7 +12,7 @@
12
12
  * @module
13
13
  */
14
14
  import { assert_row } from '../db/assert_row.js';
15
- import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_schema.js';
15
+ import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
16
16
  import { query_audit_log } from './audit_log_queries.js';
17
17
  /**
18
18
  * Error thrown by offer-lifecycle queries when the offer is in a non-pending
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Role grant offer DDL, types, and client-safe schemas.
2
+ * Role grant offer types and client-safe schemas.
3
3
  *
4
4
  * An offer is a pending grant awaiting recipient consent. Lifecycle states
5
5
  * are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
@@ -7,41 +7,16 @@
7
7
  * On accept, the offer's `resulting_role_grant_id` links to the role_grant row
8
8
  * produced by `query_accept_offer`.
9
9
  *
10
+ * Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
11
+ *
10
12
  * @module
11
13
  */
12
14
  import { z } from 'zod';
13
15
  import { Uuid } from '@fuzdev/fuz_util/id.js';
14
- /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
15
- export declare const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
16
16
  /** Maximum length of the optional message attached to an offer. */
17
17
  export declare const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
18
18
  /** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
19
19
  export declare const ROLE_GRANT_OFFER_DEFAULT_TTL_MS: number;
20
- export declare const ROLE_GRANT_OFFER_SCHEMA = "\nCREATE TABLE IF NOT EXISTS role_grant_offer (\n id UUID PRIMARY KEY DEFAULT gen_random_uuid(),\n from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,\n to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,\n to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,\n role TEXT NOT NULL,\n scope_kind TEXT NULL,\n scope_id UUID NULL,\n message TEXT NULL,\n created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),\n expires_at TIMESTAMPTZ NOT NULL,\n accepted_at TIMESTAMPTZ NULL,\n declined_at TIMESTAMPTZ NULL,\n decline_reason TEXT NULL,\n retracted_at TIMESTAMPTZ NULL,\n superseded_at TIMESTAMPTZ NULL,\n resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,\n CONSTRAINT role_grant_offer_single_terminal CHECK (\n (accepted_at IS NOT NULL)::int\n + (declined_at IS NOT NULL)::int\n + (retracted_at IS NOT NULL)::int\n + (superseded_at IS NOT NULL)::int\n <= 1\n ),\n CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (\n (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)\n ),\n CONSTRAINT role_grant_offer_reason_iff_declined CHECK (\n decline_reason IS NULL OR declined_at IS NOT NULL\n ),\n CONSTRAINT role_grant_offer_scope_kind_paired CHECK (\n (scope_kind IS NULL) = (scope_id IS NULL)\n )\n)";
21
- /**
22
- * Index-side token for the global case in the partial unique index. Uppercase
23
- * so it cannot collide with consumer-declared `ScopeKindName` values (which
24
- * are lowercase by regex). Never appears as a column value — column-level
25
- * `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
26
- */
27
- export declare const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = "GLOBAL";
28
- /**
29
- * At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
30
- *
31
- * Including `from_actor_id` in the tuple lets multiple grantors coexist —
32
- * teacher A and teacher B can each have a pending `classroom_student` offer
33
- * for the same student and scope. A same-grantor re-offer upserts the
34
- * existing pending row. `COALESCE` collapses `NULL` scopes into the
35
- * sentinel values so Postgres's NULL-in-unique-index quirk does not allow
36
- * duplicate global pending offers; the `scope_kind` / `scope_id` pair is
37
- * always either both null (global) or both non-null (scoped) per the
38
- * `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
39
- * always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
40
- * match this expression literally.
41
- */
42
- export declare const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = "\nCREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique\n ON role_grant_offer (\n to_account_id,\n role,\n COALESCE(scope_kind, 'GLOBAL'),\n COALESCE(scope_id, '00000000-0000-0000-0000-000000000000'::uuid),\n from_actor_id\n )\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
43
- /** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
44
- export declare const ROLE_GRANT_OFFER_INBOX_INDEX = "\nCREATE INDEX IF NOT EXISTS role_grant_offer_inbox\n ON role_grant_offer (to_account_id, expires_at)\n WHERE accepted_at IS NULL\n AND declined_at IS NULL\n AND retracted_at IS NULL\n AND superseded_at IS NULL";
45
20
  /** Role grant offer row as returned by the database. */
46
21
  export interface RoleGrantOffer {
47
22
  id: Uuid;
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAI5C,mHAAmH;AACnH,eAAO,MAAM,oCAAoC,yCAAyC,CAAC;AAE3F,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,eAAO,MAAM,uBAAuB,qzCAkClC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,WAAW,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qCAAqC,2XAYpB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,kOAMX,CAAC;AAQ/B,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD0D,CAAC;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
1
+ {"version":3,"file":"role_grant_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAI5C,mEAAmE;AACnE,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,yFAAyF;AACzF,eAAO,MAAM,+BAA+B,QAA2B,CAAC;AAExE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC9B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;;;;;;OAWG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;OAMG;IACH,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,uBAAuB,EAAE,IAAI,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,yBAAyB;IACzC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;kBAoD0D,CAAC;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEpE,gEAAgE;AAChE,eAAO,MAAM,wBAAwB,GAAI,OAAO,cAAc,KAAG,kBAiB/D,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Role grant offer DDL, types, and client-safe schemas.
2
+ * Role grant offer types and client-safe schemas.
3
3
  *
4
4
  * An offer is a pending grant awaiting recipient consent. Lifecycle states
5
5
  * are mutually exclusive via a CHECK constraint (`role_grant_offer_single_terminal`):
@@ -7,94 +7,17 @@
7
7
  * On accept, the offer's `resulting_role_grant_id` links to the role_grant row
8
8
  * produced by `query_accept_offer`.
9
9
  *
10
+ * Table DDL and index-side sentinel constants live in `auth/role_grant_offer_ddl.ts`.
11
+ *
10
12
  * @module
11
13
  */
12
14
  import { z } from 'zod';
13
15
  import { Uuid } from '@fuzdev/fuz_util/id.js';
14
16
  import { RoleName } from './role_schema.js';
15
- /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
16
- export const ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
17
17
  /** Maximum length of the optional message attached to an offer. */
18
18
  export const ROLE_GRANT_OFFER_MESSAGE_LENGTH_MAX = 500;
19
19
  /** Default TTL for a newly created offer — 30 days. Matches GitHub org-invite expiry. */
20
20
  export const ROLE_GRANT_OFFER_DEFAULT_TTL_MS = 30 * 24 * 60 * 60 * 1000;
21
- export const ROLE_GRANT_OFFER_SCHEMA = `
22
- CREATE TABLE IF NOT EXISTS role_grant_offer (
23
- id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
24
- from_actor_id UUID NOT NULL REFERENCES actor(id) ON DELETE CASCADE,
25
- to_account_id UUID NOT NULL REFERENCES account(id) ON DELETE CASCADE,
26
- to_actor_id UUID NULL REFERENCES actor(id) ON DELETE CASCADE,
27
- role TEXT NOT NULL,
28
- scope_kind TEXT NULL,
29
- scope_id UUID NULL,
30
- message TEXT NULL,
31
- created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
32
- expires_at TIMESTAMPTZ NOT NULL,
33
- accepted_at TIMESTAMPTZ NULL,
34
- declined_at TIMESTAMPTZ NULL,
35
- decline_reason TEXT NULL,
36
- retracted_at TIMESTAMPTZ NULL,
37
- superseded_at TIMESTAMPTZ NULL,
38
- resulting_role_grant_id UUID NULL REFERENCES role_grant(id) ON DELETE SET NULL,
39
- CONSTRAINT role_grant_offer_single_terminal CHECK (
40
- (accepted_at IS NOT NULL)::int
41
- + (declined_at IS NOT NULL)::int
42
- + (retracted_at IS NOT NULL)::int
43
- + (superseded_at IS NOT NULL)::int
44
- <= 1
45
- ),
46
- CONSTRAINT role_grant_offer_role_grant_iff_accepted CHECK (
47
- (accepted_at IS NOT NULL) = (resulting_role_grant_id IS NOT NULL)
48
- ),
49
- CONSTRAINT role_grant_offer_reason_iff_declined CHECK (
50
- decline_reason IS NULL OR declined_at IS NOT NULL
51
- ),
52
- CONSTRAINT role_grant_offer_scope_kind_paired CHECK (
53
- (scope_kind IS NULL) = (scope_id IS NULL)
54
- )
55
- )`;
56
- /**
57
- * Index-side token for the global case in the partial unique index. Uppercase
58
- * so it cannot collide with consumer-declared `ScopeKindName` values (which
59
- * are lowercase by regex). Never appears as a column value — column-level
60
- * `scope_kind = NULL` and `scope_id = NULL` together encode the global case.
61
- */
62
- export const ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN = 'GLOBAL';
63
- /**
64
- * At most one pending offer per (to_account, role, scope_kind, scope, from_actor).
65
- *
66
- * Including `from_actor_id` in the tuple lets multiple grantors coexist —
67
- * teacher A and teacher B can each have a pending `classroom_student` offer
68
- * for the same student and scope. A same-grantor re-offer upserts the
69
- * existing pending row. `COALESCE` collapses `NULL` scopes into the
70
- * sentinel values so Postgres's NULL-in-unique-index quirk does not allow
71
- * duplicate global pending offers; the `scope_kind` / `scope_id` pair is
72
- * always either both null (global) or both non-null (scoped) per the
73
- * `role_grant_offer_scope_kind_paired` CHECK, so the two COALESCE expressions
74
- * always agree. The ON CONFLICT target in `query_role_grant_offer_create` must
75
- * match this expression literally.
76
- */
77
- export const ROLE_GRANT_OFFER_PENDING_UNIQUE_INDEX = `
78
- CREATE UNIQUE INDEX IF NOT EXISTS role_grant_offer_pending_unique
79
- ON role_grant_offer (
80
- to_account_id,
81
- role,
82
- COALESCE(scope_kind, '${ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN}'),
83
- COALESCE(scope_id, '${ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID}'::uuid),
84
- from_actor_id
85
- )
86
- WHERE accepted_at IS NULL
87
- AND declined_at IS NULL
88
- AND retracted_at IS NULL
89
- AND superseded_at IS NULL`;
90
- /** Inbox lookup — pending offers for an account, ordered by soonest expiry. */
91
- export const ROLE_GRANT_OFFER_INBOX_INDEX = `
92
- CREATE INDEX IF NOT EXISTS role_grant_offer_inbox
93
- ON role_grant_offer (to_account_id, expires_at)
94
- WHERE accepted_at IS NULL
95
- AND declined_at IS NULL
96
- AND retracted_at IS NULL
97
- AND superseded_at IS NULL`;
98
21
  /** Zod schema for client-safe role_grant offer data. */
99
22
  export const RoleGrantOfferJson = z
100
23
  .strictObject({
@@ -9,7 +9,7 @@
9
9
  import type { Uuid } from '@fuzdev/fuz_util/id.js';
10
10
  import type { QueryDeps } from '../db/query_deps.js';
11
11
  import type { RoleGrant, CreateRoleGrantInput } from './account_schema.js';
12
- import { type SupersededOffer } from './role_grant_offer_schema.js';
12
+ import type { SupersededOffer } from './role_grant_offer_schema.js';
13
13
  /**
14
14
  * Grant a role_grant to an actor.
15
15
  * Idempotent — if an active role_grant already exists for this actor, role, and
@@ -1 +1 @@
1
- {"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"role_grant_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/role_grant_queries.ts"],"names":[],"mappings":"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"}
@@ -7,7 +7,7 @@
7
7
  * @module
8
8
  */
9
9
  import { assert_row } from '../db/assert_row.js';
10
- import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_schema.js';
10
+ import { ROLE_GRANT_OFFER_SCOPE_KIND_GLOBAL_TOKEN, ROLE_GRANT_OFFER_SCOPE_SENTINEL_UUID, } from './role_grant_offer_ddl.js';
11
11
  /**
12
12
  * Grant a role_grant to an actor.
13
13
  * Idempotent — if an active role_grant already exists for this actor, role, and
@@ -29,6 +29,13 @@ export declare const SelfServiceRoleSetOutput: z.ZodObject<{
29
29
  changed: z.ZodBoolean;
30
30
  }, z.core.$strict>;
31
31
  export type SelfServiceRoleSetOutput = z.infer<typeof SelfServiceRoleSetOutput>;
32
+ /**
33
+ * `rate_limit: 'account'` bounds audit-row churn. The toggle is idempotent
34
+ * (`changed: false` re-grants/re-revokes), but every call still writes a
35
+ * `role_grant_create` or `role_grant_revoke` audit row with
36
+ * `self_service: true`. Without the cap, a caller could flap the role in
37
+ * a loop to inflate the audit log and obscure other activity.
38
+ */
32
39
  export declare const self_service_role_set_action_spec: {
33
40
  method: string;
34
41
  kind: "request_response";
@@ -50,6 +57,7 @@ export declare const self_service_role_set_action_spec: {
50
57
  }, z.core.$strict>;
51
58
  async: true;
52
59
  description: string;
60
+ rate_limit: "account";
53
61
  };
54
62
  /**
55
63
  * All self-service role action specs — a codegen-ready registry. Single-element
@@ -1 +1 @@
1
- {"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;CAWT,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,aAAa,CAAC,yBAAyB,CAEvF,CAAC"}
1
+ {"version":3,"file":"self_service_role_action_specs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_action_specs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAIzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,yCAAyC;AACzC,eAAO,MAAM,uBAAuB;;;;kBAOlC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAE9E;;;;GAIG;AACH,eAAO,MAAM,wBAAwB;;;;kBAInC,CAAC;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAEhF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;;;;;;CAYT,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,aAAa,CAAC,yBAAyB,CAEvF,CAAC"}