@happyvertical/smrt-users 0.51.39 → 0.53.3

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 (33) hide show
  1. package/README.md +42 -0
  2. package/agents/permissions.md +12 -1
  3. package/dist/chunks/{TerminalAuthService-BDKUbg4N.js → TerminalAuthService-DoadwiqV.js} +121 -53
  4. package/dist/chunks/TerminalAuthService-DoadwiqV.js.map +1 -0
  5. package/dist/collections/RoleCollection.d.ts.map +1 -1
  6. package/dist/collections/RolePermissionCollection.d.ts +4 -0
  7. package/dist/collections/RolePermissionCollection.d.ts.map +1 -1
  8. package/dist/collections/seeding-transaction.d.ts +43 -0
  9. package/dist/collections/seeding-transaction.d.ts.map +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +40 -5
  13. package/dist/index.js.map +1 -1
  14. package/dist/manifest.json +97 -101
  15. package/dist/migrations/deduplicateRolePermissions.d.ts +23 -0
  16. package/dist/migrations/deduplicateRolePermissions.d.ts.map +1 -0
  17. package/dist/models/RolePermission.d.ts.map +1 -1
  18. package/dist/services/PermissionCatalogService.d.ts.map +1 -1
  19. package/dist/smrt-knowledge.json +40 -6
  20. package/dist/sveltekit.js +1 -1
  21. package/package.json +14 -14
  22. package/dist/__tests__/helpers/oidc-test-server.d.ts +0 -45
  23. package/dist/__tests__/helpers/oidc-test-server.d.ts.map +0 -1
  24. package/dist/__tests__/helpers/postgres-rls-reset.d.ts +0 -15
  25. package/dist/__tests__/helpers/postgres-rls-reset.d.ts.map +0 -1
  26. package/dist/chunks/TerminalAuthService-BDKUbg4N.js.map +0 -1
  27. package/dist/svelte/components/__tests__/InviteUserModal.test.js +0 -65
  28. package/dist/svelte/components/__tests__/UserAvatar.test.js +0 -31
  29. package/dist/svelte/components/__tests__/UserCard.test.js +0 -39
  30. package/dist/svelte/components/__tests__/UserForm.test.js +0 -50
  31. package/dist/svelte/components/__tests__/UserList.test.js +0 -48
  32. package/dist/svelte/components/__tests__/UserMenu.test.js +0 -83
  33. package/dist/svelte/components/__tests__/UserStatus.test.js +0 -36
package/README.md CHANGED
@@ -113,6 +113,8 @@ Catalog sync is additive and fail-closed:
113
113
  - it updates `name`, `description`, and `category` by slug
114
114
  - it does not auto-grant permissions to roles
115
115
  - it does not delete stale permissions in v1
116
+ - it commits created/updated rows in small batched transactions; when calling
117
+ it inside your own transaction, pass that transaction's database handle
116
118
 
117
119
  ### App-defined permissions in `smrt.config.ts`
118
120
 
@@ -918,3 +920,43 @@ for authentication, deduplication, and status semantics.
918
920
  ## License
919
921
 
920
922
  MIT
923
+
924
+ ### Upgrade duplicate role grants (#3329)
925
+
926
+ `RolePermission` now has a unique natural key `(role_id, permission_id)`.
927
+ Concurrent seeders converge through core's conflict-tolerant writes, preserving
928
+ one grant ID per pair. Permissions already use `(slug, context)` and system
929
+ roles use the tenant-aware natural key. System-role bootstrap holds a
930
+ PostgreSQL transaction advisory lock across its read/create sequence so two
931
+ seeders also return the same global (`tenant_id = NULL`) role IDs. The concurrent cold-seed test
932
+ checks all three catalogs. Seeding still uses bounded batches and is additive
933
+ unless `prune: true`; concurrent passes must use the same catalog/matrix.
934
+
935
+ Existing deployments need a maintenance window **before ordinary schema
936
+ migration**: adding the new unique index directly fails if duplicates exist.
937
+ Back up the database, stop every application writer and bootstrap/seed process,
938
+ and run the following once with the new package from an operator process:
939
+
940
+ ```typescript
941
+ import { getDatabase } from '@happyvertical/sql';
942
+ import { deduplicateRolePermissions } from '@happyvertical/smrt-users';
943
+
944
+ const db = await getDatabase(databaseConfig);
945
+ console.log(await deduplicateRolePermissions(db, { dryRun: true }));
946
+ console.log(await deduplicateRolePermissions(db, {
947
+ maintenanceConfirmed: true,
948
+ }));
949
+ ```
950
+
951
+ The migration keeps the earliest `created_at` per pair (lowest `id` breaks ties;
952
+ null timestamps sort last), deletes only extra grants, and creates
953
+ `role_permissions_role_id_permission_id_idx` in the same transaction. Other
954
+ pairs and surviving grant data remain intact. Failure rolls the transaction
955
+ back; after resolving its cause, rerun the migration. Dry runs change nothing.
956
+ The helper requires transaction support and supports SQLite, DuckDB and
957
+ PostgreSQL; PostgreSQL locks the grant table while repairing it. Writer shutdown
958
+ is still required because older application versions cannot seed safely
959
+ against the new constraint. Apply the remaining application schema migrations,
960
+ run `smrt doctor --db` / `db:status --parity`, deploy the new version to every
961
+ writer, then resume traffic. Do not roll back application writers without also
962
+ restoring the pre-upgrade schema/database backup.
@@ -93,7 +93,11 @@ membership or tenant DENY to attenuate inherited role authority.
93
93
  seedSystemRoles({ seedPermissions: true }) maps owner/admin to all catalog
94
94
  permissions, member to ordinary-resource read/create, viewer to read. Member
95
95
  create excludes identity/RBAC/security resources and their joins/overrides.
96
- Seeding is additive/idempotent; removal requires prune: true.
96
+ Seeding is additive/idempotent; removal requires prune: true. Catalog sync
97
+ and grants plan from one read, then commit in `SEEDING_BATCH_SIZE` batches
98
+ (#3323): never one durable commit per row (an fsync each on local SQLite),
99
+ and never one catalog-wide transaction (`@happyvertical/sql` transaction
100
+ scopes cost superlinearly per statement). Pass a caller transaction's handle.
97
101
  - When a package adds built-in self-personalization permissions after role
98
102
  creation, explicitly call seedDefaultRolePersonalizationPermissions(). It
99
103
  upgrades owner/admin/member/viewer idempotently and never grants custom roles.
@@ -184,3 +188,10 @@ access once principal context is set. Child-tenant sessions receive resolved
184
188
  inherited permissions automatically, but RLS row filtering remains bound to the
185
189
  session tenant: a root session's app-level guard authorization does not itself
186
190
  permit child rows through RLS. Preserve this distinction.
191
+
192
+ RolePermission's natural key is `(role_id, permission_id)` (#3329); repeated or
193
+ concurrent inserts converge without changing the grant ID. Existing deployments
194
+ must run `deduplicateRolePermissions()` with stopped writers before ordinary
195
+ schema migration; see README's "Upgrade duplicate role grants". It keeps the
196
+ oldest row and installs uniqueness atomically. Runtime seeding never performs
197
+ this schema repair.