@spfn/auth 0.2.0-beta.41 → 0.2.0-beta.42

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/README.md CHANGED
@@ -1092,6 +1092,8 @@ Update authenticated user's username. Validates uniqueness before updating.
1092
1092
  |-------|-------------|---------|
1093
1093
  | `auth.login` | 로그인 성공 | 이메일/전화 로그인, OAuth 기존 사용자 |
1094
1094
  | `auth.register` | 회원가입 성공 | 이메일/전화 회원가입, OAuth 신규 사용자 |
1095
+ | `auth.invitation.created` | 초대 생성/재발송 | createInvitation, resendInvitation |
1096
+ | `auth.invitation.accepted` | 초대 수락 | acceptInvitation |
1095
1097
 
1096
1098
  ---
1097
1099
 
@@ -1123,6 +1125,34 @@ Update authenticated user's username. Validates uniqueness before updating.
1123
1125
  `metadata`는 클라이언트가 register/OAuth 요청 body에 포함한 값이 그대로 전달됩니다.
1124
1126
  레퍼럴 코드, UTM 파라미터 등 앱 고유 데이터를 이벤트 구독자에게 전달할 때 사용합니다.
1125
1127
 
1128
+ #### `auth.invitation.created`
1129
+
1130
+ ```typescript
1131
+ {
1132
+ invitationId: string;
1133
+ email: string;
1134
+ token: string;
1135
+ roleId: number;
1136
+ invitedBy: string;
1137
+ expiresAt: string; // ISO 8601
1138
+ isResend: boolean; // true면 재발송
1139
+ metadata?: Record<string, unknown>;
1140
+ }
1141
+ ```
1142
+
1143
+ #### `auth.invitation.accepted`
1144
+
1145
+ ```typescript
1146
+ {
1147
+ invitationId: string;
1148
+ email: string;
1149
+ userId: string; // 생성된 사용자 ID
1150
+ roleId: number;
1151
+ invitedBy: string;
1152
+ metadata?: Record<string, unknown>;
1153
+ }
1154
+ ```
1155
+
1126
1156
  ---
1127
1157
 
1128
1158
  ### Subscribing to Events
@@ -1165,6 +1195,51 @@ authApi.oauthStart.call({
1165
1195
  });
