@fuzdev/fuz_app 0.39.0 → 0.41.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 (92) hide show
  1. package/dist/actions/action_codegen.d.ts +0 -9
  2. package/dist/actions/action_codegen.d.ts.map +1 -1
  3. package/dist/actions/action_codegen.js +2 -35
  4. package/dist/actions/action_event.d.ts.map +1 -1
  5. package/dist/actions/action_event.js +1 -1
  6. package/dist/actions/action_types.d.ts +1 -1
  7. package/dist/actions/action_types.d.ts.map +1 -1
  8. package/dist/actions/register_action_ws.d.ts +1 -1
  9. package/dist/actions/register_action_ws.d.ts.map +1 -1
  10. package/dist/actions/transports_ws_backend.d.ts +1 -1
  11. package/dist/actions/transports_ws_backend.d.ts.map +1 -1
  12. package/dist/actions/transports_ws_backend.js +1 -1
  13. package/dist/auth/CLAUDE.md +117 -22
  14. package/dist/auth/account_actions.d.ts +5 -3
  15. package/dist/auth/account_actions.d.ts.map +1 -1
  16. package/dist/auth/account_actions.js +5 -6
  17. package/dist/auth/account_queries.d.ts.map +1 -1
  18. package/dist/auth/account_routes.d.ts.map +1 -1
  19. package/dist/auth/account_routes.js +7 -7
  20. package/dist/auth/account_schema.d.ts +1 -1
  21. package/dist/auth/account_schema.d.ts.map +1 -1
  22. package/dist/auth/account_schema.js +1 -1
  23. package/dist/auth/admin_action_specs.d.ts +6 -138
  24. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  25. package/dist/auth/admin_action_specs.js +5 -4
  26. package/dist/auth/admin_actions.d.ts +4 -3
  27. package/dist/auth/admin_actions.d.ts.map +1 -1
  28. package/dist/auth/admin_actions.js +10 -10
  29. package/dist/auth/app_settings_schema.d.ts +1 -1
  30. package/dist/auth/app_settings_schema.d.ts.map +1 -1
  31. package/dist/auth/app_settings_schema.js +1 -1
  32. package/dist/auth/audit_log_queries.d.ts +16 -8
  33. package/dist/auth/audit_log_queries.d.ts.map +1 -1
  34. package/dist/auth/audit_log_queries.js +8 -11
  35. package/dist/auth/audit_log_schema.d.ts +28 -75
  36. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  37. package/dist/auth/audit_log_schema.js +23 -5
  38. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  39. package/dist/auth/bootstrap_routes.js +3 -3
  40. package/dist/auth/cleanup.d.ts +9 -1
  41. package/dist/auth/cleanup.d.ts.map +1 -1
  42. package/dist/auth/cleanup.js +2 -2
  43. package/dist/auth/deps.d.ts +13 -1
  44. package/dist/auth/deps.d.ts.map +1 -1
  45. package/dist/auth/invite_schema.d.ts +1 -1
  46. package/dist/auth/invite_schema.d.ts.map +1 -1
  47. package/dist/auth/invite_schema.js +1 -1
  48. package/dist/auth/permit_offer_action_specs.d.ts.map +1 -1
  49. package/dist/auth/permit_offer_action_specs.js +1 -1
  50. package/dist/auth/permit_offer_actions.d.ts +16 -2
  51. package/dist/auth/permit_offer_actions.d.ts.map +1 -1
  52. package/dist/auth/permit_offer_actions.js +26 -8
  53. package/dist/auth/permit_offer_notifications.d.ts +11 -6
  54. package/dist/auth/permit_offer_notifications.d.ts.map +1 -1
  55. package/dist/auth/permit_offer_notifications.js +11 -8
  56. package/dist/auth/permit_offer_queries.d.ts +1 -1
  57. package/dist/auth/permit_offer_queries.d.ts.map +1 -1
  58. package/dist/auth/permit_offer_schema.d.ts +1 -1
  59. package/dist/auth/permit_offer_schema.d.ts.map +1 -1
  60. package/dist/auth/permit_offer_schema.js +1 -1
  61. package/dist/auth/permit_queries.d.ts +50 -1
  62. package/dist/auth/permit_queries.d.ts.map +1 -1
  63. package/dist/auth/permit_queries.js +55 -0
  64. package/dist/auth/self_service_role_action_specs.d.ts +83 -0
  65. package/dist/auth/self_service_role_action_specs.d.ts.map +1 -0
  66. package/dist/auth/self_service_role_action_specs.js +71 -0
  67. package/dist/auth/self_service_role_actions.d.ts +67 -0
  68. package/dist/auth/self_service_role_actions.d.ts.map +1 -0
  69. package/dist/auth/self_service_role_actions.js +139 -0
  70. package/dist/auth/signup_routes.d.ts.map +1 -1
  71. package/dist/auth/signup_routes.js +2 -2
  72. package/dist/auth/standard_rpc_actions.d.ts +1 -1
  73. package/dist/auth/standard_rpc_actions.js +1 -1
  74. package/dist/server/app_backend.d.ts +9 -1
  75. package/dist/server/app_backend.d.ts.map +1 -1
  76. package/dist/server/app_backend.js +12 -1
  77. package/dist/testing/CLAUDE.md +1 -1
  78. package/dist/testing/admin_integration.d.ts.map +1 -1
  79. package/dist/testing/app_server.d.ts +13 -2
  80. package/dist/testing/app_server.d.ts.map +1 -1
  81. package/dist/testing/app_server.js +6 -1
  82. package/dist/testing/entities.d.ts.map +1 -1
  83. package/dist/testing/ws_round_trip.d.ts +1 -1
  84. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  85. package/dist/testing/ws_round_trip.js +1 -1
  86. package/dist/ui/ui_format.d.ts +2 -3
  87. package/dist/ui/ui_format.d.ts.map +1 -1
  88. package/dist/ui/ui_format.js +1 -1
  89. package/package.json +4 -4
  90. package/dist/uuid.d.ts +0 -12
  91. package/dist/uuid.d.ts.map +0 -1
  92. package/dist/uuid.js +0 -9
