@happyvertical/smrt-users 0.30.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 (150) hide show
  1. package/AGENTS.md +85 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +459 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/chunks/TerminalAuthService-DoAMQ_yn.js +5118 -0
  8. package/dist/chunks/TerminalAuthService-DoAMQ_yn.js.map +1 -0
  9. package/dist/chunks/index-DkoYIvIu.js +169 -0
  10. package/dist/chunks/index-DkoYIvIu.js.map +1 -0
  11. package/dist/collections/CliAuthRequestCollection.d.ts +19 -0
  12. package/dist/collections/CliAuthRequestCollection.d.ts.map +1 -0
  13. package/dist/collections/GroupCollection.d.ts +17 -0
  14. package/dist/collections/GroupCollection.d.ts.map +1 -0
  15. package/dist/collections/GroupMemberCollection.d.ts +43 -0
  16. package/dist/collections/GroupMemberCollection.d.ts.map +1 -0
  17. package/dist/collections/GroupRoleCollection.d.ts +33 -0
  18. package/dist/collections/GroupRoleCollection.d.ts.map +1 -0
  19. package/dist/collections/MagicLinkTokenCollection.d.ts +26 -0
  20. package/dist/collections/MagicLinkTokenCollection.d.ts.map +1 -0
  21. package/dist/collections/MembershipCollection.d.ts +38 -0
  22. package/dist/collections/MembershipCollection.d.ts.map +1 -0
  23. package/dist/collections/MembershipOverrideCollection.d.ts +55 -0
  24. package/dist/collections/MembershipOverrideCollection.d.ts.map +1 -0
  25. package/dist/collections/PermissionCollection.d.ts +34 -0
  26. package/dist/collections/PermissionCollection.d.ts.map +1 -0
  27. package/dist/collections/RoleCollection.d.ts +29 -0
  28. package/dist/collections/RoleCollection.d.ts.map +1 -0
  29. package/dist/collections/RolePermissionCollection.d.ts +33 -0
  30. package/dist/collections/RolePermissionCollection.d.ts.map +1 -0
  31. package/dist/collections/SessionCollection.d.ts +82 -0
  32. package/dist/collections/SessionCollection.d.ts.map +1 -0
  33. package/dist/collections/TenantCollection.d.ts +119 -0
  34. package/dist/collections/TenantCollection.d.ts.map +1 -0
  35. package/dist/collections/TenantPermissionOverrideCollection.d.ts +111 -0
  36. package/dist/collections/TenantPermissionOverrideCollection.d.ts.map +1 -0
  37. package/dist/collections/UserCollection.d.ts +116 -0
  38. package/dist/collections/UserCollection.d.ts.map +1 -0
  39. package/dist/collections/index.d.ts +19 -0
  40. package/dist/collections/index.d.ts.map +1 -0
  41. package/dist/index.d.ts +5 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +1482 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/manifest.json +5216 -0
  46. package/dist/models/CliAuthRequest.d.ts +25 -0
  47. package/dist/models/CliAuthRequest.d.ts.map +1 -0
  48. package/dist/models/Group.d.ts +34 -0
  49. package/dist/models/Group.d.ts.map +1 -0
  50. package/dist/models/GroupMember.d.ts +29 -0
  51. package/dist/models/GroupMember.d.ts.map +1 -0
  52. package/dist/models/GroupRole.d.ts +29 -0
  53. package/dist/models/GroupRole.d.ts.map +1 -0
  54. package/dist/models/MagicLinkToken.d.ts +22 -0
  55. package/dist/models/MagicLinkToken.d.ts.map +1 -0
  56. package/dist/models/Membership.d.ts +48 -0
  57. package/dist/models/Membership.d.ts.map +1 -0
  58. package/dist/models/MembershipOverride.d.ts +50 -0
  59. package/dist/models/MembershipOverride.d.ts.map +1 -0
  60. package/dist/models/Permission.d.ts +79 -0
  61. package/dist/models/Permission.d.ts.map +1 -0
  62. package/dist/models/Role.d.ts +67 -0
  63. package/dist/models/Role.d.ts.map +1 -0
  64. package/dist/models/RolePermission.d.ts +29 -0
  65. package/dist/models/RolePermission.d.ts.map +1 -0
  66. package/dist/models/Session.d.ts +105 -0
  67. package/dist/models/Session.d.ts.map +1 -0
  68. package/dist/models/Tenant.d.ts +138 -0
  69. package/dist/models/Tenant.d.ts.map +1 -0
  70. package/dist/models/TenantPermissionOverride.d.ts +74 -0
  71. package/dist/models/TenantPermissionOverride.d.ts.map +1 -0
  72. package/dist/models/User.d.ts +72 -0
  73. package/dist/models/User.d.ts.map +1 -0
  74. package/dist/models/index.d.ts +19 -0
  75. package/dist/models/index.d.ts.map +1 -0
  76. package/dist/playground.d.ts +2 -0
  77. package/dist/playground.d.ts.map +1 -0
  78. package/dist/playground.js +139 -0
  79. package/dist/playground.js.map +1 -0
  80. package/dist/services/MagicLinkService.d.ts +84 -0
  81. package/dist/services/MagicLinkService.d.ts.map +1 -0
  82. package/dist/services/OidcLoginService.d.ts +134 -0
  83. package/dist/services/OidcLoginService.d.ts.map +1 -0
  84. package/dist/services/PermissionCatalogService.d.ts +62 -0
  85. package/dist/services/PermissionCatalogService.d.ts.map +1 -0
  86. package/dist/services/PermissionResolver.d.ts +150 -0
  87. package/dist/services/PermissionResolver.d.ts.map +1 -0
  88. package/dist/services/PostgresPermissionPolicies.d.ts +29 -0
  89. package/dist/services/PostgresPermissionPolicies.d.ts.map +1 -0
  90. package/dist/services/SessionPermissionContext.d.ts +43 -0
  91. package/dist/services/SessionPermissionContext.d.ts.map +1 -0
  92. package/dist/services/SessionService.d.ts +139 -0
  93. package/dist/services/SessionService.d.ts.map +1 -0
  94. package/dist/services/TenantService.d.ts +135 -0
  95. package/dist/services/TenantService.d.ts.map +1 -0
  96. package/dist/services/TerminalAuthService.d.ts +189 -0
  97. package/dist/services/TerminalAuthService.d.ts.map +1 -0
  98. package/dist/services/index.d.ts +14 -0
  99. package/dist/services/index.d.ts.map +1 -0
  100. package/dist/smrt-knowledge.json +2744 -0
  101. package/dist/svelte/components/InviteUserModal.svelte +351 -0
  102. package/dist/svelte/components/InviteUserModal.svelte.d.ts +17 -0
  103. package/dist/svelte/components/InviteUserModal.svelte.d.ts.map +1 -0
  104. package/dist/svelte/components/UserAvatar.svelte +105 -0
  105. package/dist/svelte/components/UserAvatar.svelte.d.ts +10 -0
  106. package/dist/svelte/components/UserAvatar.svelte.d.ts.map +1 -0
  107. package/dist/svelte/components/UserCard.svelte +179 -0
  108. package/dist/svelte/components/UserCard.svelte.d.ts +18 -0
  109. package/dist/svelte/components/UserCard.svelte.d.ts.map +1 -0
  110. package/dist/svelte/components/UserForm.svelte +194 -0
  111. package/dist/svelte/components/UserForm.svelte.d.ts +18 -0
  112. package/dist/svelte/components/UserForm.svelte.d.ts.map +1 -0
  113. package/dist/svelte/components/UserList.svelte +107 -0
  114. package/dist/svelte/components/UserList.svelte.d.ts +20 -0
  115. package/dist/svelte/components/UserList.svelte.d.ts.map +1 -0
  116. package/dist/svelte/components/UserMenu.svelte +326 -0
  117. package/dist/svelte/components/UserMenu.svelte.d.ts +33 -0
  118. package/dist/svelte/components/UserMenu.svelte.d.ts.map +1 -0
  119. package/dist/svelte/components/__tests__/InviteUserModal.test.js +54 -0
  120. package/dist/svelte/components/__tests__/UserAvatar.test.js +31 -0
  121. package/dist/svelte/components/__tests__/UserCard.test.js +39 -0
  122. package/dist/svelte/components/__tests__/UserForm.test.js +50 -0
  123. package/dist/svelte/components/__tests__/UserList.test.js +48 -0
  124. package/dist/svelte/components/__tests__/UserMenu.test.js +38 -0
  125. package/dist/svelte/i18n.d.ts +15 -0
  126. package/dist/svelte/i18n.d.ts.map +1 -0
  127. package/dist/svelte/i18n.js +15 -0
  128. package/dist/svelte/index.d.ts +23 -0
  129. package/dist/svelte/index.d.ts.map +1 -0
  130. package/dist/svelte/index.js +27 -0
  131. package/dist/svelte/playground.d.ts +151 -0
  132. package/dist/svelte/playground.d.ts.map +1 -0
  133. package/dist/svelte/playground.js +134 -0
  134. package/dist/sveltekit/index.d.ts +379 -0
  135. package/dist/sveltekit/index.d.ts.map +1 -0
  136. package/dist/sveltekit/resource-list-handler.d.ts +127 -0
  137. package/dist/sveltekit/resource-list-handler.d.ts.map +1 -0
  138. package/dist/sveltekit/types.d.ts +31 -0
  139. package/dist/sveltekit/types.d.ts.map +1 -0
  140. package/dist/sveltekit.d.ts +2 -0
  141. package/dist/sveltekit.d.ts.map +1 -0
  142. package/dist/sveltekit.js +978 -0
  143. package/dist/sveltekit.js.map +1 -0
  144. package/dist/types/index.d.ts +61 -0
  145. package/dist/types/index.d.ts.map +1 -0
  146. package/dist/ui.d.ts +10 -0
  147. package/dist/ui.d.ts.map +1 -0
  148. package/dist/ui.js +75 -0
  149. package/dist/ui.js.map +1 -0
  150. package/package.json +97 -0