1166
1196
  ```
1167
1197
 
1198
+ #### 초대 이벤트 구독 (이메일 발송 연동)
1199
+
1200
+ ```typescript
1201
+ import { invitationCreatedEvent, invitationAcceptedEvent } from '@spfn/auth/server';
1202
+
1203
+ // 초대 생성 시 이메일 발송
1204
+ invitationCreatedEvent.subscribe(async (payload) => {
1205
+ const inviteUrl = `${APP_URL}/invite/${payload.token}`;
1206
+
1207
+ await notificationService.send({
1208
+ channel: 'email',
1209
+ to: payload.email,
1210
+ subject: payload.isResend ? '초대가 재발송되었습니다' : '초대장이 도착했습니다',
1211
+ html: renderInviteEmail({
1212
+ inviteUrl,
1213
+ inviterName: payload.metadata?.inviterName,
1214
+ message: payload.metadata?.message,
1215
+ }),
1216
+ tracking: {
1217
+ category: 'invitation',
1218
+ metadata: { invitationId: payload.invitationId },
1219
+ },
1220
+ });
1221
+ });
1222
+
1223
+ // 초대 수락 시 온보딩 처리
1224
+ invitationAcceptedEvent.subscribe(async (payload) => {
1225
+ await onboardingService.start(payload.userId);
1226
+ });
1227
+ ```
1228
+
1229
+ 초대 생성 시 커스텀 만료 시간 지정:
1230
+
1231
+ ```typescript
1232
+ // expiresAt이 expiresInDays보다 우선
1233
+ authApi.createInvitation.call({
1234
+ body: {
1235
+ email: 'user@example.com',
1236
+ roleId: 2,
1237
+ expiresAt: '2026-03-20T00:00:00Z',
1238
+ metadata: { inviterName: '홍길동', message: '함께 일해요!' },
1239
+ }
1240
+ });
1241
+ ```
1242
+
1168
1243
  ---
1169
1244
 
1170
1245
  ### Job Integration
@@ -633,6 +633,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
633
633
  email: _sinclair_typebox.TString;
634
634
  roleId: _sinclair_typebox.TNumber;
635
635
  expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
636
+ expiresAt: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
636
637
  metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TAny>;
637
638
  }>;
638
639
  }, {}, {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _spfn_core_nextjs from '@spfn/core/nextjs';
2
- import { R as RoleConfig, P as PermissionConfig, C as CheckAccountExistsResult, S as SendVerificationCodeResult, a as RegisterResult, L as LoginResult, b as RotateKeyResult, O as OAuthStartResult, U as UserProfile, c as ProfileInfo, m as mainAuthRouter } from './authenticate-2953PCm8.js';
3
- export { k as AuthInitOptions, A as AuthSession, I as INVITATION_STATUSES, n as InvitationStatus, K as KEY_ALGORITHM, l as KeyAlgorithmType, i as PERMISSION_CATEGORIES, j as PermissionCategory, e as SOCIAL_PROVIDERS, p as SocialProvider, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, f as VerificationPurpose, V as VerificationTargetType } from './authenticate-2953PCm8.js';
2
+ import { R as RoleConfig, P as PermissionConfig, C as CheckAccountExistsResult, S as SendVerificationCodeResult, a as RegisterResult, L as LoginResult, b as RotateKeyResult, O as OAuthStartResult, U as UserProfile, c as ProfileInfo, m as mainAuthRouter } from './authenticate-gnTzrnU-.js';
3
+ export { k as AuthInitOptions, A as AuthSession, I as INVITATION_STATUSES, n as InvitationStatus, K as KEY_ALGORITHM, l as KeyAlgorithmType, i as PERMISSION_CATEGORIES, j as PermissionCategory, e as SOCIAL_PROVIDERS, p as SocialProvider, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, f as VerificationPurpose, V as VerificationTargetType } from './authenticate-gnTzrnU-.js';
4
4
  import * as _spfn_core_route from '@spfn/core/route';
5
5
  import { HttpMethod } from '@spfn/core/route';
6
6
  import * as _sinclair_typebox from '@sinclair/typebox';
@@ -257,6 +257,7 @@ declare const authApi: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
257
257
  email: _sinclair_typebox.TString;
258
258
  roleId: _sinclair_typebox.TNumber;
259
259
  expiresInDays: _sinclair_typebox.TOptional<_sinclair_typebox.TNumber>;
260
+ expiresAt: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
260
261
  metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TAny>;
261
262
  }>;
262
263
  }, {}, {
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { k as AuthInitOptions, l as KeyAlgorithmType, n as InvitationStatus, f as VerificationPurpose, j as PermissionCategory, p as SocialProvider, q as AuthContext } from './authenticate-2953PCm8.js';
2
- export { B as ChangePasswordParams, w as CheckAccountExistsParams, C as CheckAccountExistsResult, a6 as EmailSchema, I as INVITATION_STATUSES, K as KEY_ALGORITHM, y as LoginParams, L as LoginResult, z as LogoutParams, a2 as OAuthCallbackParams, a3 as OAuthCallbackResult, a1 as OAuthStartParams, O as OAuthStartResult, a8 as PasswordSchema, a7 as PhoneSchema, x as RegisterParams, Q as RegisterPublicKeyParams, a as RegisterResult, W as RevokeKeyParams, T as RotateKeyParams, b as RotateKeyResult, e as SOCIAL_PROVIDERS, F as SendVerificationCodeParams, S as SendVerificationCodeResult, a9 as TargetTypeSchema, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, aa as VerificationPurposeSchema, V as VerificationTargetType, G as VerifyCodeParams, H as VerifyCodeResult, m as authRouter, a4 as authenticate, Z as buildOAuthErrorUrl, v as changePasswordService, r as checkAccountExistsService, $ as getEnabledOAuthProviders, a0 as getGoogleAccessToken, _ as isOAuthProviderEnabled, t as loginService, u as logoutService, Y as oauthCallbackService, X as oauthStartService, a5 as optionalAuth, J as registerPublicKeyService, s as registerService, N as revokeKeyService, M as rotateKeyService, D as sendVerificationCodeService, E as verifyCodeService } from './authenticate-2953PCm8.js';
1
+ import { k as AuthInitOptions, l as KeyAlgorithmType, n as InvitationStatus, f as VerificationPurpose, j as PermissionCategory, p as SocialProvider, q as AuthContext } from './authenticate-gnTzrnU-.js';
2
+ export { B as ChangePasswordParams, w as CheckAccountExistsParams, C as CheckAccountExistsResult, a6 as EmailSchema, I as INVITATION_STATUSES, K as KEY_ALGORITHM, y as LoginParams, L as LoginResult, z as LogoutParams, a2 as OAuthCallbackParams, a3 as OAuthCallbackResult, a1 as OAuthStartParams, O as OAuthStartResult, a8 as PasswordSchema, a7 as PhoneSchema, x as RegisterParams, Q as RegisterPublicKeyParams, a as RegisterResult, W as RevokeKeyParams, T as RotateKeyParams, b as RotateKeyResult, e as SOCIAL_PROVIDERS, F as SendVerificationCodeParams, S as SendVerificationCodeResult, a9 as TargetTypeSchema, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, aa as VerificationPurposeSchema, V as VerificationTargetType, G as VerifyCodeParams, H as VerifyCodeResult, m as authRouter, a4 as authenticate, Z as buildOAuthErrorUrl, v as changePasswordService, r as checkAccountExistsService, $ as getEnabledOAuthProviders, a0 as getGoogleAccessToken, _ as isOAuthProviderEnabled, t as loginService, u as logoutService, Y as oauthCallbackService, X as oauthStartService, a5 as optionalAuth, J as registerPublicKeyService, s as registerService, N as revokeKeyService, M as rotateKeyService, D as sendVerificationCodeService, E as verifyCodeService } from './authenticate-gnTzrnU-.js';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
4
  import { UserProfile as UserProfile$1, ProfileInfo } from '@spfn/auth';
5
5
  import { BaseRepository } from '@spfn/core/db';
@@ -1082,6 +1082,7 @@ declare function createInvitation(params: {
1082
1082
  roleId: number;
1083
1083
  invitedBy: number;
1084
1084
  expiresInDays?: number;
1085
+ expiresAt?: Date;
1085
1086
  metadata?: Record<string, any>;
1086
1087
  }): Promise<Invitation>;
1087
1088
  /**
@@ -2942,10 +2943,10 @@ declare class UsersRepository extends BaseRepository {
2942
2943
  * Write primary 사용
2943
2944
  */
2944
2945
  create(data: NewUser): Promise<{
2946
+ username: string | null;
2947
+ status: "active" | "inactive" | "suspended";
2945
2948
  email: string | null;
2946
2949
  phone: string | null;
2947
- status: "active" | "inactive" | "suspended";
2948
- username: string | null;
2949
2950
  id: number;
2950
2951
  createdAt: Date;
2951
2952
  updatedAt: Date;
@@ -3018,10 +3019,10 @@ declare class UsersRepository extends BaseRepository {
3018
3019
  * Write primary 사용
3019
3020
  */
3020
3021
  deleteById(id: number): Promise<{
3022
+ username: string | null;
3023
+ status: "active" | "inactive" | "suspended";
3021
3024
  email: string | null;
3022
3025
  phone: string | null;
3023
- status: "active" | "inactive" | "suspended";
3024
- username: string | null;
3025
3026
  id: number;
3026
3027
  createdAt: Date;
3027
3028
  updatedAt: Date;
@@ -4093,8 +4094,8 @@ declare class InvitationsRepository extends BaseRepository {
4093
4094
  * 초대 생성
4094
4095
  */
4095
4096
  create(data: NewInvitation): Promise<{
4096
- email: string;
4097
4097
  status: "pending" | "accepted" | "expired" | "cancelled";
4098
+ email: string;
4098
4099
  id: number;
4099
4100
  createdAt: Date;
4100
4101
  updatedAt: Date;
@@ -4127,8 +4128,8 @@ declare class InvitationsRepository extends BaseRepository {
4127
4128
  * 초대 삭제
4128
4129
  */
4129
4130
  deleteById(id: number): Promise<{
4130
- email: string;
4131
4131
  status: "pending" | "accepted" | "expired" | "cancelled";
4132
+ email: string;
4132
4133
  id: number;
4133
4134
  createdAt: Date;
4134
4135
  updatedAt: Date;
@@ -4801,10 +4802,10 @@ declare function getOptionalAuth(c: Context | {
4801
4802
  declare function getUser(c: Context | {
4802
4803
  raw: Context;
4803
4804
  }): {
4805
+ username: string | null;
4806
+ status: "active" | "inactive" | "suspended";
4804
4807
  email: string | null;
4805
4808
  phone: string | null;
4806
- status: "active" | "inactive" | "suspended";
4807
- username: string | null;
4808
4809
  id: number;
4809
4810
  createdAt: Date;
4810
4811
  updatedAt: Date;
@@ -5314,10 +5315,67 @@ declare const authRegisterEvent: _spfn_core_event.EventDef<{
5314
5315
  userId: string;
5315
5316
  provider: "email" | "phone" | "google";
5316
5317
  }>;
5318
+ /**
5319
+ * auth.invitation.created - 초대 생성 이벤트
5320
+ *
5321
+ * 발행 시점:
5322
+ * - createInvitation() 성공 시
5323
+ * - resendInvitation() 성공 시
5324
+ *
5325
+ * @example
5326
+ * ```typescript
5327
+ * invitationCreatedEvent.subscribe(async (payload) => {
5328
+ * const inviteUrl = `${APP_URL}/invite/${payload.token}`;
5329
+ * await notificationService.send({
5330
+ * channel: 'email',
5331
+ * to: payload.email,
5332
+ * subject: 'You are invited!',
5333
+ * html: renderInviteEmail({ inviteUrl, ...payload.metadata }),
5334
+ * });
5335
+ * });
5336
+ * ```
5337
+ */
5338
+ declare const invitationCreatedEvent: _spfn_core_event.EventDef<{
5339
+ metadata?: {
5340
+ [x: string]: unknown;
5341
+ } | undefined;
5342
+ email: string;
5343
+ roleId: number;
5344
+ expiresAt: string;
5345
+ token: string;
5346
+ invitedBy: string;
5347
+ invitationId: string;
5348
+ isResend: boolean;
5349
+ }>;
5350
+ /**
5351
+ * auth.invitation.accepted - 초대 수락 이벤트
5352
+ *
5353
+ * 발행 시점:
5354
+ * - acceptInvitation() 성공 시
5355
+ *
5356
+ * @example
5357
+ * ```typescript
5358
+ * invitationAcceptedEvent.subscribe(async (payload) => {
5359
+ * await onboardingService.start(payload.userId);
5360
+ * });
5361
+ * ```
5362
+ */
5363
+ declare const invitationAcceptedEvent: _spfn_core_event.EventDef<{
5364
+ metadata?: {
5365
+ [x: string]: unknown;
5366
+ } | undefined;
5367
+ email: string;
5368
+ userId: string;
5369
+ roleId: number;
5370
+ invitedBy: string;
5371
+ invitationId: string;
5372
+ }>;
5317
5373
  /**
5318
5374
  * Auth event payload types
5319
5375
  */
5320
5376
  type AuthLoginPayload = typeof authLoginEvent._payload;
5321
5377
  type AuthRegisterPayload = typeof authRegisterEvent._payload;
5378
+ type InvitationCreatedPayload = typeof invitationCreatedEvent._payload;
5379
+ type InvitationAcceptedPayload = typeof invitationAcceptedEvent._payload;
5322
5380
 
5323
- export { type AuthConfig, AuthContext, type AuthLoginPayload, AuthProviderSchema, type AuthRegisterPayload, COOKIE_NAMES, type CreateOAuthStateParams, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, InvitationStatus, InvitationsRepository, KeyAlgorithmType, type KeyPair, KeysRepository, type NewInvitation, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type OAuthState, type Permission, type PermissionEntity, PermissionsRepository, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionData, type SessionPayload, SocialAccountsRepository, SocialProvider, type TokenPayload, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, acceptInvitation, addPermissionToRole, authLogger, authLoginEvent, authRegisterEvent, authSchema, cancelInvitation, checkUsernameAvailableService, configureAuth, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, deleteInvitation, deleteRole, exchangeCodeForTokens, expireOldInvitations, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getKeySize, getLocale, getOptionalAuth, getRole, getRoleByName, getRolePermissions, getSessionInfo, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashPassword, initializeAuth, invitationsRepository, isGoogleOAuthEnabled, keysRepository, listInvitations, parseDuration, permissions, permissionsRepository, refreshAccessToken, removePermissionFromRole, requireAnyPermission, requirePermissions, requireRole, resendInvitation, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, sealSession, setRolePermissions, shouldRefreshSession, shouldRotateKey, socialAccountsRepository, unsealSession, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyKeyFingerprint, verifyOAuthState, verifyPassword, verifyToken };
5381
+ export { type AuthConfig, AuthContext, type AuthLoginPayload, AuthProviderSchema, type AuthRegisterPayload, COOKIE_NAMES, type CreateOAuthStateParams, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, KeyAlgorithmType, type KeyPair, KeysRepository, type NewInvitation, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type OAuthState, type Permission, type PermissionEntity, PermissionsRepository, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionData, type SessionPayload, SocialAccountsRepository, SocialProvider, type TokenPayload, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, acceptInvitation, addPermissionToRole, authLogger, authLoginEvent, authRegisterEvent, authSchema, cancelInvitation, checkUsernameAvailableService, configureAuth, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, deleteInvitation, deleteRole, exchangeCodeForTokens, expireOldInvitations, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getKeySize, getLocale, getOptionalAuth, getRole, getRoleByName, getRolePermissions, getSessionInfo, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashPassword, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isGoogleOAuthEnabled, keysRepository, listInvitations, parseDuration, permissions, permissionsRepository, refreshAccessToken, removePermissionFromRole, requireAnyPermission, requirePermissions, requireRole, resendInvitation, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, sealSession, setRolePermissions, shouldRefreshSession, shouldRotateKey, socialAccountsRepository, unsealSession, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyKeyFingerprint, verifyOAuthState, verifyPassword, verifyToken };
package/dist/server.js CHANGED
@@ -7153,6 +7153,30 @@ var authRegisterEvent = defineEvent(
7153
7153
  metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
7154
7154
  })
7155
7155
  );
7156
+ var invitationCreatedEvent = defineEvent(
7157
+ "auth.invitation.created",
7158
+ Type.Object({
7159
+ invitationId: Type.String(),
7160
+ email: Type.String(),
7161
+ token: Type.String(),
7162
+ roleId: Type.Number(),
7163
+ invitedBy: Type.String(),
7164
+ expiresAt: Type.String(),
7165
+ isResend: Type.Boolean(),
7166
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
7167
+ })
7168
+ );
7169
+ var invitationAcceptedEvent = defineEvent(
7170
+ "auth.invitation.accepted",
7171
+ Type.Object({
7172
+ invitationId: Type.String(),
7173
+ email: Type.String(),
7174
+ userId: Type.String(),
7175
+ roleId: Type.Number(),
7176
+ invitedBy: Type.String(),
7177
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
7178
+ })
7179
+ );
7156
7180
 
7157
7181
  // src/server/services/auth.service.ts
7158
7182
  async function checkAccountExistsService(params) {
@@ -7566,7 +7590,7 @@ function calculateExpiresAt(days = 7) {
7566
7590
  return expiresAt;
7567
7591
  }
7568
7592
  async function createInvitation(params) {
7569
- const { email, roleId, invitedBy, expiresInDays = 7, metadata } = params;
7593
+ const { email, roleId, invitedBy, expiresInDays = 7, expiresAt: expiresAtParam, metadata } = params;
7570
7594
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
7571
7595
  if (!emailRegex.test(email)) {
7572
7596
  throw new Error("Invalid email format");
@@ -7588,7 +7612,7 @@ async function createInvitation(params) {
7588
7612
  throw new Error(`User with id ${invitedBy} not found`);
7589
7613
  }
7590
7614
  const token = generateInvitationToken();
7591
- const expiresAt = calculateExpiresAt(expiresInDays);
7615
+ const expiresAt = expiresAtParam ?? calculateExpiresAt(expiresInDays);
7592
7616
  const invitation = await invitationsRepository.create({
7593
7617
  email,
7594
7618
  token,
@@ -7598,7 +7622,16 @@ async function createInvitation(params) {
7598
7622
  expiresAt,
7599
7623
  metadata: metadata || null
7600
7624
  });
7601
- console.log(`[Auth] \u2705 Created invitation: ${email} as ${role.name} (expires: ${expiresAt.toISOString()})`);
7625
+ await invitationCreatedEvent.emit({
7626
+ invitationId: String(invitation.id),
7627
+ email,
7628
+ token,
7629
+ roleId,
7630
+ invitedBy: String(invitedBy),
7631
+ expiresAt: expiresAt.toISOString(),
7632
+ isResend: false,
7633
+ metadata
7634
+ });
7602
7635
  return invitation;
7603
7636
  }
7604
7637
  async function getInvitationByToken(token) {
@@ -7662,7 +7695,14 @@ async function acceptInvitation(params) {
7662
7695
  "accepted",
7663
7696
  /* @__PURE__ */ new Date()
7664
7697
  );
7665
- console.log(`[Auth] \u2705 Invitation accepted: ${invitation.email} as ${role.name}`);
7698
+ await invitationAcceptedEvent.emit({
7699
+ invitationId: String(invitation.id),
7700
+ email: invitation.email,
7701
+ userId: String(newUser.id),
7702
+ roleId: Number(invitation.roleId),
7703
+ invitedBy: String(invitation.invitedBy),
7704
+ metadata: invitation.metadata
7705
+ });
7666
7706
  return {
7667
7707
  userId: newUser.id,
7668
7708
  email: newUser.email,
@@ -7707,7 +7747,16 @@ async function resendInvitation(id11, expiresInDays = 7) {
7707
7747
  if (!updated) {
7708
7748
  throw new Error("Failed to update invitation");
7709
7749
  }
7710
- console.log(`[Auth] \u{1F4E7} Invitation resent: ${invitation.email} (new expiry: ${newExpiresAt.toISOString()})`);
7750
+ await invitationCreatedEvent.emit({
7751
+ invitationId: String(invitation.id),
7752
+ email: invitation.email,
7753
+ token: invitation.token,
7754
+ roleId: Number(invitation.roleId),
7755
+ invitedBy: String(invitation.invitedBy),
7756
+ expiresAt: newExpiresAt.toISOString(),
7757
+ isResend: true,
7758
+ metadata: invitation.metadata
7759
+ });
7711
7760
  return updated;
7712
7761
  }
7713
7762
 
@@ -8675,6 +8724,10 @@ var createInvitation2 = route2.post("/_auth/invitations").input({
8675
8724
  maximum: 30,
8676
8725
  description: "Days until invitation expires (default: 7)"
8677
8726
  })),
8727
+ expiresAt: Type.Optional(Type.String({
8728
+ format: "date-time",
8729
+ description: "Exact expiration timestamp (ISO 8601). Takes precedence over expiresInDays."
8730
+ })),
8678
8731
  metadata: Type.Optional(Type.Any({
8679
8732
  description: "Custom metadata (welcome message, department, etc.)"
8680
8733
  }))
@@ -8687,6 +8740,7 @@ var createInvitation2 = route2.post("/_auth/invitations").input({
8687
8740
  roleId: body.roleId,
8688
8741
  invitedBy: Number(userId),
8689
8742
  expiresInDays: body.expiresInDays,
8743
+ expiresAt: body.expiresAt ? new Date(body.expiresAt) : void 0,
8690
8744
  metadata: body.metadata
8691
8745
  });
8692
8746
  const baseUrl = process.env.SPFN_API_URL || "http://localhost:8790";
@@ -9507,6 +9561,8 @@ export {
9507
9561
  hasRole,
9508
9562
  hashPassword,
9509
9563
  initializeAuth,
9564
+ invitationAcceptedEvent,
9565
+ invitationCreatedEvent,
9510
9566
  invitationsRepository,
9511
9567
  isGoogleOAuthEnabled,
9512
9568
  isOAuthProviderEnabled,