@@ -1 +1 @@
1
- {"version":3,"file":"permit_offer_notifications.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_notifications.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permit_offer_notifications.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_notifications.ts"],"names":[],"mappings":"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"}
@@ -10,8 +10,9 @@
10
10
  * - `permit_offer_retracted` → recipient's sockets when a grantor retracts
11
11
  * - `permit_offer_accepted` → grantor's sockets when the recipient accepts
12
12
  * - `permit_offer_declined` → grantor's sockets when the recipient declines
13
- * - `permit_offer_supersede` → grantor's sockets when a sibling accept or
14
- * a revoke of the resulting permit obsoletes their pending offer
13
+ * - `permit_offer_supersede` → grantor's sockets when a sibling accept,
14
+ * a revoke of the resulting permit, or destruction of the parent scope
15
+ * row obsoletes their pending offer
15
16
  * - `permit_revoke` → revokee's sockets when one of their active permits
16
17
  * is revoked (companion to the `permit_revoke` audit event)
17
18
  *
@@ -29,9 +30,9 @@
29
30
  * @module
30
31
  */
31
32
  import { z } from 'zod';
33
+ import { Uuid as UuidSchema } from '@fuzdev/fuz_util/id.js';
32
34
  import { create_action_event_spec } from '../actions/action_bridge.js';
33
35
  import { create_jsonrpc_notification } from '../http/jsonrpc_helpers.js';
34
- import { Uuid as UuidSchema } from '../uuid.js';
35
36
  import { RoleName } from './role_schema.js';
36
37
  import { PermitOfferJson } from './permit_offer_schema.js';
37
38
  import { PERMIT_REVOKED_REASON_LENGTH_MAX } from './account_schema.js';
