@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.
- package/AGENTS.md +85 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +459 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/chunks/TerminalAuthService-DoAMQ_yn.js +5118 -0
- package/dist/chunks/TerminalAuthService-DoAMQ_yn.js.map +1 -0
- package/dist/chunks/index-DkoYIvIu.js +169 -0
- package/dist/chunks/index-DkoYIvIu.js.map +1 -0
- package/dist/collections/CliAuthRequestCollection.d.ts +19 -0
- package/dist/collections/CliAuthRequestCollection.d.ts.map +1 -0
- package/dist/collections/GroupCollection.d.ts +17 -0
- package/dist/collections/GroupCollection.d.ts.map +1 -0
- package/dist/collections/GroupMemberCollection.d.ts +43 -0
- package/dist/collections/GroupMemberCollection.d.ts.map +1 -0
- package/dist/collections/GroupRoleCollection.d.ts +33 -0
- package/dist/collections/GroupRoleCollection.d.ts.map +1 -0
- package/dist/collections/MagicLinkTokenCollection.d.ts +26 -0
- package/dist/collections/MagicLinkTokenCollection.d.ts.map +1 -0
- package/dist/collections/MembershipCollection.d.ts +38 -0
- package/dist/collections/MembershipCollection.d.ts.map +1 -0
- package/dist/collections/MembershipOverrideCollection.d.ts +55 -0
- package/dist/collections/MembershipOverrideCollection.d.ts.map +1 -0
- package/dist/collections/PermissionCollection.d.ts +34 -0
- package/dist/collections/PermissionCollection.d.ts.map +1 -0
- package/dist/collections/RoleCollection.d.ts +29 -0
- package/dist/collections/RoleCollection.d.ts.map +1 -0
- package/dist/collections/RolePermissionCollection.d.ts +33 -0
- package/dist/collections/RolePermissionCollection.d.ts.map +1 -0
- package/dist/collections/SessionCollection.d.ts +82 -0
- package/dist/collections/SessionCollection.d.ts.map +1 -0
- package/dist/collections/TenantCollection.d.ts +119 -0
- package/dist/collections/TenantCollection.d.ts.map +1 -0
- package/dist/collections/TenantPermissionOverrideCollection.d.ts +111 -0
- package/dist/collections/TenantPermissionOverrideCollection.d.ts.map +1 -0
- package/dist/collections/UserCollection.d.ts +116 -0
- package/dist/collections/UserCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +19 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1482 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +5216 -0
- package/dist/models/CliAuthRequest.d.ts +25 -0
- package/dist/models/CliAuthRequest.d.ts.map +1 -0
- package/dist/models/Group.d.ts +34 -0
- package/dist/models/Group.d.ts.map +1 -0
- package/dist/models/GroupMember.d.ts +29 -0
- package/dist/models/GroupMember.d.ts.map +1 -0
- package/dist/models/GroupRole.d.ts +29 -0
- package/dist/models/GroupRole.d.ts.map +1 -0
- package/dist/models/MagicLinkToken.d.ts +22 -0
- package/dist/models/MagicLinkToken.d.ts.map +1 -0
- package/dist/models/Membership.d.ts +48 -0
- package/dist/models/Membership.d.ts.map +1 -0
- package/dist/models/MembershipOverride.d.ts +50 -0
- package/dist/models/MembershipOverride.d.ts.map +1 -0
- package/dist/models/Permission.d.ts +79 -0
- package/dist/models/Permission.d.ts.map +1 -0
- package/dist/models/Role.d.ts +67 -0
- package/dist/models/Role.d.ts.map +1 -0
- package/dist/models/RolePermission.d.ts +29 -0
- package/dist/models/RolePermission.d.ts.map +1 -0
- package/dist/models/Session.d.ts +105 -0
- package/dist/models/Session.d.ts.map +1 -0
- package/dist/models/Tenant.d.ts +138 -0
- package/dist/models/Tenant.d.ts.map +1 -0
- package/dist/models/TenantPermissionOverride.d.ts +74 -0
- package/dist/models/TenantPermissionOverride.d.ts.map +1 -0
- package/dist/models/User.d.ts +72 -0
- package/dist/models/User.d.ts.map +1 -0
- package/dist/models/index.d.ts +19 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +139 -0
- package/dist/playground.js.map +1 -0
- package/dist/services/MagicLinkService.d.ts +84 -0
- package/dist/services/MagicLinkService.d.ts.map +1 -0
- package/dist/services/OidcLoginService.d.ts +134 -0
- package/dist/services/OidcLoginService.d.ts.map +1 -0
- package/dist/services/PermissionCatalogService.d.ts +62 -0
- package/dist/services/PermissionCatalogService.d.ts.map +1 -0
- package/dist/services/PermissionResolver.d.ts +150 -0
- package/dist/services/PermissionResolver.d.ts.map +1 -0
- package/dist/services/PostgresPermissionPolicies.d.ts +29 -0
- package/dist/services/PostgresPermissionPolicies.d.ts.map +1 -0
- package/dist/services/SessionPermissionContext.d.ts +43 -0
- package/dist/services/SessionPermissionContext.d.ts.map +1 -0
- package/dist/services/SessionService.d.ts +139 -0
- package/dist/services/SessionService.d.ts.map +1 -0
- package/dist/services/TenantService.d.ts +135 -0
- package/dist/services/TenantService.d.ts.map +1 -0
- package/dist/services/TerminalAuthService.d.ts +189 -0
- package/dist/services/TerminalAuthService.d.ts.map +1 -0
- package/dist/services/index.d.ts +14 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +2744 -0
- package/dist/svelte/components/InviteUserModal.svelte +351 -0
- package/dist/svelte/components/InviteUserModal.svelte.d.ts +17 -0
- package/dist/svelte/components/InviteUserModal.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserAvatar.svelte +105 -0
- package/dist/svelte/components/UserAvatar.svelte.d.ts +10 -0
- package/dist/svelte/components/UserAvatar.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserCard.svelte +179 -0
- package/dist/svelte/components/UserCard.svelte.d.ts +18 -0
- package/dist/svelte/components/UserCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserForm.svelte +194 -0
- package/dist/svelte/components/UserForm.svelte.d.ts +18 -0
- package/dist/svelte/components/UserForm.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserList.svelte +107 -0
- package/dist/svelte/components/UserList.svelte.d.ts +20 -0
- package/dist/svelte/components/UserList.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UserMenu.svelte +326 -0
- package/dist/svelte/components/UserMenu.svelte.d.ts +33 -0
- package/dist/svelte/components/UserMenu.svelte.d.ts.map +1 -0
- package/dist/svelte/components/__tests__/InviteUserModal.test.js +54 -0
- package/dist/svelte/components/__tests__/UserAvatar.test.js +31 -0
- package/dist/svelte/components/__tests__/UserCard.test.js +39 -0
- package/dist/svelte/components/__tests__/UserForm.test.js +50 -0
- package/dist/svelte/components/__tests__/UserList.test.js +48 -0
- package/dist/svelte/components/__tests__/UserMenu.test.js +38 -0
- package/dist/svelte/i18n.d.ts +15 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +15 -0
- package/dist/svelte/index.d.ts +23 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +27 -0
- package/dist/svelte/playground.d.ts +151 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +134 -0
- package/dist/sveltekit/index.d.ts +379 -0
- package/dist/sveltekit/index.d.ts.map +1 -0
- package/dist/sveltekit/resource-list-handler.d.ts +127 -0
- package/dist/sveltekit/resource-list-handler.d.ts.map +1 -0
- package/dist/sveltekit/types.d.ts +31 -0
- package/dist/sveltekit/types.d.ts.map +1 -0
- package/dist/sveltekit.d.ts +2 -0
- package/dist/sveltekit.d.ts.map +1 -0
- package/dist/sveltekit.js +978 -0
- package/dist/sveltekit.js.map +1 -0
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/ui.d.ts +10 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +75 -0
- package/dist/ui.js.map +1 -0
- package/package.json +97 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { Tenant as TenantContract } from '@happyvertical/smrt-types';
|
|
3
|
+
import { TenantStatus } from '../types/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Maximum allowed depth for tenant hierarchy.
|
|
6
|
+
* Prevents excessively deep trees that could cause performance issues.
|
|
7
|
+
*/
|
|
8
|
+
export declare const MAX_TENANT_HIERARCHY_DEPTH = 10;
|
|
9
|
+
/**
|
|
10
|
+
* Tenant represents an organizational boundary in the multi-tenant system.
|
|
11
|
+
*
|
|
12
|
+
* Supports hierarchical organization with parent-child relationships.
|
|
13
|
+
* Users can belong to multiple tenants through Memberships.
|
|
14
|
+
* Each tenant can have custom roles in addition to system defaults.
|
|
15
|
+
*
|
|
16
|
+
* ## Hierarchical Tenants
|
|
17
|
+
*
|
|
18
|
+
* Tenants can be organized in a tree structure where child tenants
|
|
19
|
+
* can optionally inherit permissions from their parent tenants.
|
|
20
|
+
*
|
|
21
|
+
* ### Cascade Control
|
|
22
|
+
*
|
|
23
|
+
* Two flags control permission inheritance:
|
|
24
|
+
* - `cascadePermissions`: If true, this tenant pushes its permissions to children
|
|
25
|
+
* - `inheritPermissions`: If true, this tenant accepts permissions from parent
|
|
26
|
+
*
|
|
27
|
+
* Both must be true for inheritance to flow from parent to child.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* // Create root tenant
|
|
32
|
+
* const corp = await tenants.create({
|
|
33
|
+
* name: 'Acme Corporation',
|
|
34
|
+
* slug: 'acme-corp',
|
|
35
|
+
* cascadePermissions: true, // Push permissions to children
|
|
36
|
+
* });
|
|
37
|
+
* await corp.save();
|
|
38
|
+
*
|
|
39
|
+
* // Create child tenant that inherits
|
|
40
|
+
* const division = await tenants.create({
|
|
41
|
+
* name: 'Acme West Division',
|
|
42
|
+
* slug: 'acme-west',
|
|
43
|
+
* parentTenantId: corp.id,
|
|
44
|
+
* inheritPermissions: true, // Accept parent permissions
|
|
45
|
+
* });
|
|
46
|
+
* await division.save();
|
|
47
|
+
*
|
|
48
|
+
* // Create independent child (breaks inheritance chain)
|
|
49
|
+
* const independent = await tenants.create({
|
|
50
|
+
* name: 'Acme Labs',
|
|
51
|
+
* slug: 'acme-labs',
|
|
52
|
+
* parentTenantId: corp.id,
|
|
53
|
+
* inheritPermissions: false, // Does NOT inherit from parent
|
|
54
|
+
* });
|
|
55
|
+
* await independent.save();
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare class Tenant extends SmrtObject implements TenantContract {
|
|
59
|
+
/**
|
|
60
|
+
* Display name for the tenant
|
|
61
|
+
*/
|
|
62
|
+
name: string;
|
|
63
|
+
/**
|
|
64
|
+
* Tenant status
|
|
65
|
+
*/
|
|
66
|
+
status: TenantStatus;
|
|
67
|
+
/**
|
|
68
|
+
* Optional description
|
|
69
|
+
*/
|
|
70
|
+
description: string;
|
|
71
|
+
/**
|
|
72
|
+
* Parent tenant ID for hierarchical organization.
|
|
73
|
+
* Null for root-level tenants.
|
|
74
|
+
*/
|
|
75
|
+
parentTenantId?: string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Depth in the hierarchy tree (0 = root, 1 = first level child, etc.)
|
|
78
|
+
* Automatically managed by TenantCollection methods.
|
|
79
|
+
*/
|
|
80
|
+
hierarchyLevel: number;
|
|
81
|
+
/**
|
|
82
|
+
* Materialized path for efficient tree traversal.
|
|
83
|
+
* Format: "ancestor-id/parent-id" (path to parent; does not include this tenant's id)
|
|
84
|
+
* Empty string for root tenants.
|
|
85
|
+
* Automatically managed by TenantCollection methods.
|
|
86
|
+
*/
|
|
87
|
+
hierarchyPath: string;
|
|
88
|
+
/**
|
|
89
|
+
* If true, this tenant's permissions cascade DOWN to child tenants.
|
|
90
|
+
* Children can still opt-out by setting inheritPermissions: false.
|
|
91
|
+
* Default: true
|
|
92
|
+
*/
|
|
93
|
+
cascadePermissions: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* If true, this tenant ACCEPTS permissions from its parent tenant.
|
|
96
|
+
* Parent must also have cascadePermissions: true for inheritance to work.
|
|
97
|
+
* Default: true
|
|
98
|
+
*/
|
|
99
|
+
inheritPermissions: boolean;
|
|
100
|
+
constructor(options?: any);
|
|
101
|
+
/**
|
|
102
|
+
* Check if tenant is active
|
|
103
|
+
*/
|
|
104
|
+
isActive(): boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Check if tenant is suspended
|
|
107
|
+
*/
|
|
108
|
+
isSuspended(): boolean;
|
|
109
|
+
/**
|
|
110
|
+
* Check if this is a root-level tenant (no parent)
|
|
111
|
+
*/
|
|
112
|
+
isRoot(): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Check if this tenant is configured to cascade permissions to children.
|
|
115
|
+
*
|
|
116
|
+
* Note: This does NOT indicate whether any child tenants actually exist.
|
|
117
|
+
* Use TenantCollection.findChildren() for accurate child lookup.
|
|
118
|
+
*/
|
|
119
|
+
canCascadeToChildren(): boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Check if permission inheritance is active for this tenant.
|
|
122
|
+
* Inheritance is active if:
|
|
123
|
+
* - This tenant has a parent AND
|
|
124
|
+
* - This tenant has inheritPermissions: true
|
|
125
|
+
*
|
|
126
|
+
* Note: The parent must also have cascadePermissions: true
|
|
127
|
+
* for actual inheritance to occur. Use PermissionResolver
|
|
128
|
+
* for accurate permission calculation.
|
|
129
|
+
*/
|
|
130
|
+
acceptsInheritance(): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Get ancestor IDs from the hierarchy path.
|
|
133
|
+
* Returns an array of tenant IDs from root to immediate parent.
|
|
134
|
+
* Empty array for root tenants.
|
|
135
|
+
*/
|
|
136
|
+
getAncestorIds(): string[];
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=Tenant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tenant.d.ts","sourceRoot":"","sources":["../../src/models/Tenant.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqB,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,qBASa,MAAO,SAAQ,UAAW,YAAW,cAAc;IAC9D;;OAEG;IACH,IAAI,EAAE,MAAM,CAAM;IAElB;;OAEG;IAEH,MAAM,EAAE,YAAY,CAAuB;IAE3C;;OAEG;IACH,WAAW,EAAE,MAAM,CAAM;IAIzB;;;OAGG;IAEH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE/B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAK;IAE3B;;;;;OAKG;IACH,aAAa,EAAE,MAAM,CAAM;IAI3B;;;;OAIG;IACH,kBAAkB,EAAE,OAAO,CAAQ;IAEnC;;;;OAIG;IACH,kBAAkB,EAAE,OAAO,CAAQ;gBAEvB,OAAO,GAAE,GAAQ;IAkB7B;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACH,oBAAoB,IAAI,OAAO;IAI/B;;;;;;;;;OASG;IACH,kBAAkB,IAAI,OAAO;IAI7B;;;;OAIG;IACH,cAAc,IAAI,MAAM,EAAE;CAI3B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { TenantPermissionEffect } from '../types/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* TenantPermissionOverride allows setting explicit permission values at the tenant level.
|
|
5
|
+
*
|
|
6
|
+
* This is used in hierarchical tenant structures to control permission inheritance.
|
|
7
|
+
* Each override specifies whether a permission is:
|
|
8
|
+
* - INHERIT: Use the parent tenant's value (default behavior)
|
|
9
|
+
* - GRANT: Explicitly grant at this tenant level
|
|
10
|
+
* - DENY: Explicitly deny at this tenant level (blocks inheritance)
|
|
11
|
+
*
|
|
12
|
+
* ## Resolution Order
|
|
13
|
+
*
|
|
14
|
+
* When resolving effective tenant permissions:
|
|
15
|
+
* 1. Walk down the tenant hierarchy from root to target, accumulating permissions
|
|
16
|
+
* 2. Apply each tenant's overrides (DENY blocks, GRANT adds)
|
|
17
|
+
* 3. INHERIT means "use whatever the parent resolved to"
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // Explicitly grant a permission at this tenant level
|
|
22
|
+
* const override = await tenantPermissionOverrides.create({
|
|
23
|
+
* tenantId: tenant.id,
|
|
24
|
+
* permissionId: specialPermission.id,
|
|
25
|
+
* effect: TenantPermissionEffect.GRANT
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Block a permission from being inherited (even if parent grants it)
|
|
29
|
+
* const block = await tenantPermissionOverrides.create({
|
|
30
|
+
* tenantId: tenant.id,
|
|
31
|
+
* permissionId: dangerousPermission.id,
|
|
32
|
+
* effect: TenantPermissionEffect.DENY
|
|
33
|
+
* });
|
|
34
|
+
*
|
|
35
|
+
* // Explicitly inherit (same as not having an override, but documents intent)
|
|
36
|
+
* const inherit = await tenantPermissionOverrides.create({
|
|
37
|
+
* tenantId: tenant.id,
|
|
38
|
+
* permissionId: standardPermission.id,
|
|
39
|
+
* effect: TenantPermissionEffect.INHERIT
|
|
40
|
+
* });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class TenantPermissionOverride extends SmrtObject {
|
|
44
|
+
/**
|
|
45
|
+
* Foreign key to Tenant
|
|
46
|
+
*/
|
|
47
|
+
tenantId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Foreign key to Permission
|
|
50
|
+
*/
|
|
51
|
+
permissionId?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Effect of the override: inherit, grant, or deny
|
|
54
|
+
*/
|
|
55
|
+
effect: TenantPermissionEffect;
|
|
56
|
+
constructor(options?: any);
|
|
57
|
+
/**
|
|
58
|
+
* Check if this override inherits from parent
|
|
59
|
+
*/
|
|
60
|
+
isInherit(): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Check if this override grants the permission
|
|
63
|
+
*/
|
|
64
|
+
isGrant(): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Check if this override denies the permission
|
|
67
|
+
*/
|
|
68
|
+
isDeny(): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Check if this override has an explicit effect (not inherit)
|
|
71
|
+
*/
|
|
72
|
+
hasExplicitEffect(): boolean;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=TenantPermissionOverride.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TenantPermissionOverride.d.ts","sourceRoot":"","sources":["../../src/models/TenantPermissionOverride.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAqB,UAAU,EAAQ,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,qBAQa,wBAAyB,SAAQ,UAAU;IACtD;;OAEG;IAEH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IAEH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IAEH,MAAM,EAAE,sBAAsB,CAAkC;gBAEpD,OAAO,GAAE,GAAQ;IAQ7B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,iBAAiB,IAAI,OAAO;CAG7B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
2
|
+
import { User as UserContract } from '@happyvertical/smrt-types';
|
|
3
|
+
import { UserStatus } from '../types/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Validate email format.
|
|
6
|
+
* @param email - Email address to validate
|
|
7
|
+
* @returns true if email format is valid
|
|
8
|
+
*/
|
|
9
|
+
export declare function isValidEmail(email: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Normalize email address (lowercase and trim).
|
|
12
|
+
* @param email - Email address to normalize
|
|
13
|
+
* @returns Normalized email
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeEmail(email: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* User represents an authenticated identity in the system.
|
|
18
|
+
*
|
|
19
|
+
* Users are linked to Profiles from smrt-profiles via profileId.
|
|
20
|
+
* A User can have multiple Memberships across different Tenants.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const user = await users.create({
|
|
25
|
+
* profileId: 'profile-uuid',
|
|
26
|
+
* email: 'user@example.com',
|
|
27
|
+
* status: UserStatus.ACTIVE
|
|
28
|
+
* });
|
|
29
|
+
* await user.save();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare class User extends SmrtObject implements UserContract {
|
|
33
|
+
/**
|
|
34
|
+
* Foreign key to smrt-profiles Profile (cross-package)
|
|
35
|
+
*/
|
|
36
|
+
profileId: string;
|
|
37
|
+
/**
|
|
38
|
+
* User's email address (unique, used for lookup)
|
|
39
|
+
*/
|
|
40
|
+
email: string;
|
|
41
|
+
/**
|
|
42
|
+
* User account status
|
|
43
|
+
*/
|
|
44
|
+
status: UserStatus;
|
|
45
|
+
/**
|
|
46
|
+
* Last login timestamp
|
|
47
|
+
*/
|
|
48
|
+
lastLoginAt: Date | null;
|
|
49
|
+
constructor(options?: any);
|
|
50
|
+
/**
|
|
51
|
+
* Validate the user's email format.
|
|
52
|
+
* @returns true if email is valid
|
|
53
|
+
*/
|
|
54
|
+
hasValidEmail(): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check if user is active
|
|
57
|
+
*/
|
|
58
|
+
isActive(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Check if user is suspended
|
|
61
|
+
*/
|
|
62
|
+
isSuspended(): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Check if user is pending verification
|
|
65
|
+
*/
|
|
66
|
+
isPending(): boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Record a login event
|
|
69
|
+
*/
|
|
70
|
+
recordLogin(): void;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=User.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"User.d.ts","sourceRoot":"","sources":["../../src/models/User.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAGL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,IAAI,IAAI,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAQ/C;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGpD;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAOa,IAAK,SAAQ,UAAW,YAAW,YAAY;IAC1D;;OAEG;IAEH,SAAS,EAAE,MAAM,CAAM;IAEvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAM;IAEnB;;OAEG;IAEH,MAAM,EAAE,UAAU,CAAqB;IAEvC;;OAEG;IACH,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;gBAEpB,OAAO,GAAE,GAAQ;IAS7B;;;OAGG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,WAAW,IAAI,IAAI;CAGpB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model exports for smrt-users
|
|
3
|
+
* @packageDocumentation
|
|
4
|
+
*/
|
|
5
|
+
export { CliAuthRequest, type CliAuthRequestStatus, UsersCliAuthRequest, } from './CliAuthRequest.js';
|
|
6
|
+
export { Group } from './Group.js';
|
|
7
|
+
export { GroupMember } from './GroupMember.js';
|
|
8
|
+
export { GroupRole } from './GroupRole.js';
|
|
9
|
+
export { DEFAULT_TOKEN_EXPIRY_SECONDS, MagicLinkToken, UsersMagicLinkToken, } from './MagicLinkToken.js';
|
|
10
|
+
export { Membership } from './Membership.js';
|
|
11
|
+
export { MembershipOverride } from './MembershipOverride.js';
|
|
12
|
+
export { isValidPermissionSlug, type ParsedPermissionSlug, Permission, parsePermissionSlug, } from './Permission.js';
|
|
13
|
+
export { Role } from './Role.js';
|
|
14
|
+
export { RolePermission } from './RolePermission.js';
|
|
15
|
+
export { DEFAULT_SESSION_TTL, generateSessionId, Session, } from './Session.js';
|
|
16
|
+
export { MAX_TENANT_HIERARCHY_DEPTH, Tenant } from './Tenant.js';
|
|
17
|
+
export { TenantPermissionOverride } from './TenantPermissionOverride.js';
|
|
18
|
+
export { isValidEmail, normalizeEmail, User } from './User.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/models/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EACL,cAAc,EACd,KAAK,oBAAoB,EACzB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EACL,4BAA4B,EAC5B,cAAc,EACd,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,UAAU,EACV,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EACL,mBAAmB,EACnB,iBAAiB,EACjB,OAAO,GACR,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playground.d.ts","sourceRoot":"","sources":["../src/playground.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { USERS_MODULE_META } from "./ui.js";
|
|
2
|
+
const noop = () => {
|
|
3
|
+
};
|
|
4
|
+
const sampleTenant = {
|
|
5
|
+
id: "tenant-riverstone",
|
|
6
|
+
name: "Riverstone Newsroom",
|
|
7
|
+
slug: "riverstone-newsroom",
|
|
8
|
+
status: "active"
|
|
9
|
+
};
|
|
10
|
+
const sampleRoles = [
|
|
11
|
+
{
|
|
12
|
+
id: "role-member",
|
|
13
|
+
slug: "member",
|
|
14
|
+
name: "Member",
|
|
15
|
+
description: "Can collaborate on editorial work."
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "role-editor",
|
|
19
|
+
slug: "editor",
|
|
20
|
+
name: "Editor",
|
|
21
|
+
description: "Can approve and publish content."
|
|
22
|
+
}
|
|
23
|
+
];
|
|
24
|
+
const sampleUsers = [
|
|
25
|
+
{
|
|
26
|
+
user: {
|
|
27
|
+
id: "user-taylor",
|
|
28
|
+
email: "taylor@example.com",
|
|
29
|
+
status: "active"
|
|
30
|
+
},
|
|
31
|
+
profile: {
|
|
32
|
+
id: "profile-taylor",
|
|
33
|
+
name: "Taylor Rowan",
|
|
34
|
+
email: "taylor@example.com"
|
|
35
|
+
},
|
|
36
|
+
role: "Editor"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
user: {
|
|
40
|
+
id: "user-jordan",
|
|
41
|
+
email: "jordan@example.com",
|
|
42
|
+
status: "pending"
|
|
43
|
+
},
|
|
44
|
+
profile: {
|
|
45
|
+
id: "profile-jordan",
|
|
46
|
+
name: "Jordan Lee",
|
|
47
|
+
email: "jordan@example.com"
|
|
48
|
+
},
|
|
49
|
+
role: "Contributor"
|
|
50
|
+
}
|
|
51
|
+
];
|
|
52
|
+
const loadInviteUserModal = () => import("./svelte/components/InviteUserModal.svelte");
|
|
53
|
+
const loadUserForm = () => import("./svelte/components/UserForm.svelte");
|
|
54
|
+
const loadUserList = () => import("./svelte/components/UserList.svelte");
|
|
55
|
+
const loadUserMenu = () => import("./svelte/components/UserMenu.svelte");
|
|
56
|
+
const playground = {
|
|
57
|
+
packageName: "@happyvertical/smrt-users",
|
|
58
|
+
displayName: USERS_MODULE_META.displayName,
|
|
59
|
+
description: USERS_MODULE_META.description,
|
|
60
|
+
moduleMeta: USERS_MODULE_META,
|
|
61
|
+
entries: [
|
|
62
|
+
{
|
|
63
|
+
id: "user-list",
|
|
64
|
+
title: "User List",
|
|
65
|
+
description: "Role-aware list of users with selection and profile display.",
|
|
66
|
+
loadComponent: loadUserList,
|
|
67
|
+
order: 1,
|
|
68
|
+
props: {
|
|
69
|
+
users: sampleUsers,
|
|
70
|
+
selectedId: "user-taylor",
|
|
71
|
+
onselect: noop
|
|
72
|
+
},
|
|
73
|
+
modes: {
|
|
74
|
+
mock: {
|
|
75
|
+
label: "Mock"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "user-form",
|
|
81
|
+
title: "User Form",
|
|
82
|
+
description: "Create and edit form for user records and account status.",
|
|
83
|
+
loadComponent: loadUserForm,
|
|
84
|
+
order: 2,
|
|
85
|
+
props: {
|
|
86
|
+
user: sampleUsers[0].user,
|
|
87
|
+
profile: sampleUsers[0].profile,
|
|
88
|
+
onsubmit: noop,
|
|
89
|
+
oncancel: noop
|
|
90
|
+
},
|
|
91
|
+
modes: {
|
|
92
|
+
mock: {
|
|
93
|
+
label: "Mock"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "invite-user-modal",
|
|
99
|
+
title: "Invite User Modal",
|
|
100
|
+
description: "Invitation flow for adding a tenant member with a role assignment.",
|
|
101
|
+
loadComponent: loadInviteUserModal,
|
|
102
|
+
order: 3,
|
|
103
|
+
props: {
|
|
104
|
+
open: false,
|
|
105
|
+
tenant: sampleTenant,
|
|
106
|
+
roles: sampleRoles,
|
|
107
|
+
onsubmit: noop,
|
|
108
|
+
onclose: noop
|
|
109
|
+
},
|
|
110
|
+
modes: {
|
|
111
|
+
mock: {
|
|
112
|
+
label: "Mock"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "user-menu",
|
|
118
|
+
title: "User Menu",
|
|
119
|
+
description: "Authenticated account menu for profile, settings, and sign-out actions.",
|
|
120
|
+
loadComponent: loadUserMenu,
|
|
121
|
+
order: 4,
|
|
122
|
+
props: {
|
|
123
|
+
profile: sampleUsers[0].profile,
|
|
124
|
+
profileUrl: "/settings/profile",
|
|
125
|
+
settingsUrl: "/settings",
|
|
126
|
+
signoutUrl: "/auth/signout"
|
|
127
|
+
},
|
|
128
|
+
modes: {
|
|
129
|
+
mock: {
|
|
130
|
+
label: "Mock"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
};
|
|
136
|
+
export {
|
|
137
|
+
playground as default
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=playground.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playground.js","sources":["../src/svelte/playground.ts"],"sourcesContent":["import { USERS_MODULE_META } from '../ui.js';\n\nconst noop = () => {};\n\nconst sampleTenant = {\n id: 'tenant-riverstone',\n name: 'Riverstone Newsroom',\n slug: 'riverstone-newsroom',\n status: 'active',\n};\n\nconst sampleRoles = [\n {\n id: 'role-member',\n slug: 'member',\n name: 'Member',\n description: 'Can collaborate on editorial work.',\n },\n {\n id: 'role-editor',\n slug: 'editor',\n name: 'Editor',\n description: 'Can approve and publish content.',\n },\n];\n\nconst sampleUsers = [\n {\n user: {\n id: 'user-taylor',\n email: 'taylor@example.com',\n status: 'active',\n },\n profile: {\n id: 'profile-taylor',\n name: 'Taylor Rowan',\n email: 'taylor@example.com',\n },\n role: 'Editor',\n },\n {\n user: {\n id: 'user-jordan',\n email: 'jordan@example.com',\n status: 'pending',\n },\n profile: {\n id: 'profile-jordan',\n name: 'Jordan Lee',\n email: 'jordan@example.com',\n },\n role: 'Contributor',\n },\n];\n\nconst loadInviteUserModal = () => import('./components/InviteUserModal.svelte');\nconst loadUserForm = () => import('./components/UserForm.svelte');\nconst loadUserList = () => import('./components/UserList.svelte');\nconst loadUserMenu = () => import('./components/UserMenu.svelte');\n\nexport default {\n packageName: '@happyvertical/smrt-users',\n displayName: USERS_MODULE_META.displayName,\n description: USERS_MODULE_META.description,\n moduleMeta: USERS_MODULE_META,\n entries: [\n {\n id: 'user-list',\n title: 'User List',\n description:\n 'Role-aware list of users with selection and profile display.',\n loadComponent: loadUserList,\n order: 1,\n props: {\n users: sampleUsers,\n selectedId: 'user-taylor',\n onselect: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'user-form',\n title: 'User Form',\n description: 'Create and edit form for user records and account status.',\n loadComponent: loadUserForm,\n order: 2,\n props: {\n user: sampleUsers[0].user,\n profile: sampleUsers[0].profile,\n onsubmit: noop,\n oncancel: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'invite-user-modal',\n title: 'Invite User Modal',\n description:\n 'Invitation flow for adding a tenant member with a role assignment.',\n loadComponent: loadInviteUserModal,\n order: 3,\n props: {\n open: false,\n tenant: sampleTenant,\n roles: sampleRoles,\n onsubmit: noop,\n onclose: noop,\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n {\n id: 'user-menu',\n title: 'User Menu',\n description:\n 'Authenticated account menu for profile, settings, and sign-out actions.',\n loadComponent: loadUserMenu,\n order: 4,\n props: {\n profile: sampleUsers[0].profile,\n profileUrl: '/settings/profile',\n settingsUrl: '/settings',\n signoutUrl: '/auth/signout',\n },\n modes: {\n mock: {\n label: 'Mock',\n },\n },\n },\n ],\n};\n"],"names":[],"mappings":";AAEA,MAAM,OAAO,MAAM;AAAC;AAEpB,MAAM,eAAe;AAAA,EACnB,IAAI;AAAA,EACJ,MAAM;AAAA,EACN,MAAM;AAAA,EACN,QAAQ;AACV;AAEA,MAAM,cAAc;AAAA,EAClB;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,EAAA;AAAA,EAEf;AAAA,IACE,IAAI;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,IACN,aAAa;AAAA,EAAA;AAEjB;AAEA,MAAM,cAAc;AAAA,EAClB;AAAA,IACE,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA;AAAA,IAEV,SAAS;AAAA,MACP,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,IAAA;AAAA,IAET,MAAM;AAAA,EAAA;AAAA,EAER;AAAA,IACE,MAAM;AAAA,MACJ,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,QAAQ;AAAA,IAAA;AAAA,IAEV,SAAS;AAAA,MACP,IAAI;AAAA,MACJ,MAAM;AAAA,MACN,OAAO;AAAA,IAAA;AAAA,IAET,MAAM;AAAA,EAAA;AAEV;AAEA,MAAM,sBAAsB,MAAM,OAAO,4CAAqC;AAC9E,MAAM,eAAe,MAAM,OAAO,qCAA8B;AAChE,MAAM,eAAe,MAAM,OAAO,qCAA8B;AAChE,MAAM,eAAe,MAAM,OAAO,qCAA8B;AAEhE,MAAA,aAAe;AAAA,EACb,aAAa;AAAA,EACb,aAAa,kBAAkB;AAAA,EAC/B,aAAa,kBAAkB;AAAA,EAC/B,YAAY;AAAA,EACZ,SAAS;AAAA,IACP;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aACE;AAAA,MACF,eAAe;AAAA,MACf,OAAO;AAAA,MACP,OAAO;AAAA,QACL,OAAO;AAAA,QACP,YAAY;AAAA,QACZ,UAAU;AAAA,MAAA;AAAA,MAEZ,OAAO;AAAA,QACL,MAAM;AAAA,UACJ,OAAO;AAAA,QAAA;AAAA,MACT;AAAA,IACF;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAe;AAAA,MACf,OAAO;AAAA,MACP,OAAO;AAAA,QACL,MAAM,YAAY,CAAC,EAAE;AAAA,QACrB,SAAS,YAAY,CAAC,EAAE;AAAA,QACxB,UAAU;AAAA,QACV,UAAU;AAAA,MAAA;AAAA,MAEZ,OAAO;AAAA,QACL,MAAM;AAAA,UACJ,OAAO;AAAA,QAAA;AAAA,MACT;AAAA,IACF;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aACE;AAAA,MACF,eAAe;AAAA,MACf,OAAO;AAAA,MACP,OAAO;AAAA,QACL,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,OAAO;AAAA,QACP,UAAU;AAAA,QACV,SAAS;AAAA,MAAA;AAAA,MAEX,OAAO;AAAA,QACL,MAAM;AAAA,UACJ,OAAO;AAAA,QAAA;AAAA,MACT;AAAA,IACF;AAAA,IAEF;AAAA,MACE,IAAI;AAAA,MACJ,OAAO;AAAA,MACP,aACE;AAAA,MACF,eAAe;AAAA,MACf,OAAO;AAAA,MACP,OAAO;AAAA,QACL,SAAS,YAAY,CAAC,EAAE;AAAA,QACxB,YAAY;AAAA,QACZ,aAAa;AAAA,QACb,YAAY;AAAA,MAAA;AAAA,MAEd,OAAO;AAAA,QACL,MAAM;AAAA,UACJ,OAAO;AAAA,QAAA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEJ;"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
/**
|
|
3
|
+
* Options for MagicLinkService
|
|
4
|
+
*/
|
|
5
|
+
export interface MagicLinkServiceOptions extends SmrtClassOptions {
|
|
6
|
+
/** Secret used to derive the HMAC signing key (required) */
|
|
7
|
+
secret: string;
|
|
8
|
+
/** Token expiry in seconds (default: 10 minutes) */
|
|
9
|
+
tokenExpiry?: number;
|
|
10
|
+
/** JWT issuer claim (default: 'smrt:magiclink') */
|
|
11
|
+
issuer?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Result of generating a magic link token
|
|
15
|
+
*/
|
|
16
|
+
export interface MagicLinkResult {
|
|
17
|
+
/** The signed JWT token */
|
|
18
|
+
token: string;
|
|
19
|
+
/** When the token expires */
|
|
20
|
+
expiresAt: Date;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Result of verifying a magic link token
|
|
24
|
+
*/
|
|
25
|
+
export interface MagicLinkVerifyResult {
|
|
26
|
+
/** The email address from the token */
|
|
27
|
+
email: string;
|
|
28
|
+
/** The nonce (for correlation/logging) */
|
|
29
|
+
nonce: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Error class for magic link authentication failures
|
|
33
|
+
*/
|
|
34
|
+
export declare class MagicLinkError extends Error {
|
|
35
|
+
constructor(message: string);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* MagicLinkService provides passwordless authentication via email magic links.
|
|
39
|
+
*
|
|
40
|
+
* Tokens are HMAC-signed JWTs with embedded nonces for replay protection.
|
|
41
|
+
* The service is framework-agnostic — it does not send emails or set cookies.
|
|
42
|
+
*/
|
|
43
|
+
export declare class MagicLinkService {
|
|
44
|
+
private tokenCollection;
|
|
45
|
+
private signingKey;
|
|
46
|
+
private readonly secret;
|
|
47
|
+
private readonly tokenExpiry;
|
|
48
|
+
private readonly issuer;
|
|
49
|
+
private readonly options;
|
|
50
|
+
constructor(options: MagicLinkServiceOptions);
|
|
51
|
+
/**
|
|
52
|
+
* Initialize collections
|
|
53
|
+
*/
|
|
54
|
+
initialize(): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Derive the HMAC signing key from the secret
|
|
57
|
+
*/
|
|
58
|
+
private getSigningKey;
|
|
59
|
+
/**
|
|
60
|
+
* Generate a magic link token for the given email.
|
|
61
|
+
*
|
|
62
|
+
* Stores a nonce in the database for replay protection.
|
|
63
|
+
* The caller is responsible for emailing the token to the user.
|
|
64
|
+
*/
|
|
65
|
+
generate(email: string): Promise<MagicLinkResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Verify a magic link token.
|
|
68
|
+
*
|
|
69
|
+
* Checks JWT signature, expiry, and that the nonce hasn't been used.
|
|
70
|
+
* Marks the nonce as used on success (single-use enforcement).
|
|
71
|
+
*
|
|
72
|
+
* @throws {MagicLinkError} If the token is invalid, expired, or already used
|
|
73
|
+
*/
|
|
74
|
+
verify(token: string): Promise<MagicLinkVerifyResult>;
|
|
75
|
+
/**
|
|
76
|
+
* Clean up expired tokens (run periodically)
|
|
77
|
+
*/
|
|
78
|
+
cleanupExpiredTokens(): Promise<number>;
|
|
79
|
+
/**
|
|
80
|
+
* Static factory method
|
|
81
|
+
*/
|
|
82
|
+
static create(options: MagicLinkServiceOptions): Promise<MagicLinkService>;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=MagicLinkService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MagicLinkService.d.ts","sourceRoot":"","sources":["../../src/services/MagicLinkService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAKjE;;GAEG;AACH,MAAM,WAAW,uBAAwB,SAAQ,gBAAgB;IAC/D,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,eAAe,CAAiC;IACxD,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0B;gBAEtC,OAAO,EAAE,uBAAuB;IAU5C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAMjC;;OAEG;YACW,aAAa;IAW3B;;;;;OAKG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAoCvD;;;;;;;OAOG;IACG,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAuC3D;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7C;;OAEG;WACU,MAAM,CACjB,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;CAK7B"}
|