import type { ScopeService } from '@pikku/core/services'; import type { Role } from '@pikku/core/services'; import type { FlatScope } from '@pikku/core/scope'; import type { SystemRole } from '@pikku/core/role'; import type { Kysely } from 'kysely'; import type { KyselyPikkuDB } from './kysely-tables.js'; /** * Resolves and administers user scopes against four self-created tables. * * Requires better-auth: `pikku_user_role.user_id` references its `user` table * with ON DELETE CASCADE, so deleting a user takes their grants with it. That * table is created by better-auth's own migrations, which `pikku db migrate` * hard-fails without, so it exists before `init()` runs. * * Scopes are declared in code and synced here; roles are data, composed by * admins at runtime. `pikku_role_scopes` FKs into `pikku_scopes`, so the * database itself refuses to grant a scope that was never declared. */ export declare class KyselyScopeService implements ScopeService { private db; private initialized; constructor(db: Kysely); init(): Promise; /** * Registers the declared scope set. * * Additive: rows are upserted and anything no longer declared is *marked* * (`declared = false`), never deleted. Marking is non-destructive, so a * rename, a rollback, or a rolling deploy where an older replica is still * serving cannot silently strip a grant. `pruneScopes` is the deliberate * removal path. */ syncScopes(scopes: FlatScope[]): Promise; resolveScopes(userId: string): Promise; listScopes(): Promise>; /** * Registers the roles declared with `defineSystemRole`. * * Additive on the same terms as `syncScopes` — a role whose declaration has * gone is marked `declared = false` rather than deleted, so a rollback or a * rolling deploy cannot strip everyone's grant. Its *scope set* is replaced * outright, because that is the declaration's entire content: editing * `defineSystemRole` is how you change what a role means, and the deploy is * when it takes effect. */ syncSystemRoles(roles: SystemRole[]): Promise; createRole(role: Role): Promise; deleteRole(name: string): Promise; setRoleScopes(name: string, scopes: string[]): Promise; /** * Whether the store holds this name as a system role. * * Asked of the store rather than of the generated `SYSTEM_ROLES`, because the * store is where a shadow would actually collide — and because a role whose * declaration was deleted is still immutable until someone prunes it. */ private isSystemRole; listRoles(): Promise; addUserToRole(userId: string, role: string, grantedBy?: string): Promise; removeUserFromRole(userId: string, role: string): Promise; listUserRoles(userId: string): Promise; addScopeToUser(userId: string, scope: string, grantedBy?: string): Promise; removeScopeFromUser(userId: string, scope: string): Promise; listUserScopes(userId: string): Promise; /** * Scopes marked undeclared by the last sync, with the roles that would lose * them. Powers `pikku scopes audit`. */ findStaleScopes(): Promise>; /** * Removes undeclared scopes, cascading them out of every role that holds * them. This revokes access, so it is never run implicitly. */ pruneScopes(): Promise; /** * System roles the last sync found no declaration for, with how many people * still hold each. Powers `pikku roles audit`. * * The count is the whole point: "this role is gone from the code" and "this * role is gone from the code and 40 people are standing on it" call for * different decisions, and the number is what makes pruning a choice rather * than a formality. */ findStaleSystemRoles(): Promise>; /** * Removes undeclared system roles, cascading them out of every user grant * that holds them. This revokes access, so it is never run implicitly. */ pruneSystemRoles(): Promise; }