@@ -66,13 +67,15 @@ export const PermitOfferDeclinedParams = z.strictObject({
66
67
  /**
67
68
  * Params for `permit_offer_supersede`. Fires to the grantor's sockets when
68
69
  * their pending offer is obsoleted — either by a sibling accept
69
- * (`reason: 'sibling_accepted'`) or by revoke of the resulting permit
70
- * (`reason: 'permit_revoked'`). `cause_id` points at the accepted offer id
71
- * or the revoked permit id respectively.
70
+ * (`reason: 'sibling_accepted'`), by revoke of the resulting permit
71
+ * (`reason: 'permit_revoked'`), or by deletion of the parent scope row
72
+ * the offer was bound to (`reason: 'scope_destroyed'`). `cause_id` points
73
+ * at the accepted offer id, the revoked permit id, or the destroyed scope
74
+ * row id respectively.
72
75
  */
73
76
  export const PermitOfferSupersedeParams = z.strictObject({
74
77
  offer: PermitOfferJson,
75
- reason: z.enum(['sibling_accepted', 'permit_revoked']),
78
+ reason: z.enum(['sibling_accepted', 'permit_revoked', 'scope_destroyed']),
76
79
  cause_id: UuidSchema,
77
80
  });
78
81
  /**
@@ -142,7 +145,7 @@ export const permit_offer_supersede_notification_spec = {
142
145
  input: PermitOfferSupersedeParams,
143
146
  output: z.void(),
144
147
  async: true,
145
- description: 'A grantor’s pending permit offer was obsoleted by a sibling accept or by revoke of the resulting permit.',
148
+ description: 'A grantor’s pending permit offer was obsoleted by a sibling accept, by revoke of the resulting permit, or by destruction of the parent scope row.',
146
149
  };
147
150
  export const permit_revoke_notification_spec = {
148
151
  method: PERMIT_REVOKE_NOTIFICATION_METHOD,
@@ -11,8 +11,8 @@
11
11
  *
12
12
  * @module
13
13
  */
14
+ import type { Uuid } from '@fuzdev/fuz_util/id.js';
14
15
  import type { QueryDeps } from '../db/query_deps.js';
15
- import type { Uuid } from '../uuid.js';
16
16
  import type { Permit } from './account_schema.js';
17
17
  import { type CreatePermitOfferInput, type PermitOffer, type SupersededOffer } from './permit_offer_schema.js';
18
18
  import type { AuditLogEvent } from './audit_log_schema.js';
@@ -1 +1 @@
1
- {"version":3,"file":"permit_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAEN,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEzD;;;;;GAKG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;gBAC7C,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACtC,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;;CAKpD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,OAAO,sBAAsB,KAC3B,OAAO,CAAC,WAAW,CAyBrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,eAAe,MAAM,EACrB,QAAQ,MAAM,GAAG,IAAI,KACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAe5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,eAAe,MAAM,KACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAe5B,CAAC;AA8BF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,eAAe,MAAM,KACnB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAY5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,cAAW,EACX,eAAU,KACR,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAS5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAY5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,KACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAU5B,CAAC;AAEF,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,IAAI,CAAC;IACf,mGAAmG;IACnG,aAAa,EAAE,IAAI,CAAC;IACpB,gDAAgD;IAChD,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB;AAED,yHAAyH;AACzH,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,4IAA4I;IAC5I,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC1C,sLAAsL;IACtL,YAAY,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,OAAO,gBAAgB,KACrB,OAAO,CAAC,iBAAiB,CAqK3B,CAAC"}
1
+ {"version":3,"file":"permit_offer_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAEjD,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AAEnD,OAAO,KAAK,EAAC,MAAM,EAAC,MAAM,qBAAqB,CAAC;AAEhD,OAAO,EAEN,KAAK,sBAAsB,EAC3B,KAAK,WAAW,EAChB,KAAK,eAAe,EACpB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,uBAAuB,CAAC;AAEzD;;;;;GAKG;AACH,qBAAa,+BAAgC,SAAQ,KAAK;gBAC7C,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;gBACtC,QAAQ,EAAE,MAAM;CAI5B;AAED;;;;;;GAMG;AACH,qBAAa,0BAA2B,SAAQ,KAAK;;CAKpD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,SAAS,EACf,OAAO,sBAAsB,KAC3B,OAAO,CAAC,WAAW,CAyBrB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,eAAe,MAAM,EACrB,QAAQ,MAAM,GAAG,IAAI,KACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAe5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACtC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,eAAe,MAAM,KACnB,OAAO,CAAC,WAAW,GAAG,IAAI,CAe5B,CAAC;AA8BF;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,GACnC,MAAM,SAAS,EACf,eAAe,MAAM,KACnB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAY5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sCAAsC,GAClD,MAAM,SAAS,EACf,YAAY,MAAM,EAClB,cAAW,EACX,eAAU,KACR,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAS5B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,+BAA+B,GAC3C,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,WAAW,GAAG,IAAI,CAY5B,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,SAAS,KACb,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAU5B,CAAC;AAEF,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAChC,QAAQ,EAAE,IAAI,CAAC;IACf,mGAAmG;IACnG,aAAa,EAAE,IAAI,CAAC;IACpB,gDAAgD;IAChD,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACnB;AAED,yHAAyH;AACzH,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,WAAW,CAAC;IACnB,4IAA4I;IAC5I,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAC1C,sLAAsL;IACtL,YAAY,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,OAAO,gBAAgB,KACrB,OAAO,CAAC,iBAAiB,CAqK3B,CAAC"}
@@ -10,7 +10,7 @@
10
10
  * @module
11
11
  */
12
12
  import { z } from 'zod';
13
- import { Uuid } from '../uuid.js';
13
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
14
14
  /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
15
15
  export declare const PERMIT_OFFER_SCOPE_SENTINEL_UUID = "00000000-0000-0000-0000-000000000000";
16
16
  /** Maximum length of the optional message attached to an offer. */
@@ -1 +1 @@
1
- {"version":3,"file":"permit_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_offer_schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,OAAO,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AAGhC,mHAAmH;AACnH,eAAO,MAAM,gCAAgC,yCAAyC,CAAC;AAEvF,mEAAmE;AACnE,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,QAA2B,CAAC;AAEpE,eAAO,MAAM,mBAAmB,6kCA6B9B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,8UAWhB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,0NAMP,CAAC;AAE/B,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,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,mBAAmB,EAAE,IAAI,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IACnD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,oDAAoD;AACpD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;kBA4CyD,CAAC;AACtF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,GAAI,OAAO,WAAW,KAAG,eAexD,CAAC"}
1
+ {"version":3,"file":"permit_offer_schema.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_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,gCAAgC,yCAAyC,CAAC;AAEvF,mEAAmE;AACnE,eAAO,MAAM,+BAA+B,MAAM,CAAC;AAEnD,yFAAyF;AACzF,eAAO,MAAM,2BAA2B,QAA2B,CAAC;AAEpE,eAAO,MAAM,mBAAmB,6kCA6B9B,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iCAAiC,8UAWhB,CAAC;AAE/B,+EAA+E;AAC/E,eAAO,MAAM,wBAAwB,0NAMP,CAAC;AAE/B,oDAAoD;AACpD,MAAM,WAAW,WAAW;IAC3B,EAAE,EAAE,IAAI,CAAC;IACT,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,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,mBAAmB,EAAE,IAAI,GAAG,IAAI,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IACnD,eAAe,EAAE,IAAI,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,IAAI,CAAC;IACpB,aAAa,EAAE,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,EAAE,IAAI,CAAC;CACjB;AAED,oDAAoD;AACpD,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;kBA4CyD,CAAC;AACtF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAE9D,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,GAAI,OAAO,WAAW,KAAG,eAexD,CAAC"}
@@ -10,7 +10,7 @@
10
10
  * @module
11
11
  */
12
12
  import { z } from 'zod';
13
- import { Uuid } from '../uuid.js';
13
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
14
14
  import { RoleName } from './role_schema.js';
15
15
  /** Sentinel UUID used inside the partial unique indexes to collapse `scope_id IS NULL` into a comparable value. */
16
16
  export const PERMIT_OFFER_SCOPE_SENTINEL_UUID = '00000000-0000-0000-0000-000000000000';
@@ -6,8 +6,8 @@
6
6
  *
7
7
  * @module
8
8
  */
9
+ import type { Uuid } from '@fuzdev/fuz_util/id.js';
9
10
  import type { QueryDeps } from '../db/query_deps.js';
10
- import type { Uuid } from '../uuid.js';
11
11
  import type { Permit, GrantPermitInput } from './account_schema.js';
12
12
  import { type SupersededOffer } from './permit_offer_schema.js';
13
13
  /**
@@ -110,6 +110,55 @@ export declare const query_permit_list_for_actor: (deps: QueryDeps, actor_id: st
110
110
  * @returns the account ID, or `null`
111
111
  */
112
112
  export declare const query_permit_find_account_id_for_role: (deps: QueryDeps, role: string) => Promise<string | null>;
113
+ /** Result of `query_permit_revoke_for_scope` — every permit revoked plus every pending offer superseded by the scope-wide cascade. */
114
+ export interface RevokeForScopeResult {
115
+ /**
116
+ * One entry per permit revoked by this call. Carries the revokee's
117
+ * `account_id` so callers can fan out a `permit_revoke` notification per
118
+ * permit. Empty array means no active permit was bound to the scope.
119
+ */
120
+ revoked: Array<{
121
+ permit_id: Uuid;
122
+ role: string;
123
+ scope_id: Uuid;
124
+ account_id: Uuid;
125
+ }>;
126
+ /**
127
+ * Every pending offer at the scope — tuple-matched and orphan, undifferentiated
128
+ * — superseded in the same cascade. Each entry carries its grantor's
129
+ * `from_account_id` for `permit_offer_supersede` notification fan-out.
130
+ *
131
+ * The caller is responsible for emitting `permit_offer_supersede` audit
132
+ * events with `reason: 'scope_destroyed'` and `cause_id: <destroyed scope row id>`
133
+ * per entry — the cause of every supersede here is the scope deletion,
134
+ * not any individual permit revoke (the revokes are themselves
135
+ * consequences of the scope going away).
136
+ */
137
+ superseded_offers: Array<SupersededOffer>;
138
+ }
139
+ /**
140
+ * Revoke every active permit bound to a scope and supersede every pending
141
+ * offer at the scope, in one cascade.
142
+ *
143
+ * Use this from a consumer's parent-scope delete handler (e.g., classroom
144
+ * deletion) — `permit.scope_id` and `permit_offer.scope_id` are polymorphic
145
+ * with no FK constraint by design, so a parent row deletion would otherwise
146
+ * orphan permits and offers. The cascade is **role-agnostic**: anything
147
+ * attached to the destroyed scope is cleaned up.
148
+ *
149
+ * Both updates run as separate statements inside the caller's transaction
150
+ * (mirrors `query_permit_revoke_role`'s shape). The two halves are
151
+ * independent — orphan pending offers can exist at a scope with no active
152
+ * permits, so the supersede half always runs even when no permit was
153
+ * revoked.
154
+ *
155
+ * @param deps - query dependencies
156
+ * @param scope_id - the scope whose permits and offers to terminate
157
+ * @param revoked_by - the actor performing the cascade (audit trail)
158
+ * @param reason - optional free-form reason, stamped on `permit.revoked_reason`.
159
+ * @returns the revoked permits (with `account_id` for fan-out) and superseded offers (with `from_account_id` for fan-out)
160
+ */
161
+ export declare const query_permit_revoke_for_scope: (deps: QueryDeps, scope_id: Uuid, revoked_by: Uuid | null, reason?: string | null) => Promise<RevokeForScopeResult>;
113
162
  /** Result of `query_permit_revoke_role` — every permit revoked plus the pending offers superseded by the bulk revoke. */
114
163
  export interface RevokeRoleResult {
115
164
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"permit_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_queries.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,qBAAqB,CAAC;AACnD,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,KAAK,EAAC,MAAM,EAAE,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAElE,OAAO,EAAmC,KAAK,eAAe,EAAC,MAAM,0BAA0B,CAAC;AAEhG;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,kBAAkB,GAC9B,MAAM,SAAS,EACf,OAAO,gBAAgB,KACrB,OAAO,CAAC,MAAM,CA4BhB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uCAAuC,GACnD,MAAM,SAAS,EACf,WAAW,MAAM,EACjB,UAAU,MAAM,KACd,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,GAAG,IAAI,CAO/B,CAAC;AAEF,6GAA6G;AAC7G,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,IAAI,GAAG,IAAI,CAAC;IACtB;;;;;;;;OAQG;IACH,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,mBAAmB,GAC/B,MAAM,SAAS,EACf,WAAW,IAAI,EACf,UAAU,IAAI,EACd,YAAY,IAAI,GAAG,IAAI,EACvB,SAAS,MAAM,GAAG,IAAI,KACpB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAsCnC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CASvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,GACjC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,MAAM,MAAM,EACZ,WAAW,MAAM,GAAG,IAAI,KACtB,OAAO,CAAC,OAAO,CAajB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,2BAA2B,GACvC,MAAM,SAAS,EACf,UAAU,MAAM,KACd,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAKvB,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,qCAAqC,GACjD,MAAM,SAAS,EACf,MAAM,MAAM,KACV,OAAO,CAAC,MAAM,GAAG,IAAI,CAavB,CAAC;AAEF,yHAAyH;AACzH,MAAM,WAAW,gBAAgB;IAChC;;;;OAIG;IACH,OAAO,EAAE,KAAK,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;IAC/F;;;;;OAKG;IACH,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,wBAAwB,GACpC,MAAM,SAAS,EACf,UAAU,MAAM,EAChB,MAAM,MAAM,EACZ,YAAY,MAAM,GAAG,IAAI,EACzB,SAAS,MAAM,GAAG,IAAI,KACpB,OAAO,CAAC,gBAAgB,CA2C1B,CAAC"}
1
+ {"version":3,"file":"permit_queries.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/permit_queries.ts"],"names":[],"mappings":"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"}
@@ -178,6 +178,61 @@ export const query_permit_find_account_id_for_role = async (deps, role) => {
178
178
  LIMIT 1`, [role]);
179
179
  return row?.account_id ?? null;
180
180
  };
181
+ /**
182
+ * Revoke every active permit bound to a scope and supersede every pending
183
+ * offer at the scope, in one cascade.
184
+ *
185
+ * Use this from a consumer's parent-scope delete handler (e.g., classroom
186
+ * deletion) — `permit.scope_id` and `permit_offer.scope_id` are polymorphic
187
+ * with no FK constraint by design, so a parent row deletion would otherwise
188
+ * orphan permits and offers. The cascade is **role-agnostic**: anything
189
+ * attached to the destroyed scope is cleaned up.
190
+ *
191
+ * Both updates run as separate statements inside the caller's transaction
192
+ * (mirrors `query_permit_revoke_role`'s shape). The two halves are
193
+ * independent — orphan pending offers can exist at a scope with no active
194
+ * permits, so the supersede half always runs even when no permit was
195
+ * revoked.
196
+ *
197
+ * @param deps - query dependencies
198
+ * @param scope_id - the scope whose permits and offers to terminate
199
+ * @param revoked_by - the actor performing the cascade (audit trail)
200
+ * @param reason - optional free-form reason, stamped on `permit.revoked_reason`.
201
+ * @returns the revoked permits (with `account_id` for fan-out) and superseded offers (with `from_account_id` for fan-out)
202
+ */
203
+ export const query_permit_revoke_for_scope = async (deps, scope_id, revoked_by, reason) => {
204
+ // Revoke every active permit at the scope. CTE pulls `account_id` via a
205
+ // join on `actor` so callers fan out `permit_revoke` notifications without
206
+ // an extra round-trip.
207
+ const revoked = await deps.db.query(`WITH updated AS (
208
+ UPDATE permit
209
+ SET revoked_at = NOW(), revoked_by = $2, revoked_reason = $3
210
+ WHERE scope_id = $1 AND revoked_at IS NULL
211
+ RETURNING id, role, scope_id, actor_id
212
+ )
213
+ SELECT u.id AS permit_id, u.role, u.scope_id, a.account_id
214
+ FROM updated u
215
+ JOIN actor a ON a.id = u.actor_id`, [scope_id, revoked_by ?? null, reason ?? null]);
216
+ // Supersede every pending offer at the scope — tuple-matched or orphan,
217
+ // no distinction. The cause of every supersede in this cascade is the
218
+ // scope deletion; offers tuple-matched to a revoked permit are not
219
+ // tagged separately because the revoke is itself a consequence of the
220
+ // scope going away.
221
+ const superseded_offers = await deps.db.query(`WITH updated AS (
222
+ UPDATE permit_offer o
223
+ SET superseded_at = NOW()
224
+ WHERE o.scope_id = $1
225
+ AND o.accepted_at IS NULL
226
+ AND o.declined_at IS NULL
227
+ AND o.retracted_at IS NULL
228
+ AND o.superseded_at IS NULL
229
+ RETURNING o.*
230
+ )
231
+ SELECT u.*, grantor.account_id AS from_account_id
232
+ FROM updated u
233
+ JOIN actor grantor ON grantor.id = u.from_actor_id`, [scope_id]);
234
+ return { revoked, superseded_offers };
235
+ };
181
236
  /**
182
237
  * Revoke every active permit an actor holds for a given role.
183
238
  *
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Self-service role grant/revoke action specs — schemas, error reasons,
3
+ * and the codegen-ready registry.
4
+ *
5
+ * Client-safe: no query-layer or audit-write imports. Handler factory
6
+ * lives in `self_service_role_actions.ts`.
7
+ *
8
+ * @module
9
+ */
10
+ import { z } from 'zod';
11
+ import type { RequestResponseActionSpec } from '../actions/action_spec.js';
12
+ /** Error reason — caller asked to self-toggle a role outside the configured allowlist. */
13
+ export declare const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE: "role_not_self_service_eligible";
14
+ /** Input for `self_service_role_grant`. */
15
+ export declare const SelfServiceRoleGrantInput: z.ZodObject<{
16
+ role: z.ZodString;
17
+ }, z.core.$strict>;
18
+ export type SelfServiceRoleGrantInput = z.infer<typeof SelfServiceRoleGrantInput>;
19
+ /**
20
+ * Output for `self_service_role_grant`. `granted` is `false` on idempotent
21
+ * re-grant (caller already held the role globally); `permit_id` is set on
22
+ * new grants only.
23
+ */
24
+ export declare const SelfServiceRoleGrantOutput: z.ZodObject<{
25
+ ok: z.ZodLiteral<true>;
26
+ granted: z.ZodBoolean;
27
+ permit_id: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
28
+ }, z.core.$strict>;
29
+ export type SelfServiceRoleGrantOutput = z.infer<typeof SelfServiceRoleGrantOutput>;
30
+ /** Input for `self_service_role_revoke`. */
31
+ export declare const SelfServiceRoleRevokeInput: z.ZodObject<{
32
+ role: z.ZodString;
33
+ }, z.core.$strict>;
34
+ export type SelfServiceRoleRevokeInput = z.infer<typeof SelfServiceRoleRevokeInput>;
35
+ /**
36
+ * Output for `self_service_role_revoke`. `revoked` is `false` when the
37
+ * caller held no active global permit for the role (idempotent).
38
+ */
39
+ export declare const SelfServiceRoleRevokeOutput: z.ZodObject<{
40
+ ok: z.ZodLiteral<true>;
41
+ revoked: z.ZodBoolean;
42
+ }, z.core.$strict>;
43
+ export type SelfServiceRoleRevokeOutput = z.infer<typeof SelfServiceRoleRevokeOutput>;
44
+ export declare const self_service_role_grant_action_spec: {
45
+ method: string;
46
+ kind: "request_response";
47
+ initiator: "frontend";
48
+ auth: "authenticated";
49
+ side_effects: true;
50
+ input: z.ZodObject<{
51
+ role: z.ZodString;
52
+ }, z.core.$strict>;
53
+ output: z.ZodObject<{
54
+ ok: z.ZodLiteral<true>;
55
+ granted: z.ZodBoolean;
56
+ permit_id: z.ZodOptional<z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">>;
57
+ }, z.core.$strict>;
58
+ async: true;
59
+ description: string;
60
+ };
61
+ export declare const self_service_role_revoke_action_spec: {
62
+ method: string;
63
+ kind: "request_response";
64
+ initiator: "frontend";
65
+ auth: "authenticated";
66
+ side_effects: true;
67
+ input: z.ZodObject<{
68
+ role: z.ZodString;
69
+ }, z.core.$strict>;
70
+ output: z.ZodObject<{
71
+ ok: z.ZodLiteral<true>;
72
+ revoked: z.ZodBoolean;
73
+ }, z.core.$strict>;
74
+ async: true;
75
+ description: string;
76
+ };
77
+ /**
78
+ * All self-service role action specs — a codegen-ready registry. Method
79
+ * names are static, so consumer typed-client codegen picks them up the
80
+ * same way it picks up `account_*_action_specs`.
81
+ */
82
+ export declare const all_self_service_role_action_specs: Array<RequestResponseActionSpec>;
83
+ //# sourceMappingURL=self_service_role_action_specs.d.ts.map
@@ -0,0 +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;AAGtB,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,2BAA2B,CAAC;AAGzE,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,EAAG,gCAAyC,CAAC;AAE9F,2CAA2C;AAC3C,eAAO,MAAM,yBAAyB;;kBAEpC,CAAC;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAElF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;kBAIrC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF,4CAA4C;AAC5C,eAAO,MAAM,0BAA0B;;kBAErC,CAAC;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,2BAA2B;;;kBAGtC,CAAC;AACH,MAAM,MAAM,2BAA2B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAEtF,eAAO,MAAM,mCAAmC;;;;;;;;;;;;;;;;CAWX,CAAC;AAEtC,eAAO,MAAM,oCAAoC;;;;;;;;;;;;;;;CAWZ,CAAC;AAEtC;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,KAAK,CAAC,yBAAyB,CAG/E,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Self-service role grant/revoke action specs — schemas, error reasons,
3
+ * and the codegen-ready registry.
4
+ *
5
+ * Client-safe: no query-layer or audit-write imports. Handler factory
6
+ * lives in `self_service_role_actions.ts`.
7
+ *
8
+ * @module
9
+ */
10
+ import { z } from 'zod';
11
+ import { Uuid } from '@fuzdev/fuz_util/id.js';
12
+ import { RoleName } from './role_schema.js';
13
+ /** Error reason — caller asked to self-toggle a role outside the configured allowlist. */
14
+ export const ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE = 'role_not_self_service_eligible';
15
+ /** Input for `self_service_role_grant`. */
16
+ export const SelfServiceRoleGrantInput = z.strictObject({
17
+ role: RoleName.meta({ description: 'Role to self-grant. Must be in the configured allowlist.' }),
18
+ });
19
+ /**
20
+ * Output for `self_service_role_grant`. `granted` is `false` on idempotent
21
+ * re-grant (caller already held the role globally); `permit_id` is set on
22
+ * new grants only.
23
+ */
24
+ export const SelfServiceRoleGrantOutput = z.strictObject({
25
+ ok: z.literal(true),
26
+ granted: z.boolean(),
27
+ permit_id: Uuid.optional(),
28
+ });
29
+ /** Input for `self_service_role_revoke`. */
30
+ export const SelfServiceRoleRevokeInput = z.strictObject({
31
+ role: RoleName.meta({ description: 'Role to self-revoke. Must be in the configured allowlist.' }),
32
+ });
33
+ /**
34
+ * Output for `self_service_role_revoke`. `revoked` is `false` when the
35
+ * caller held no active global permit for the role (idempotent).
36
+ */
37
+ export const SelfServiceRoleRevokeOutput = z.strictObject({
38
+ ok: z.literal(true),
39
+ revoked: z.boolean(),
40
+ });
41
+ export const self_service_role_grant_action_spec = {
42
+ method: 'self_service_role_grant',
43
+ kind: 'request_response',
44
+ initiator: 'frontend',
45
+ auth: 'authenticated',
46
+ side_effects: true,
47
+ input: SelfServiceRoleGrantInput,
48
+ output: SelfServiceRoleGrantOutput,
49
+ async: true,
50
+ description: 'Self-grant an active permit for an allowlisted role. Idempotent — already-granted callers receive `granted: false`.',
51
+ };
52
+ export const self_service_role_revoke_action_spec = {
53
+ method: 'self_service_role_revoke',
54
+ kind: 'request_response',
55
+ initiator: 'frontend',
56
+ auth: 'authenticated',
57
+ side_effects: true,
58
+ input: SelfServiceRoleRevokeInput,
59
+ output: SelfServiceRoleRevokeOutput,
60
+ async: true,
61
+ description: 'Self-revoke an active global permit for an allowlisted role. Idempotent — callers without an active permit receive `revoked: false`.',
62
+ };
63
+ /**
64
+ * All self-service role action specs — a codegen-ready registry. Method
65
+ * names are static, so consumer typed-client codegen picks them up the
66
+ * same way it picks up `account_*_action_specs`.
67
+ */
68
+ export const all_self_service_role_action_specs = [
69
+ self_service_role_grant_action_spec,
70
+ self_service_role_revoke_action_spec,
71
+ ];
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Self-service role grant/revoke RPC actions.
3
+ *
4
+ * Two static `request_response` actions — `self_service_role_grant` and
5
+ * `self_service_role_revoke` — that take `{role}` as input and toggle a
6
+ * permit on the caller for an allowlisted role. Idempotent in both
7
+ * directions: re-granting an already-held role returns `granted: false`;
8
+ * revoking a role the caller doesn't hold returns `revoked: false`.
9
+ *
10
+ * The factory takes an `eligible_roles` allowlist (validated against the
11
+ * supplied `roles.role_options` at factory time so typos surface at startup
12
+ * instead of at first call). Roles outside the allowlist are rejected
13
+ * with `forbidden` + reason `role_not_self_service_eligible`.
14
+ *
15
+ * Audit metadata carries `self_service: true` so admin reviewers can
16
+ * distinguish self-toggled permits from admin grants/offers. The
17
+ * `permit_grant` / `permit_revoke` metadata schemas declare
18
+ * `self_service: z.boolean().optional()` explicitly, so the field is
19
+ * part of the documented schema surface and is round-trip-validated by
20
+ * `query_audit_log`.
21
+ *
22
+ * Static method names — `role` lives in the input, not the method name —
23
+ * so specs are codegen-compatible (`satisfies RequestResponseActionSpec`)
24
+ * and the surface stays constant as consumers add eligible roles. Mirrors
25
+ * the existing `permit_offer_create({role})` precedent rather than
26
+ * generating per-role methods.
27
+ *
28
+ * Specs and schemas live in `self_service_role_action_specs.ts` so
29
+ * client-side codegen can import the surface without dragging in the
30
+ * query layer.
31
+ *
32
+ * @module
33
+ */
34
+ import { type RpcAction } from '../actions/action_rpc.js';
35
+ import type { RoleSchemaResult } from './role_schema.js';
36
+ import type { RouteFactoryDeps } from './deps.js';
37
+ /** Options for `create_self_service_role_actions`. */
38
+ export interface SelfServiceRoleActionsOptions {
39
+ /**
40
+ * Allowlist of role strings eligible for self-service. Empty array
41
+ * effectively disables the surface — every call comes back as
42
+ * `forbidden` with reason `role_not_self_service_eligible`.
43
+ */
44
+ eligible_roles: ReadonlyArray<string>;
45
+ /**
46
+ * Optional role schema. When supplied, `eligible_roles` entries are
47
+ * checked against `roles.role_options` at factory time so typos throw
48
+ * at startup instead of at first call.
49
+ */
50
+ roles?: RoleSchemaResult;
51
+ }
52
+ /**
53
+ * Dependencies for `create_self_service_role_actions`. Same shape as the
54
+ * peer factories so consumers thread one deps object through all three.
55
+ * `audit_log_config` flows from `AppDeps` and is consumed by
56
+ * `audit_log_fire_and_forget`.
57
+ */
58
+ export type SelfServiceRoleActionDeps = Pick<RouteFactoryDeps, 'log' | 'on_audit_event' | 'audit_log_config'>;
59
+ /**
60
+ * Build the self-service role grant/revoke RPC actions.
61
+ *
62
+ * @param deps - `SelfServiceRoleActionDeps` slice of `AppDeps` (`log`, `on_audit_event`, optional `audit_log_config`)
63
+ * @param options - eligible-role allowlist plus optional role schema for typo-checking
64
+ * @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
65
+ */
66
+ export declare const create_self_service_role_actions: (deps: SelfServiceRoleActionDeps, options: SelfServiceRoleActionsOptions) => Array<RpcAction>;
67
+ //# sourceMappingURL=self_service_role_actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self_service_role_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/self_service_role_actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAiC,KAAK,SAAS,EAAC,MAAM,0BAA0B,CAAC;AAExF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,kBAAkB,CAAC;AACvD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAmBhD,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC7C;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC;;;;OAIG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAC3C,gBAAgB,EAChB,KAAK,GAAG,gBAAgB,GAAG,kBAAkB,CAC7C,CAAC;AAOF;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,GAC5C,MAAM,yBAAyB,EAC/B,SAAS,6BAA6B,KACpC,KAAK,CAAC,SAAS,CAqHjB,CAAC"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Self-service role grant/revoke RPC actions.
3
+ *
4
+ * Two static `request_response` actions — `self_service_role_grant` and
5
+ * `self_service_role_revoke` — that take `{role}` as input and toggle a
6
+ * permit on the caller for an allowlisted role. Idempotent in both
7
+ * directions: re-granting an already-held role returns `granted: false`;
8
+ * revoking a role the caller doesn't hold returns `revoked: false`.
9
+ *
10
+ * The factory takes an `eligible_roles` allowlist (validated against the
11
+ * supplied `roles.role_options` at factory time so typos surface at startup
12
+ * instead of at first call). Roles outside the allowlist are rejected
13
+ * with `forbidden` + reason `role_not_self_service_eligible`.
14
+ *
15
+ * Audit metadata carries `self_service: true` so admin reviewers can
16
+ * distinguish self-toggled permits from admin grants/offers. The
17
+ * `permit_grant` / `permit_revoke` metadata schemas declare
18
+ * `self_service: z.boolean().optional()` explicitly, so the field is
19
+ * part of the documented schema surface and is round-trip-validated by
20
+ * `query_audit_log`.
21
+ *
22
+ * Static method names — `role` lives in the input, not the method name —
23
+ * so specs are codegen-compatible (`satisfies RequestResponseActionSpec`)
24
+ * and the surface stays constant as consumers add eligible roles. Mirrors
25
+ * the existing `permit_offer_create({role})` precedent rather than
26
+ * generating per-role methods.
27
+ *
28
+ * Specs and schemas live in `self_service_role_action_specs.ts` so
29
+ * client-side codegen can import the surface without dragging in the
30
+ * query layer.
31
+ *
32
+ * @module
33
+ */
34
+ import { rpc_action } from '../actions/action_rpc.js';
35
+ import { jsonrpc_errors } from '../http/jsonrpc_errors.js';
36
+ import { query_grant_permit, query_permit_find_active_for_actor, query_permit_has_role, query_revoke_permit, } from './permit_queries.js';
37
+ import { audit_log_fire_and_forget } from './audit_log_queries.js';
38
+ import { ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE, self_service_role_grant_action_spec, self_service_role_revoke_action_spec, } from './self_service_role_action_specs.js';
39
+ const require_request_auth = (auth) => {
40
+ if (!auth)
41
+ throw new Error('unreachable: action auth guard did not enforce authentication');
42
+ return auth;
43
+ };
44
+ /**
45
+ * Build the self-service role grant/revoke RPC actions.
46
+ *
47
+ * @param deps - `SelfServiceRoleActionDeps` slice of `AppDeps` (`log`, `on_audit_event`, optional `audit_log_config`)
48
+ * @param options - eligible-role allowlist plus optional role schema for typo-checking
49
+ * @returns the `RpcAction` array to spread into a `create_rpc_endpoint` call
50
+ */
51
+ export const create_self_service_role_actions = (deps, options) => {
52
+ const eligible = new Set(options.eligible_roles);
53
+ if (options.roles) {
54
+ const role_options = options.roles.role_options;
55
+ for (const r of eligible) {
56
+ if (!role_options.has(r)) {
57
+ throw new Error(`create_self_service_role_actions: eligible_roles entry "${r}" is not registered in roles.role_options — typo or missing call to create_role_schema`);
58
+ }
59
+ }
60
+ }
61
+ const reject_if_ineligible = (role) => {
62
+ if (!eligible.has(role)) {
63
+ throw jsonrpc_errors.forbidden('role not eligible for self-service', {
64
+ reason: ERROR_ROLE_NOT_SELF_SERVICE_ELIGIBLE,
65
+ });
66
+ }
67
+ };
68
+ const grant_handler = async (input, ctx) => {
69
+ const auth = require_request_auth(ctx.auth);
70
+ reject_if_ineligible(input.role);
71
+ // Pre-check for idempotent re-grant. `query_grant_permit` is itself
72
+ // idempotent (returns the existing permit instead of inserting), but
73
+ // it doesn't signal "already existed" vs "newly inserted" — so we
74
+ // peek first. The TOCTOU window is benign for self-service: two
75
+ // concurrent grants both observe "no permit", both call
76
+ // `query_grant_permit`, and one collapses onto the other inside the
77
+ // query's `ON CONFLICT DO NOTHING`. Worst case both responses report
78
+ // `granted: true`; the DB still ends up with exactly one permit.
79
+ const already = await query_permit_has_role(ctx, auth.actor.id, input.role);
80
+ if (already) {
81
+ return { ok: true, granted: false };
82
+ }
83
+ const permit = await query_grant_permit(ctx, {
84
+ actor_id: auth.actor.id,
85
+ role: input.role,
86
+ scope_id: null,
87
+ expires_at: null,
88
+ granted_by: auth.actor.id,
89
+ });
90
+ void audit_log_fire_and_forget(ctx, {
91
+ event_type: 'permit_grant',
92
+ actor_id: auth.actor.id,
93
+ account_id: auth.account.id,
94
+ ip: ctx.client_ip,
95
+ metadata: {
96
+ role: permit.role,
97
+ permit_id: permit.id,
98
+ scope_id: permit.scope_id,
99
+ self_service: true,
100
+ },
101
+ }, deps);
102
+ return { ok: true, granted: true, permit_id: permit.id };
103
+ };
104
+ const revoke_handler = async (input, ctx) => {
105
+ const auth = require_request_auth(ctx.auth);
106
+ reject_if_ineligible(input.role);
107
+ // Find an active global permit for this (actor, role). No dedicated
108
+ // query exists, but `query_permit_find_active_for_actor` returns the
109
+ // short list of every active permit and we filter in JS — fewer
110
+ // round-trips than a new helper for a one-call-per-revoke path.
111
+ const active = await query_permit_find_active_for_actor(ctx, auth.actor.id);
112
+ const target = active.find((p) => p.role === input.role && p.scope_id === null);
113
+ if (!target) {
114
+ return { ok: true, revoked: false };
115
+ }
116
+ const result = await query_revoke_permit(ctx, target.id, auth.actor.id, auth.actor.id);
117
+ if (!result) {
118
+ // Raced with another revoker — treat as already revoked.
119
+ return { ok: true, revoked: false };
120
+ }
121
+ void audit_log_fire_and_forget(ctx, {
122
+ event_type: 'permit_revoke',
123
+ actor_id: auth.actor.id,
124
+ account_id: auth.account.id,
125
+ ip: ctx.client_ip,
126
+ metadata: {
127
+ role: result.role,
128
+ permit_id: result.id,
129
+ scope_id: result.scope_id,
130
+ self_service: true,
131
+ },
132
+ }, deps);
133
+ return { ok: true, revoked: true };
134
+ };
135
+ return [
136
+ rpc_action(self_service_role_grant_action_spec, grant_handler),
137
+ rpc_action(self_service_role_revoke_action_spec, revoke_handler),
138
+ ];
139
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAStB,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAQhD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE,6FAA6F;IAC7F,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,yFAAyF;IACzF,YAAY,EAAE,WAAW,CAAC;CAC1B;AAID,0FAA0F;AAC1F,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,8EAA8E;AAC9E,eAAO,MAAM,YAAY;;kBAEvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,gBAAgB,EACtB,SAAS,kBAAkB,KACzB,KAAK,CAAC,SAAS,CA4HjB,CAAC"}
1
+ {"version":3,"file":"signup_routes.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/auth/signup_routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAStB,OAAO,EAAkB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAEtE,OAAO,EAA+B,KAAK,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAClF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,WAAW,CAAC;AAQhD,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,0BAA0B,CAAC;AAE1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,qBAAqB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,kBAAmB,SAAQ,uBAAuB;IAClE,6FAA6F;IAC7F,2BAA2B,EAAE,WAAW,GAAG,IAAI,CAAC;IAChD,yFAAyF;IACzF,YAAY,EAAE,WAAW,CAAC;CAC1B;AAID,0FAA0F;AAC1F,eAAO,MAAM,WAAW;;;;kBAItB,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD,8EAA8E;AAC9E,eAAO,MAAM,YAAY;;kBAEvB,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAExD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACrC,MAAM,gBAAgB,EACtB,SAAS,kBAAkB,KACzB,KAAK,CAAC,SAAS,CA2HjB,CAAC"}