@@ -0,0 +1,134 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { OidcClaims, OidcIdentityResult } from '../collections/UserCollection.js';
3
+ export type OidcProviderKind = 'kanidm' | 'dex' | 'generic';
4
+ export type OidcTokenEndpointAuthMethod = 'client_secret_basic' | 'client_secret_post' | 'none';
5
+ export interface OidcProviderConfig {
6
+ /**
7
+ * Optional preset label for documentation and UI.
8
+ *
9
+ * Kanidm and Dex are standards-compliant OIDC providers, so the preset does
10
+ * not change protocol behavior; it lets apps name their intent clearly.
11
+ */
12
+ kind?: OidcProviderKind;
13
+ /** OIDC issuer URL, e.g. https://id.example.com/dex */
14
+ issuer: string;
15
+ /** OAuth2/OIDC client ID registered with the provider */
16
+ clientId: string;
17
+ /** OAuth2/OIDC client secret for confidential clients */
18
+ clientSecret?: string;
19
+ /**
20
+ * Redirect URI registered with the provider.
21
+ *
22
+ * SvelteKit helpers can derive this from the current request when omitted.
23
+ */
24
+ redirectUri?: string;
25
+ /** Scopes to request. Defaults to openid profile email. */
26
+ scopes?: string[];
27
+ /** Override the discovery document URL when needed. */
28
+ discoveryUrl?: string;
29
+ /** Additional authorization request parameters. */
30
+ authorizationParams?: Record<string, string | number | boolean | undefined>;
31
+ /**
32
+ * Client authentication method for the token endpoint.
33
+ *
34
+ * Defaults to client_secret_basic when clientSecret exists, otherwise none.
35
+ */
36
+ tokenEndpointAuthMethod?: OidcTokenEndpointAuthMethod;
37
+ }
38
+ export interface OidcProviderResolutionOptions {
39
+ defaultProvider?: string;
40
+ providers?: Record<string, OidcProviderConfig>;
41
+ }
42
+ export interface UsersOidcConfig extends OidcProviderResolutionOptions {
43
+ /** Seconds before login transaction cookies expire. */
44
+ transactionTtl?: number;
45
+ }
46
+ export interface OidcProviderResolution {
47
+ provider: OidcProviderConfig;
48
+ providerName: string;
49
+ }
50
+ export interface ResolvedOidcProviderConfig extends OidcProviderConfig {
51
+ redirectUri: string;
52
+ }
53
+ export interface OidcProviderMetadata {
54
+ authorization_endpoint: string;
55
+ end_session_endpoint?: string;
56
+ issuer: string;
57
+ jwks_uri: string;
58
+ token_endpoint: string;
59
+ userinfo_endpoint?: string;
60
+ [key: string]: unknown;
61
+ }
62
+ export interface OidcTransaction {
63
+ codeVerifier: string;
64
+ createdAt: number;
65
+ nonce: string;
66
+ provider: string;
67
+ returnTo?: string;
68
+ state: string;
69
+ }
70
+ export interface CreateAuthorizationUrlOptions {
71
+ authorizationParams?: Record<string, string | number | boolean | undefined>;
72
+ transaction?: OidcTransaction;
73
+ }
74
+ export interface OidcTokenSet {
75
+ accessToken?: string;
76
+ expiresAt?: Date;
77
+ idToken: string;
78
+ refreshToken?: string;
79
+ scope?: string;
80
+ tokenType?: string;
81
+ }
82
+ export interface OidcCallbackResult {
83
+ claims: OidcClaims;
84
+ tokens: OidcTokenSet;
85
+ }
86
+ export interface OidcLoginResult extends OidcIdentityResult {
87
+ claims: OidcClaims;
88
+ tokens: OidcTokenSet;
89
+ }
90
+ export interface OidcLoginServiceOptions extends SmrtClassOptions {
91
+ /** Provider key, e.g. kanidm or dex */
92
+ providerName: string;
93
+ /** Fully resolved provider config. redirectUri is required here. */
94
+ provider: ResolvedOidcProviderConfig;
95
+ /** Optional fetch override for tests or custom runtimes. */
96
+ fetch?: typeof fetch;
97
+ /** JWT clock tolerance passed to jose. Defaults to 15 seconds. */
98
+ clockTolerance?: number | string;
99
+ /** Provider metadata cache TTL in milliseconds. Defaults to 5 minutes. */
100
+ metadataCacheTtlMs?: number;
101
+ }
102
+ export declare class OidcLoginError extends Error {
103
+ constructor(message: string);
104
+ }
105
+ export declare function getUsersOidcConfig(): UsersOidcConfig;
106
+ export declare function encodeOidcTransaction(transaction: OidcTransaction): string;
107
+ export declare function decodeOidcTransaction(value: string): OidcTransaction;
108
+ export declare function resolveOidcProviderConfig(providerName?: string, options?: OidcProviderResolutionOptions): OidcProviderResolution;
109
+ export declare class OidcLoginService {
110
+ private readonly classOptions;
111
+ private readonly clockTolerance;
112
+ private readonly fetchImpl;
113
+ private readonly metadataCacheTtlMs;
114
+ private metadataFetchedAt;
115
+ private metadataPromise?;
116
+ private remoteJwks?;
117
+ readonly provider: ResolvedOidcProviderConfig;
118
+ readonly providerName: string;
119
+ constructor(options: OidcLoginServiceOptions);
120
+ createTransaction(returnTo?: string): OidcTransaction;
121
+ getMetadata(): Promise<OidcProviderMetadata>;
122
+ private fetchMetadata;
123
+ createAuthorizationUrl(options?: CreateAuthorizationUrlOptions): Promise<{
124
+ transaction: OidcTransaction;
125
+ url: URL;
126
+ }>;
127
+ exchangeCallback(callbackUrl: string | URL, transaction: OidcTransaction): Promise<OidcCallbackResult>;
128
+ completeLogin(callbackUrl: string | URL, transaction: OidcTransaction): Promise<OidcLoginResult>;
129
+ private validateCallback;
130
+ private exchangeCode;
131
+ private enrichClaimsFromUserInfo;
132
+ private verifyIdToken;
133
+ }
134
+ //# sourceMappingURL=OidcLoginService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OidcLoginService.d.ts","sourceRoot":"","sources":["../../src/services/OidcLoginService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ export type PermissionCatalogSource = 'manifest' | 'config' | 'runtime';
3
+ export type PostgresPermissionAction = 'SELECT' | 'INSERT' | 'UPDATE' | 'DELETE';
4
+ export interface PostgresPermissionBinding {
5
+ action: Lowercase<PostgresPermissionAction> | PostgresPermissionAction;
6
+ permission?: string;
7
+ schemaName?: string;
8
+ tableName: string;
9
+ tenantField?: string;
10
+ }
11
+ export interface PermissionDefinition {
12
+ category?: string;
13
+ className?: string;
14
+ collection?: string;
15
+ description?: string;
16
+ name?: string;
17
+ postgres?: {
18
+ bindings?: PostgresPermissionBinding[];
19
+ };
20
+ qualifiedName?: string;
21
+ slug: string;
22
+ source?: PermissionCatalogSource;
23
+ }
24
+ export interface PermissionCatalog {
25
+ customPermissions: PermissionDefinition[];
26
+ manifestPermissions: PermissionDefinition[];
27
+ permissions: PermissionDefinition[];
28
+ runtimePermissions: PermissionDefinition[];
29
+ }
30
+ export interface PermissionCatalogSyncResult {
31
+ catalog: PermissionCatalog;
32
+ created: string[];
33
+ unchanged: string[];
34
+ updated: string[];
35
+ }
36
+ export interface UsersConfig extends Record<string, unknown> {
37
+ permissions?: {
38
+ custom?: PermissionDefinition[];
39
+ postgres?: {
40
+ bindings?: PostgresPermissionBinding[];
41
+ enabled?: boolean;
42
+ };
43
+ };
44
+ }
45
+ declare global {
46
+ var __smrtUsersPermissionRegistrations: Map<number, PermissionDefinition[]> | undefined;
47
+ var __smrtUsersPermissionRegistrationCounter: number | undefined;
48
+ }
49
+ export declare function registerPermissionDefinitions(definitions: PermissionDefinition[]): () => void;
50
+ export declare class PermissionCatalogService {
51
+ private readonly options;
52
+ constructor(options?: SmrtClassOptions);
53
+ getUsersConfig(): UsersConfig;
54
+ getRuntimePermissionDefinitions(): PermissionDefinition[];
55
+ getCustomPermissionDefinitions(): PermissionDefinition[];
56
+ getCatalog(): PermissionCatalog;
57
+ syncPermissionCatalog(): Promise<PermissionCatalogSyncResult>;
58
+ private getManifestPermissionDefinitions;
59
+ static create(options?: SmrtClassOptions): PermissionCatalogService;
60
+ }
61
+ export declare function syncPermissionCatalog(options?: SmrtClassOptions): Promise<PermissionCatalogSyncResult>;
62
+ //# sourceMappingURL=PermissionCatalogService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionCatalogService.d.ts","sourceRoot":"","sources":["../../src/services/PermissionCatalogService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAIL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAOlC,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAExE,MAAM,MAAM,wBAAwB,GAChC,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,SAAS,CAAC,wBAAwB,CAAC,GAAG,wBAAwB,CAAC;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE;QACT,QAAQ,CAAC,EAAE,yBAAyB,EAAE,CAAC;KACxC,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,uBAAuB,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,oBAAoB,EAAE,CAAC;IAC1C,mBAAmB,EAAE,oBAAoB,EAAE,CAAC;IAC5C,WAAW,EAAE,oBAAoB,EAAE,CAAC;IACpC,kBAAkB,EAAE,oBAAoB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,iBAAiB,CAAC;IAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAY,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,WAAW,CAAC,EAAE;QACZ,MAAM,CAAC,EAAE,oBAAoB,EAAE,CAAC;QAChC,QAAQ,CAAC,EAAE;YACT,QAAQ,CAAC,EAAE,yBAAyB,EAAE,CAAC;YACvC,OAAO,CAAC,EAAE,OAAO,CAAC;SACnB,CAAC;KACH,CAAC;CACH;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,kCAAkC,EAClC,GAAG,CAAC,MAAM,EAAE,oBAAoB,EAAE,CAAC,GACnC,SAAS,CAAC;IAEd,IAAI,wCAAwC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClE;AA4TD,wBAAgB,6BAA6B,CAC3C,WAAW,EAAE,oBAAoB,EAAE,GAClC,MAAM,IAAI,CASZ;AAED,qBAAa,wBAAwB;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,gBAAqB;IAE3D,cAAc,IAAI,WAAW;IAI7B,+BAA+B,IAAI,oBAAoB,EAAE;IAIzD,8BAA8B,IAAI,oBAAoB,EAAE;IAIxD,UAAU,IAAI,iBAAiB;IA0BzB,qBAAqB,IAAI,OAAO,CAAC,2BAA2B,CAAC;IAoDnE,OAAO,CAAC,gCAAgC;IA8GxC,MAAM,CAAC,MAAM,CAAC,OAAO,GAAE,gBAAqB,GAAG,wBAAwB;CAGxE;AAED,wBAAsB,qBAAqB,CACzC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,2BAA2B,CAAC,CAEtC"}
@@ -0,0 +1,150 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { Membership } from '../models/Membership.js';
3
+ import { Tenant } from '../models/Tenant.js';
4
+ /**
5
+ * Permission resolution result
6
+ */
7
+ export interface PermissionResolutionResult {
8
+ /** Set of granted permission slugs */
9
+ permissions: Set<string>;
10
+ /** Membership ID used for resolution */
11
+ membershipId: string | null;
12
+ /** Role ID from membership */
13
+ roleId: string | null;
14
+ /** Group IDs that contributed permissions */
15
+ groupIds: string[];
16
+ /** Permission IDs explicitly denied */
17
+ deniedPermissionIds: string[];
18
+ }
19
+ export interface PermissionResolutionOptions {
20
+ /**
21
+ * Active membership already resolved by the caller for this user/tenant.
22
+ * Passing this lets request-scoped session loaders avoid re-querying the
23
+ * same membership row before resolving permissions.
24
+ */
25
+ membership?: Membership | null;
26
+ }
27
+ /**
28
+ * Tenant permission inheritance chain result
29
+ */
30
+ export interface TenantPermissionInheritanceResult {
31
+ /** Effective tenant permissions (slugs) after inheritance resolution */
32
+ permissions: Set<string>;
33
+ /** Tenant IDs that contributed to the inheritance chain */
34
+ contributingTenantIds: string[];
35
+ /** Whether inheritance was active (at least one tenant in chain had inheritPermissions: true) */
36
+ inheritanceActive: boolean;
37
+ }
38
+ /**
39
+ * PermissionResolver resolves the effective permissions for a user in a tenant.
40
+ *
41
+ * Resolution algorithm:
42
+ * 1. Resolve tenant hierarchy permissions (if hierarchical tenants are used)
43
+ * 2. Get user's membership in the tenant
44
+ * 3. Get base permissions from membership's role
45
+ * 4. Get user's groups in the tenant
46
+ * 5. Add permissions from group roles
47
+ * 6. Apply membership overrides (grant/deny)
48
+ *
49
+ * DENY overrides take precedence over GRANT overrides at every level.
50
+ *
51
+ * ## Hierarchical Tenant Permissions
52
+ *
53
+ * When tenants are organized hierarchically, permissions can cascade from
54
+ * parent tenants to child tenants. The cascade is controlled by:
55
+ * - Parent's `cascadePermissions`: If true, parent pushes permissions down
56
+ * - Child's `inheritPermissions`: If true, child accepts parent's permissions
57
+ *
58
+ * Child tenants can override inherited permissions using TenantPermissionOverride:
59
+ * - INHERIT: Use parent's value (default)
60
+ * - GRANT: Explicitly grant at this level
61
+ * - DENY: Explicitly block (even if parent grants)
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const resolver = new PermissionResolver(options);
66
+ * await resolver.initialize();
67
+ *
68
+ * // Check single permission
69
+ * const canCreate = await resolver.hasPermission(userId, tenantId, 'articles.create');
70
+ *
71
+ * // Get all permissions
72
+ * const result = await resolver.resolvePermissions(userId, tenantId);
73
+ * console.log(result.permissions); // Set<string>
74
+ *
75
+ * // Resolve tenant-level permissions only (without user context)
76
+ * const tenantPerms = await resolver.resolveTenantPermissions(tenantId);
77
+ * ```
78
+ */
79
+ export declare class PermissionResolver {
80
+ private options;
81
+ private membershipCollection;
82
+ private rolePermissionCollection;
83
+ private membershipOverrideCollection;
84
+ private groupMemberCollection;
85
+ private groupRoleCollection;
86
+ private permissionCollection;
87
+ private tenantCollection;
88
+ private tenantPermissionOverrideCollection;
89
+ constructor(options: SmrtClassOptions);
90
+ /**
91
+ * Initialize collections
92
+ *
93
+ * Note: The `as any` casts are required because `SmrtCollection.create()` is
94
+ * declared with a protected constructor and a static factory. TypeScript
95
+ * cannot infer that the static `create()` returns the concrete subclass type
96
+ * when invoked through a subclass reference, so the cast is needed to call
97
+ * `create(options)` on each collection. This is a known SMRT framework
98
+ * limitation around the protected-constructor + static-factory pattern.
99
+ */
100
+ initialize(): Promise<void>;
101
+ /**
102
+ * Resolve effective permissions for a tenant, considering hierarchy inheritance.
103
+ *
104
+ * Algorithm:
105
+ * 1. Get the tenant and its ancestors (from root to immediate parent)
106
+ * 2. Batch fetch all permission overrides for the entire chain (single query)
107
+ * 3. Walk down the chain, building up permissions:
108
+ * - Start with root tenant's permissions
109
+ * - For each child: if parent.cascadePermissions && child.inheritPermissions:
110
+ * - Merge parent's permissions
111
+ * - Apply child's overrides (GRANT adds, DENY removes)
112
+ * 4. Return the final effective permission set
113
+ */
114
+ resolveTenantPermissions(tenantId: string): Promise<TenantPermissionInheritanceResult>;
115
+ /**
116
+ * Get the inheritance chain for a tenant (for debugging/display purposes)
117
+ */
118
+ getTenantInheritanceChain(tenantId: string): Promise<Array<{
119
+ tenant: Tenant;
120
+ inherits: boolean;
121
+ cascades: boolean;
122
+ }>>;
123
+ /**
124
+ * Resolve all effective permissions for a user in a tenant
125
+ *
126
+ * Algorithm:
127
+ * 1. Get membership and collect all permission IDs from all sources
128
+ * 2. Batch fetch all permissions in a single query
129
+ * 3. Apply permissions from role, groups, and overrides
130
+ * 4. DENY overrides take precedence over GRANT
131
+ */
132
+ resolvePermissions(userId: string, tenantId: string, options?: PermissionResolutionOptions): Promise<PermissionResolutionResult>;
133
+ /**
134
+ * Check if a user has a specific permission in a tenant
135
+ */
136
+ hasPermission(userId: string, tenantId: string, permissionSlug: string, options?: PermissionResolutionOptions): Promise<boolean>;
137
+ /**
138
+ * Check if a user has all of the specified permissions
139
+ */
140
+ hasAllPermissions(userId: string, tenantId: string, permissionSlugs: string[], options?: PermissionResolutionOptions): Promise<boolean>;
141
+ /**
142
+ * Check if a user has any of the specified permissions
143
+ */
144
+ hasAnyPermission(userId: string, tenantId: string, permissionSlugs: string[], options?: PermissionResolutionOptions): Promise<boolean>;
145
+ /**
146
+ * Static factory method
147
+ */
148
+ static create(options: SmrtClassOptions): Promise<PermissionResolver>;
149
+ }
150
+ //# sourceMappingURL=PermissionResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PermissionResolver.d.ts","sourceRoot":"","sources":["../../src/services/PermissionResolver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AASjE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,sCAAsC;IACtC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,wCAAwC;IACxC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,8BAA8B;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,uCAAuC;IACvC,mBAAmB,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,wEAAwE;IACxE,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,2DAA2D;IAC3D,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,iGAAiG;IACjG,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,wBAAwB,CAA4B;IAC5D,OAAO,CAAC,4BAA4B,CAAgC;IACpE,OAAO,CAAC,qBAAqB,CAAyB;IACtD,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,OAAO,CAAC,kCAAkC,CAAsC;gBAEpE,OAAO,EAAE,gBAAgB;IAIrC;;;;;;;;;OASG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA+BjC;;;;;;;;;;;;OAYG;IACG,wBAAwB,CAC5B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,iCAAiC,CAAC;IA6G7C;;OAEG;IACG,yBAAyB,CAC7B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAmC3E;;;;;;;;OAQG;IACG,kBAAkB,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,0BAA0B,CAAC;IA6ItC;;OAEG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;IACG,iBAAiB,CACrB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;IACG,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,OAAO,CAAC;IAKnB;;OAEG;WACU,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,CAAC;CAK5E"}
@@ -0,0 +1,29 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { PostgresPermissionAction, PostgresPermissionBinding } from './PermissionCatalogService.js';
3
+ export interface PostgresPermissionPolicyReportItem {
4
+ className?: string;
5
+ collection?: string;
6
+ qualifiedName?: string;
7
+ reason: string;
8
+ schemaName?: string;
9
+ tableName?: string;
10
+ }
11
+ export interface PostgresPermissionPolicyTarget {
12
+ actions: Partial<Record<PostgresPermissionAction, string[]>>;
13
+ className?: string;
14
+ collection?: string;
15
+ qualifiedName?: string;
16
+ schemaName: string;
17
+ tableName: string;
18
+ tenantField: string;
19
+ }
20
+ export interface GeneratePostgresPermissionSqlResult {
21
+ bindings: PostgresPermissionBinding[];
22
+ skipped: PostgresPermissionPolicyReportItem[];
23
+ sql: string;
24
+ statements: string[];
25
+ targets: PostgresPermissionPolicyTarget[];
26
+ }
27
+ export declare function generatePostgresPermissionSql(options?: SmrtClassOptions): GeneratePostgresPermissionSqlResult;
28
+ export declare function applyPostgresPermissionPolicies(options?: SmrtClassOptions): Promise<GeneratePostgresPermissionSqlResult>;
29
+ //# sourceMappingURL=PostgresPermissionPolicies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresPermissionPolicies.d.ts","sourceRoot":"","sources":["../../src/services/PostgresPermissionPolicies.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC/B,MAAM,+BAA+B,CAAC;AAmBvC,MAAM,WAAW,kCAAkC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,wBAAwB,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,EAAE,yBAAyB,EAAE,CAAC;IACtC,OAAO,EAAE,kCAAkC,EAAE,CAAC;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,OAAO,EAAE,8BAA8B,EAAE,CAAC;CAC3C;AAuQD,wBAAgB,6BAA6B,CAC3C,OAAO,GAAE,gBAAqB,GAC7B,mCAAmC,CAgOrC;AAED,wBAAsB,+BAA+B,CACnD,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,mCAAmC,CAAC,CA0B9C"}
@@ -0,0 +1,43 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { DatabaseInterface } from '@happyvertical/sql';
3
+ import { SessionContext, SessionService } from './SessionService.js';
4
+ interface QueryableDatabase extends DatabaseInterface {
5
+ beginTransaction?: () => Promise<TransactionDatabase>;
6
+ url: string;
7
+ }
8
+ interface TransactionDatabase extends QueryableDatabase {
9
+ commit: () => Promise<void>;
10
+ isActive: () => boolean;
11
+ rollback: () => Promise<void>;
12
+ }
13
+ export interface SessionPermissionRuntimeContext {
14
+ database?: QueryableDatabase;
15
+ permissions: string[];
16
+ permissionSet: Set<string>;
17
+ membership: SessionContext['membership'];
18
+ postgresRls: boolean;
19
+ session: SessionContext | null;
20
+ sessionId: string | null;
21
+ superAdminBypass: boolean;
22
+ systemContext: boolean;
23
+ tenantId: string | null;
24
+ user: SessionContext['user'] | null;
25
+ userId: string | null;
26
+ }
27
+ export interface SessionPermissionRuntimeOptions extends SmrtClassOptions {
28
+ enterTenantContext?: boolean;
29
+ postgresRls?: boolean;
30
+ sessionId?: string | null;
31
+ sessionService?: SessionService;
32
+ superAdminBypass?: boolean;
33
+ systemContext?: boolean;
34
+ }
35
+ declare global {
36
+ var __smrtGetRequestPermissionContext: (() => SessionPermissionRuntimeContext | undefined) | undefined;
37
+ var __smrtGetRequestScopedDatabase: (() => QueryableDatabase | undefined) | undefined;
38
+ }
39
+ export declare function getCurrentSessionPermissionContext(): SessionPermissionRuntimeContext | undefined;
40
+ export declare function getRequestScopedDatabase(): QueryableDatabase | undefined;
41
+ export declare function withSessionPermissionContext<T>(options: SessionPermissionRuntimeOptions, fn: (context: SessionPermissionRuntimeContext) => Promise<T>): Promise<T>;
42
+ export {};
43
+ //# sourceMappingURL=SessionPermissionContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionPermissionContext.d.ts","sourceRoot":"","sources":["../../src/services/SessionPermissionContext.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EACL,KAAK,cAAc,EACnB,cAAc,EAEf,MAAM,qBAAqB,CAAC;AAE7B,UAAU,iBAAkB,SAAQ,iBAAiB;IACnD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACtD,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,mBAAoB,SAAQ,iBAAiB;IACrD,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,EAAE,MAAM,OAAO,CAAC;IACxB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,CAAC;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAC;IAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IACpC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,+BAAgC,SAAQ,gBAAgB;IACvE,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,iCAAiC,EACjC,CAAC,MAAM,+BAA+B,GAAG,SAAS,CAAC,GACnD,SAAS,CAAC;IAEd,IAAI,8BAA8B,EAC9B,CAAC,MAAM,iBAAiB,GAAG,SAAS,CAAC,GACrC,SAAS,CAAC;CACf;AAKD,wBAAgB,kCAAkC,IAC9C,+BAA+B,GAC/B,SAAS,CAEZ;AAED,wBAAgB,wBAAwB,IAAI,iBAAiB,GAAG,SAAS,CAExE;AA2GD,wBAAsB,4BAA4B,CAAC,CAAC,EAClD,OAAO,EAAE,+BAA+B,EACxC,EAAE,EAAE,CAAC,OAAO,EAAE,+BAA+B,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3D,OAAO,CAAC,CAAC,CAAC,CAoFZ"}
@@ -0,0 +1,139 @@
1
+ import { SmrtClassOptions } from '@happyvertical/smrt-core';
2
+ import { CreateSessionOptions } from '../collections/SessionCollection.js';
3
+ import { Membership } from '../models/Membership.js';
4
+ import { User } from '../models/User.js';
5
+ /**
6
+ * Session context with user and permissions
7
+ */
8
+ export interface SessionContext {
9
+ /** The User record */
10
+ user: User;
11
+ /** Active membership for the current tenant, if one was resolved */
12
+ membership?: Membership | null;
13
+ /** Resolved permission slugs */
14
+ permissions: string[];
15
+ /** Tenant ID from session (if any) */
16
+ tenantId: string | null;
17
+ /** Session ID */
18
+ sessionId: string;
19
+ }
20
+ /**
21
+ * Options for SessionService
22
+ */
23
+ export interface SessionServiceOptions extends SmrtClassOptions {
24
+ /** Default session TTL in seconds (default: 7 days) */
25
+ defaultTTL?: number;
26
+ /** Cookie name (default: 'sid') */
27
+ cookieName?: string;
28
+ /** Whether to auto-extend sessions on access (default: false) */
29
+ autoExtend?: boolean;
30
+ }
31
+ /**
32
+ * SessionService provides high-level session management that combines
33
+ * session storage with user and permission loading.
34
+ *
35
+ * This is the main service to use for session-based authentication.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const sessionService = await SessionService.create({
40
+ * db: { type: 'sqlite', url: 'app.db' },
41
+ * defaultTTL: 7 * 24 * 60 * 60, // 7 days
42
+ * });
43
+ *
44
+ * // Create session after login
45
+ * const sessionId = await sessionService.createSession(userId, tenantId);
46
+ *
47
+ * // Load session context on each request
48
+ * const context = await sessionService.loadSessionContext(sessionId);
49
+ * if (context) {
50
+ * console.log('User:', context.user.email);
51
+ * console.log('Permissions:', context.permissions);
52
+ * }
53
+ *
54
+ * // Destroy session on logout
55
+ * await sessionService.destroySession(sessionId);
56
+ * ```
57
+ */
58
+ export declare class SessionService {
59
+ private options;
60
+ private sessionCollection;
61
+ private userCollection;
62
+ private membershipCollection;
63
+ private permissionResolver;
64
+ private defaultTTL;
65
+ private autoExtend;
66
+ constructor(options: SessionServiceOptions);
67
+ /**
68
+ * Initialize collections
69
+ */
70
+ initialize(): Promise<void>;
71
+ /**
72
+ * Create a new session for a user
73
+ *
74
+ * @param userId - The user ID
75
+ * @param tenantId - Optional tenant context
76
+ * @param options - Additional session options
77
+ * @returns The session ID
78
+ */
79
+ createSession(userId: string, tenantId?: string, options?: Partial<CreateSessionOptions>): Promise<string>;
80
+ /**
81
+ * Load full session context (user + permissions)
82
+ *
83
+ * Returns null if session is invalid or user doesn't exist
84
+ */
85
+ loadSessionContext(sessionId: string): Promise<SessionContext | null>;
86
+ /**
87
+ * Get the initialized database connection backing this session service.
88
+ */
89
+ getDatabase(): import('@happyvertical/sql').DatabaseInterface;
90
+ /**
91
+ * Refresh session (extend expiry, update lastAccessed)
92
+ */
93
+ refreshSession(sessionId: string): Promise<boolean>;
94
+ /**
95
+ * Destroy a session (revoke it)
96
+ */
97
+ destroySession(sessionId: string): Promise<boolean>;
98
+ /**
99
+ * Destroy all sessions for a user (logout from all devices)
100
+ */
101
+ destroyAllUserSessions(userId: string): Promise<number>;
102
+ /**
103
+ * Switch tenant context for a session.
104
+ *
105
+ * A session's `tenantId` is the tenant-isolation key for every `@TenantScoped`
106
+ * query, so it must never be set to a tenant the session's user is not an
107
+ * active member of — otherwise a caller could read/write another tenant's data
108
+ * by feeding an arbitrary id here (e.g. straight from untrusted form data).
109
+ * Fail-closed (#1400): returns `false` without switching when the session is
110
+ * unknown or the user has no active membership in the target tenant. Passing
111
+ * `null` clears the tenant context and is always allowed.
112
+ */
113
+ switchTenant(sessionId: string, tenantId: string | null): Promise<boolean>;
114
+ /**
115
+ * Get all active sessions for a user (for "manage sessions" UI)
116
+ */
117
+ getUserSessions(userId: string): Promise<import('../index.js').Session[]>;
118
+ /**
119
+ * Clean up expired sessions (run periodically)
120
+ */
121
+ cleanupExpiredSessions(): Promise<number>;
122
+ /**
123
+ * Check if a permission is granted for the session
124
+ */
125
+ hasPermission(sessionId: string, permission: string): Promise<boolean>;
126
+ /**
127
+ * Get session data
128
+ */
129
+ getSessionData<T>(sessionId: string, key: string): Promise<T | undefined>;
130
+ /**
131
+ * Set session data
132
+ */
133
+ setSessionData(sessionId: string, key: string, value: unknown): Promise<boolean>;
134
+ /**
135
+ * Static factory method
136
+ */
137
+ static create(options: SessionServiceOptions): Promise<SessionService>;
138
+ }
139
+ //# sourceMappingURL=SessionService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SessionService.d.ts","sourceRoot":"","sources":["../../src/services/SessionService.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,qCAAqC,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAG9C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,sBAAsB;IACtB,IAAI,EAAE,IAAI,CAAC;IACX,oEAAoE;IACpE,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,gCAAgC;IAChC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,sCAAsC;IACtC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,oBAAoB,CAAwB;IACpD,OAAO,CAAC,kBAAkB,CAAsB;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAU;gBAEhB,OAAO,EAAE,qBAAqB;IAM1C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAWjC;;;;;;;OAOG;IACG,aAAa,CACjB,MAAM,EAAE,MAAM,EACd,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC;IAalB;;;;OAIG;IACG,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA6C3E;;OAEG;IACH,WAAW;IAIX;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD;;OAEG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzD;;OAEG;IACG,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI7D;;;;;;;;;;OAUG;IACG,YAAY,CAChB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,OAAO,CAAC,OAAO,CAAC;IAiBnB;;OAEG;IACG,eAAe,CAAC,MAAM,EAAE,MAAM;IAIpC;;OAEG;IACG,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI/C;;OAEG;IACG,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAM5E;;OAEG;IACG,cAAc,CAAC,CAAC,EACpB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAIzB;;OAEG;IACG,cAAc,CAClB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,OAAO,CAAC;IAInB;;OAEG;WACU,MAAM,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,cAAc,CAAC;CAK7E"}