@spfn/auth 0.1.0-alpha.1 → 0.1.0-alpha.86

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 (124) hide show
  1. package/README.md +250 -0
  2. package/dist/adapters/nextjs/api.d.ts +446 -0
  3. package/dist/adapters/nextjs/api.js +3279 -0
  4. package/dist/adapters/nextjs/api.js.map +1 -0
  5. package/dist/adapters/nextjs/server.d.ts +246 -0
  6. package/dist/adapters/nextjs/server.js +3645 -0
  7. package/dist/adapters/nextjs/server.js.map +1 -0
  8. package/dist/index.d.ts +3 -46
  9. package/dist/index.js +777 -645
  10. package/dist/index.js.map +1 -1
  11. package/dist/lib/api/auth-codes-verify.d.ts +37 -0
  12. package/dist/lib/api/auth-codes-verify.js +2949 -0
  13. package/dist/lib/api/auth-codes-verify.js.map +1 -0
  14. package/dist/lib/api/auth-codes.d.ts +37 -0
  15. package/dist/lib/api/auth-codes.js +2949 -0
  16. package/dist/lib/api/auth-codes.js.map +1 -0
  17. package/dist/lib/api/auth-exists.d.ts +38 -0
  18. package/dist/lib/api/auth-exists.js +2949 -0
  19. package/dist/lib/api/auth-exists.js.map +1 -0
  20. package/dist/lib/api/auth-invitations-accept.d.ts +38 -0
  21. package/dist/lib/api/auth-invitations-accept.js +2883 -0
  22. package/dist/lib/api/auth-invitations-accept.js.map +1 -0
  23. package/dist/lib/api/auth-invitations-cancel.d.ts +37 -0
  24. package/dist/lib/api/auth-invitations-cancel.js +2883 -0
  25. package/dist/lib/api/auth-invitations-cancel.js.map +1 -0
  26. package/dist/lib/api/auth-invitations-delete.d.ts +36 -0
  27. package/dist/lib/api/auth-invitations-delete.js +2883 -0
  28. package/dist/lib/api/auth-invitations-delete.js.map +1 -0
  29. package/dist/lib/api/auth-invitations-resend.d.ts +37 -0
  30. package/dist/lib/api/auth-invitations-resend.js +2883 -0
  31. package/dist/lib/api/auth-invitations-resend.js.map +1 -0
  32. package/dist/lib/api/auth-invitations.d.ts +109 -0
  33. package/dist/lib/api/auth-invitations.js +2887 -0
  34. package/dist/lib/api/auth-invitations.js.map +1 -0
  35. package/dist/lib/api/auth-keys-rotate.d.ts +37 -0
  36. package/dist/lib/api/auth-keys-rotate.js +2949 -0
  37. package/dist/lib/api/auth-keys-rotate.js.map +1 -0
  38. package/dist/lib/api/auth-login.d.ts +39 -0
  39. package/dist/lib/api/auth-login.js +2949 -0
  40. package/dist/lib/api/auth-login.js.map +1 -0
  41. package/dist/lib/api/auth-logout.d.ts +36 -0
  42. package/dist/lib/api/auth-logout.js +2949 -0
  43. package/dist/lib/api/auth-logout.js.map +1 -0
  44. package/dist/lib/api/auth-me.d.ts +50 -0
  45. package/dist/lib/api/auth-me.js +2949 -0
  46. package/dist/lib/api/auth-me.js.map +1 -0
  47. package/dist/lib/api/auth-password.d.ts +36 -0
  48. package/dist/lib/api/auth-password.js +2949 -0
  49. package/dist/lib/api/auth-password.js.map +1 -0
  50. package/dist/lib/api/auth-register.d.ts +38 -0
  51. package/dist/lib/api/auth-register.js +2949 -0
  52. package/dist/lib/api/auth-register.js.map +1 -0
  53. package/dist/lib/api/index.d.ts +356 -0
  54. package/dist/lib/api/index.js +3261 -0
  55. package/dist/lib/api/index.js.map +1 -0
  56. package/dist/lib/config.d.ts +70 -0
  57. package/dist/lib/config.js +64 -0
  58. package/dist/lib/config.js.map +1 -0
  59. package/dist/lib/contracts/auth.d.ts +41 -1
  60. package/dist/lib/contracts/auth.js +28 -0
  61. package/dist/lib/contracts/auth.js.map +1 -1
  62. package/dist/lib/contracts/index.d.ts +1 -1
  63. package/dist/lib/contracts/index.js +28 -0
  64. package/dist/lib/contracts/index.js.map +1 -1
  65. package/dist/lib/crypto.d.ts +76 -0
  66. package/dist/lib/crypto.js +127 -0
  67. package/dist/lib/crypto.js.map +1 -0
  68. package/dist/lib/index.d.ts +4 -0
  69. package/dist/lib/index.js +313 -0
  70. package/dist/lib/index.js.map +1 -0
  71. package/dist/lib/session.d.ts +68 -0
  72. package/dist/lib/session.js +126 -0
  73. package/dist/lib/session.js.map +1 -0
  74. package/dist/{api-BcQM4WKb.d.ts → lib/types/api.d.ts} +2 -2
  75. package/dist/lib/types/api.js +1 -0
  76. package/dist/lib/types/api.js.map +1 -0
  77. package/dist/lib/types/index.d.ts +3 -0
  78. package/dist/lib/types/index.js +2647 -0
  79. package/dist/lib/types/index.js.map +1 -0
  80. package/dist/lib/types/schemas.d.ts +45 -0
  81. package/dist/lib/types/schemas.js +2647 -0
  82. package/dist/lib/types/schemas.js.map +1 -0
  83. package/dist/lib.d.ts +2 -0
  84. package/dist/lib.js +1 -0
  85. package/dist/lib.js.map +1 -0
  86. package/dist/plugin.js +777 -645
  87. package/dist/plugin.js.map +1 -1
  88. package/dist/server/entities/index.d.ts +1 -0
  89. package/dist/server/entities/index.js +23 -27
  90. package/dist/server/entities/index.js.map +1 -1
  91. package/dist/server/entities/invitations.js +12 -9
  92. package/dist/server/entities/invitations.js.map +1 -1
  93. package/dist/server/entities/permissions.js +8 -3
  94. package/dist/server/entities/permissions.js.map +1 -1
  95. package/dist/server/entities/role-permissions.js +12 -9
  96. package/dist/server/entities/role-permissions.js.map +1 -1
  97. package/dist/server/entities/roles.js +8 -3
  98. package/dist/server/entities/roles.js.map +1 -1
  99. package/dist/server/entities/schema.d.ts +14 -0
  100. package/dist/server/entities/schema.js +7 -0
  101. package/dist/server/entities/schema.js.map +1 -0
  102. package/dist/server/entities/user-permissions.js +14 -12
  103. package/dist/server/entities/user-permissions.js.map +1 -1
  104. package/dist/server/entities/user-public-keys.js +12 -9
  105. package/dist/server/entities/user-public-keys.js.map +1 -1
  106. package/dist/server/entities/user-social-accounts.js +12 -9
  107. package/dist/server/entities/user-social-accounts.js.map +1 -1
  108. package/dist/server/entities/users.js +10 -6
  109. package/dist/server/entities/users.js.map +1 -1
  110. package/dist/server/entities/verification-codes.js +8 -3
  111. package/dist/server/entities/verification-codes.js.map +1 -1
  112. package/dist/server/routes/auth/index.js +495 -512
  113. package/dist/server/routes/auth/index.js.map +1 -1
  114. package/dist/server/routes/index.js +775 -545
  115. package/dist/server/routes/index.js.map +1 -1
  116. package/dist/server/routes/invitations/index.js +416 -230
  117. package/dist/server/routes/invitations/index.js.map +1 -1
  118. package/dist/server.d.ts +91 -62
  119. package/dist/server.js +320 -327
  120. package/dist/server.js.map +1 -1
  121. package/migrations/{0000_tired_gambit.sql → 0000_complex_swordsman.sql} +2 -0
  122. package/migrations/meta/0000_snapshot.json +4 -2
  123. package/migrations/meta/_journal.json +2 -2
  124. package/package.json +30 -3
@@ -0,0 +1,14 @@
1
+ import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
2
+
3
+ /**
4
+ * @spfn/auth - Database Schema Definition
5
+ *
6
+ * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables
7
+ */
8
+ /**
9
+ * Auth schema for all authentication and authorization tables
10
+ * Tables: users, roles, permissions, user_invitations, etc.
11
+ */
12
+ declare const authSchema: drizzle_orm_pg_core.PgSchema<string>;
13
+
14
+ export { authSchema };
@@ -0,0 +1,7 @@
1
+ // src/server/entities/schema.ts
2
+ import { createFunctionSchema } from "@spfn/core/db";
3
+ var authSchema = createFunctionSchema("@spfn/auth");
4
+ export {
5
+ authSchema
6
+ };
7
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/server/entities/schema.ts"],"sourcesContent":["/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');"],"mappings":";AAMA,SAAS,4BAA4B;AAM9B,IAAM,aAAa,qBAAqB,YAAY;","names":[]}
@@ -1,17 +1,22 @@
1
1
  // src/server/entities/user-permissions.ts
2
2
  import { bigint as bigint2, boolean as boolean4, text as text4, timestamp as timestamp2, index as index4, unique } from "drizzle-orm/pg-core";
3
- import { id as id4, timestamps as timestamps4, createFunctionSchema as createFunctionSchema4 } from "@spfn/core/db";
3
+ import { id as id4, timestamps as timestamps4 } from "@spfn/core/db";
4
4
 
5
5
  // src/server/entities/users.ts
6
6
  import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
7
- import { id as id2, timestamps as timestamps2, createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
7
+ import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
8
8
  import { sql } from "drizzle-orm";
9
9
 
10
10
  // src/server/entities/roles.ts
11
11
  import { text, boolean, integer, index } from "drizzle-orm/pg-core";
12
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
13
- var schema = createFunctionSchema("@spfn/auth");
14
- var roles = schema.table(
12
+ import { id, timestamps } from "@spfn/core/db";
13
+
14
+ // src/server/entities/schema.ts
15
+ import { createFunctionSchema } from "@spfn/core/db";
16
+ var authSchema = createFunctionSchema("@spfn/auth");
17
+
18
+ // src/server/entities/roles.ts
19
+ var roles = authSchema.table(
15
20
  "roles",
16
21
  {
17
22
  // Primary key
@@ -50,8 +55,7 @@ var roles = schema.table(
50
55
  );
51
56
 
52
57
  // src/server/entities/users.ts
53
- var schema2 = createFunctionSchema2("@spfn/auth");
54
- var users = schema2.table(
58
+ var users = authSchema.table(
55
59
  "users",
56
60
  {
57
61
  // Identity
@@ -114,9 +118,8 @@ var users = schema2.table(
114
118
 
115
119
  // src/server/entities/permissions.ts
116
120
  import { text as text3, boolean as boolean3, index as index3 } from "drizzle-orm/pg-core";
117
- import { id as id3, timestamps as timestamps3, createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
118
- var schema3 = createFunctionSchema3("@spfn/auth");
119
- var permissions = schema3.table(
121
+ import { id as id3, timestamps as timestamps3 } from "@spfn/core/db";
122
+ var permissions = authSchema.table(
120
123
  "permissions",
121
124
  {
122
125
  // Primary key
@@ -154,8 +157,7 @@ var permissions = schema3.table(
154
157
  );
155
158
 
156
159
  // src/server/entities/user-permissions.ts
157
- var schema4 = createFunctionSchema4("@spfn/auth");
158
- var userPermissions = schema4.table(
160
+ var userPermissions = authSchema.table(
159
161
  "user_permissions",
160
162
  {
161
163
  // Primary key
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/entities/user-permissions.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts","../../../src/server/entities/permissions.ts"],"sourcesContent":["/**\n * @spfn/auth - User-Permissions Override Entity\n *\n * Per-user permission grants/revocations\n *\n * Features:\n * - Grant additional permissions to specific users\n * - Revoke role-inherited permissions from specific users\n * - Temporary permissions with expiration\n * - Audit trail with reason field\n *\n * Priority:\n * User permissions override role permissions\n */\n\nimport { bigint, boolean, text, timestamp, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\nimport { users } from './users';\nimport { permissions } from './permissions';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const userPermissions = schema.table('user_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to users table\n userId: bigint('user_id', { mode: 'number' })\n .notNull()\n .references(() => users.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n // Grant or revoke\n // true: Grant this permission (even if role doesn't have it)\n // false: Revoke this permission (even if role has it)\n granted: boolean('granted').notNull().default(true),\n\n // Reason for grant/revocation (audit trail)\n reason: text('reason'),\n\n // Expiration timestamp (optional)\n // null: Permanent override\n // timestamp: Permission expires at this time\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('user_permissions_user_id_idx').on(table.userId),\n index('user_permissions_permission_id_idx').on(table.permissionId),\n index('user_permissions_expires_at_idx').on(table.expiresAt),\n\n // Unique constraint: one user-permission pair only\n unique('user_permissions_unique').on(table.userId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type UserPermission = typeof userPermissions.$inferSelect;\nexport type NewUserPermission = typeof userPermissions.$inferInsert;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const users = schema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const roles = schema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Permissions Entity\n *\n * Granular permissions for RBAC system\n *\n * Features:\n * - Built-in permissions (auth:*, user:*, rbac:*) - required for package\n * - System permissions (preset permissions) - optional\n * - Custom permissions (app-specific) - defined by developers\n * - Category grouping for organization\n */\n\nimport { text, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const permissions = schema.table('permissions',\n {\n // Primary key\n id: id(),\n\n // Permission identifier (e.g., 'user:delete', 'post:publish')\n // Format: resource:action or namespace:resource:action\n // Must be unique\n name: text('name').notNull().unique(),\n\n // Display name for UI\n displayName: text('display_name').notNull(),\n\n // Permission description\n description: text('description'),\n\n // Category for grouping (e.g., 'user', 'post', 'admin', 'system')\n category: text('category'),\n\n // Built-in permission flag\n // true: Core package permissions - cannot be deleted\n // false: Custom or preset permissions\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System permission flag\n // true: Defined in code (builtin or preset)\n // false: Runtime created custom permission\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated permission (not enforced)\n isActive: boolean('is_active').notNull().default(true),\n\n ...timestamps(),\n },\n (table) => [\n index('permissions_name_idx').on(table.name),\n index('permissions_category_idx').on(table.category),\n index('permissions_is_system_idx').on(table.isSystem),\n index('permissions_is_active_idx').on(table.isActive),\n index('permissions_is_builtin_idx').on(table.isBuiltin),\n ]\n);\n\n// Type exports\nexport type PermissionEntity = typeof permissions.$inferSelect;\nexport type NewPermissionEntity = typeof permissions.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Permission = PermissionEntity;\nexport type NewPermission = NewPermissionEntity;"],"mappings":"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","names":["bigint","boolean","text","timestamp","index","id","timestamps","createFunctionSchema","text","boolean","index","id","timestamps","createFunctionSchema","schema","createFunctionSchema","id","text","boolean","timestamps","index","text","boolean","index","id","timestamps","createFunctionSchema","schema","schema","createFunctionSchema","id","bigint","boolean","text","timestamp","timestamps","index"]}
1
+ {"version":3,"sources":["../../../src/server/entities/user-permissions.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts","../../../src/server/entities/schema.ts","../../../src/server/entities/permissions.ts"],"sourcesContent":["/**\n * @spfn/auth - User-Permissions Override Entity\n *\n * Per-user permission grants/revocations\n *\n * Features:\n * - Grant additional permissions to specific users\n * - Revoke role-inherited permissions from specific users\n * - Temporary permissions with expiration\n * - Audit trail with reason field\n *\n * Priority:\n * User permissions override role permissions\n */\n\nimport { bigint, boolean, text, timestamp, index, unique } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { users } from './users';\nimport { permissions } from './permissions';\nimport { authSchema } from './schema';\n\nexport const userPermissions = authSchema.table('user_permissions',\n {\n // Primary key\n id: id(),\n\n // Foreign key to users table\n userId: bigint('user_id', { mode: 'number' })\n .notNull()\n .references(() => users.id, { onDelete: 'cascade' }),\n\n // Foreign key to permissions table\n permissionId: bigint('permission_id', { mode: 'number' })\n .notNull()\n .references(() => permissions.id, { onDelete: 'cascade' }),\n\n // Grant or revoke\n // true: Grant this permission (even if role doesn't have it)\n // false: Revoke this permission (even if role has it)\n granted: boolean('granted').notNull().default(true),\n\n // Reason for grant/revocation (audit trail)\n reason: text('reason'),\n\n // Expiration timestamp (optional)\n // null: Permanent override\n // timestamp: Permission expires at this time\n expiresAt: timestamp('expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Indexes for query performance\n index('user_permissions_user_id_idx').on(table.userId),\n index('user_permissions_permission_id_idx').on(table.permissionId),\n index('user_permissions_expires_at_idx').on(table.expiresAt),\n\n // Unique constraint: one user-permission pair only\n unique('user_permissions_unique').on(table.userId, table.permissionId),\n ]\n);\n\n// Type exports\nexport type UserPermission = typeof userPermissions.$inferSelect;\nexport type NewUserPermission = typeof userPermissions.$inferInsert;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');","/**\n * @spfn/auth - Permissions Entity\n *\n * Granular permissions for RBAC system\n *\n * Features:\n * - Built-in permissions (auth:*, user:*, rbac:*) - required for package\n * - System permissions (preset permissions) - optional\n * - Custom permissions (app-specific) - defined by developers\n * - Category grouping for organization\n */\n\nimport { text, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const permissions = authSchema.table('permissions',\n {\n // Primary key\n id: id(),\n\n // Permission identifier (e.g., 'user:delete', 'post:publish')\n // Format: resource:action or namespace:resource:action\n // Must be unique\n name: text('name').notNull().unique(),\n\n // Display name for UI\n displayName: text('display_name').notNull(),\n\n // Permission description\n description: text('description'),\n\n // Category for grouping (e.g., 'user', 'post', 'admin', 'system')\n category: text('category'),\n\n // Built-in permission flag\n // true: Core package permissions - cannot be deleted\n // false: Custom or preset permissions\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System permission flag\n // true: Defined in code (builtin or preset)\n // false: Runtime created custom permission\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated permission (not enforced)\n isActive: boolean('is_active').notNull().default(true),\n\n ...timestamps(),\n },\n (table) => [\n index('permissions_name_idx').on(table.name),\n index('permissions_category_idx').on(table.category),\n index('permissions_is_system_idx').on(table.isSystem),\n index('permissions_is_active_idx').on(table.isActive),\n index('permissions_is_builtin_idx').on(table.isBuiltin),\n ]\n);\n\n// Type exports\nexport type PermissionEntity = typeof permissions.$inferSelect;\nexport type NewPermissionEntity = typeof permissions.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Permission = PermissionEntity;\nexport type NewPermission = NewPermissionEntity;"],"mappings":";AAeA,SAAS,UAAAA,SAAQ,WAAAC,UAAS,QAAAC,OAAM,aAAAC,YAAW,SAAAC,QAAO,cAAc;AAChE,SAAS,MAAAC,KAAI,cAAAC,mBAAkB;;;ACF/B,SAAS,QAAAC,OAAM,WAAW,OAAO,WAAAC,UAAS,QAAQ,SAAAC,cAAa;AAC/D,SAAS,MAAAC,KAAI,cAAAC,mBAAkB;AAC/B,SAAS,WAAW;;;ACJpB,SAAS,MAAM,SAAS,SAAS,aAAa;AAC9C,SAAS,IAAI,kBAAkB;;;ACP/B,SAAS,4BAA4B;AAM9B,IAAM,aAAa,qBAAqB,YAAY;;;ADIpD,IAAM,QAAQ,WAAW;AAAA,EAAM;AAAA,EAClC;AAAA;AAAA,IAEI,IAAI,GAAG;AAAA;AAAA;AAAA,IAIP,MAAM,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO;AAAA;AAAA,IAGpC,aAAa,KAAK,cAAc,EAAE,QAAQ;AAAA;AAAA,IAG1C,aAAa,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,IAK/B,WAAW,QAAQ,YAAY,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,IAKxD,UAAU,QAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA,IAItD,UAAU,QAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,IAKrD,UAAU,QAAQ,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAElD,GAAG,WAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA,IACP,MAAM,gBAAgB,EAAE,GAAG,MAAM,IAAI;AAAA,IACrC,MAAM,qBAAqB,EAAE,GAAG,MAAM,QAAQ;AAAA,IAC9C,MAAM,qBAAqB,EAAE,GAAG,MAAM,QAAQ;AAAA,IAC9C,MAAM,sBAAsB,EAAE,GAAG,MAAM,SAAS;AAAA,IAChD,MAAM,oBAAoB,EAAE,GAAG,MAAM,QAAQ;AAAA,EACjD;AACJ;;;ADvCO,IAAM,QAAQ,WAAW;AAAA,EAAM;AAAA,EAClC;AAAA;AAAA,IAEI,IAAIC,IAAG;AAAA;AAAA;AAAA,IAIP,OAAOC,MAAK,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,IAK5B,OAAOA,MAAK,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,IAK5B,cAAcA,MAAK,eAAe;AAAA;AAAA;AAAA,IAIlC,wBAAwBC,SAAQ,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAMnF,QAAQ,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EACvC,WAAW,MAAM,MAAM,EAAE,EACzB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,IAMb,QAAQD;AAAA,MACJ;AAAA,MACA;AAAA,QACI,MAAM,CAAC,UAAU,YAAY,WAAW;AAAA,MAC5C;AAAA,IACJ,EAAE,QAAQ,EAAE,QAAQ,QAAQ;AAAA;AAAA;AAAA;AAAA,IAK5B,iBAAiB,UAAU,qBAAqB,EAAE,cAAc,KAAK,CAAC;AAAA;AAAA,IAGtE,iBAAiB,UAAU,qBAAqB,EAAE,cAAc,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,IAKtE,aAAa,UAAU,iBAAiB,EAAE,cAAc,KAAK,CAAC;AAAA,IAE9D,GAAGE,YAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA;AAAA;AAAA,IAGP;AAAA,MACI;AAAA,MACA,MAAM,MAAM,KAAK,mBAAmB,MAAM,KAAK;AAAA,IACnD;AAAA;AAAA,IAGAC,OAAM,iBAAiB,EAAE,GAAG,MAAM,KAAK;AAAA,IACvCA,OAAM,iBAAiB,EAAE,GAAG,MAAM,KAAK;AAAA,IACvCA,OAAM,kBAAkB,EAAE,GAAG,MAAM,MAAM;AAAA,IACzCA,OAAM,mBAAmB,EAAE,GAAG,MAAM,MAAM;AAAA,EAC9C;AACJ;;;AG/EA,SAAS,QAAAC,OAAM,WAAAC,UAAS,SAAAC,cAAa;AACrC,SAAS,MAAAC,KAAI,cAAAC,mBAAkB;AAGxB,IAAM,cAAc,WAAW;AAAA,EAAM;AAAA,EACxC;AAAA;AAAA,IAEI,IAAIC,IAAG;AAAA;AAAA;AAAA;AAAA,IAKP,MAAMC,MAAK,MAAM,EAAE,QAAQ,EAAE,OAAO;AAAA;AAAA,IAGpC,aAAaA,MAAK,cAAc,EAAE,QAAQ;AAAA;AAAA,IAG1C,aAAaA,MAAK,aAAa;AAAA;AAAA,IAG/B,UAAUA,MAAK,UAAU;AAAA;AAAA;AAAA;AAAA,IAKzB,WAAWC,SAAQ,YAAY,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,IAKxD,UAAUA,SAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA,IAItD,UAAUA,SAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAErD,GAAGC,YAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA,IACPC,OAAM,sBAAsB,EAAE,GAAG,MAAM,IAAI;AAAA,IAC3CA,OAAM,0BAA0B,EAAE,GAAG,MAAM,QAAQ;AAAA,IACnDA,OAAM,2BAA2B,EAAE,GAAG,MAAM,QAAQ;AAAA,IACpDA,OAAM,2BAA2B,EAAE,GAAG,MAAM,QAAQ;AAAA,IACpDA,OAAM,4BAA4B,EAAE,GAAG,MAAM,SAAS;AAAA,EAC1D;AACJ;;;AJrCO,IAAM,kBAAkB,WAAW;AAAA,EAAM;AAAA,EAC5C;AAAA;AAAA,IAEI,IAAIC,IAAG;AAAA;AAAA,IAGP,QAAQC,QAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EACvC,QAAQ,EACR,WAAW,MAAM,MAAM,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA;AAAA,IAGvD,cAAcA,QAAO,iBAAiB,EAAE,MAAM,SAAS,CAAC,EACnD,QAAQ,EACR,WAAW,MAAM,YAAY,IAAI,EAAE,UAAU,UAAU,CAAC;AAAA;AAAA;AAAA;AAAA,IAK7D,SAASC,SAAQ,SAAS,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA,IAGlD,QAAQC,MAAK,QAAQ;AAAA;AAAA;AAAA;AAAA,IAKrB,WAAWC,WAAU,cAAc,EAAE,cAAc,KAAK,CAAC;AAAA,IAEzD,GAAGC,YAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA;AAAA,IAEPC,OAAM,8BAA8B,EAAE,GAAG,MAAM,MAAM;AAAA,IACrDA,OAAM,oCAAoC,EAAE,GAAG,MAAM,YAAY;AAAA,IACjEA,OAAM,iCAAiC,EAAE,GAAG,MAAM,SAAS;AAAA;AAAA,IAG3D,OAAO,yBAAyB,EAAE,GAAG,MAAM,QAAQ,MAAM,YAAY;AAAA,EACzE;AACJ;","names":["bigint","boolean","text","timestamp","index","id","timestamps","text","boolean","index","id","timestamps","id","text","boolean","timestamps","index","text","boolean","index","id","timestamps","id","text","boolean","timestamps","index","id","bigint","boolean","text","timestamp","timestamps","index"]}
@@ -1,17 +1,22 @@
1
1
  // src/server/entities/user-public-keys.ts
2
2
  import { text as text3, timestamp as timestamp2, boolean as boolean3, index as index3 } from "drizzle-orm/pg-core";
3
- import { id as id3, foreignKey, createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
3
+ import { id as id3, foreignKey } from "@spfn/core/db";
4
4
 
5
5
  // src/server/entities/users.ts
6
6
  import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
7
- import { id as id2, timestamps as timestamps2, createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
7
+ import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
8
8
  import { sql } from "drizzle-orm";
9
9
 
10
10
  // src/server/entities/roles.ts
11
11
  import { text, boolean, integer, index } from "drizzle-orm/pg-core";
12
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
13
- var schema = createFunctionSchema("@spfn/auth");
14
- var roles = schema.table(
12
+ import { id, timestamps } from "@spfn/core/db";
13
+
14
+ // src/server/entities/schema.ts
15
+ import { createFunctionSchema } from "@spfn/core/db";
16
+ var authSchema = createFunctionSchema("@spfn/auth");
17
+
18
+ // src/server/entities/roles.ts
19
+ var roles = authSchema.table(
15
20
  "roles",
16
21
  {
17
22
  // Primary key
@@ -50,8 +55,7 @@ var roles = schema.table(
50
55
  );
51
56
 
52
57
  // src/server/entities/users.ts
53
- var schema2 = createFunctionSchema2("@spfn/auth");
54
- var users = schema2.table(
58
+ var users = authSchema.table(
55
59
  "users",
56
60
  {
57
61
  // Identity
@@ -113,8 +117,7 @@ var users = schema2.table(
113
117
  );
114
118
 
115
119
  // src/server/entities/user-public-keys.ts
116
- var schema3 = createFunctionSchema3("@spfn/auth");
117
- var userPublicKeys = schema3.table(
120
+ var userPublicKeys = authSchema.table(
118
121
  "user_public_keys",
119
122
  {
120
123
  id: id3(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/entities/user-public-keys.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts"],"sourcesContent":["/**\n * @spfn/auth - User Public Keys Entity\n *\n * Stores client-generated public keys for JWT verification\n * Supports key rotation and multi-key management per user\n */\n\nimport { text, timestamp, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, foreignKey, createFunctionSchema } from '@spfn/core/db';\nimport { users } from './users';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\n/**\n * User Public Keys Table\n * Each user can have multiple public keys (for rotation)\n */\nexport const userPublicKeys = schema.table(\n 'user_public_keys',\n {\n id: id(),\n\n // User reference\n userId: foreignKey('user', () => users.id),\n\n // Key identification (client-generated UUID)\n keyId: text('key_id').notNull().unique(),\n\n // Public key in Base64-encoded DER format (SPKI)\n publicKey: text('public_key').notNull(),\n\n // Algorithm used (ES256 recommended, RS256 fallback)\n algorithm: text('algorithm', {\n enum: ['ES256', 'RS256']\n }).notNull().default('ES256'),\n\n // Key fingerprint (SHA-256 hash for quick identification)\n fingerprint: text('fingerprint').notNull(),\n\n // Key status\n isActive: boolean('is_active').notNull().default(true),\n\n // Timestamps\n createdAt: timestamp('created_at', { mode: 'date', withTimezone: true })\n .notNull()\n .defaultNow(),\n\n lastUsedAt: timestamp('last_used_at', { mode: 'date', withTimezone: true }),\n\n expiresAt: timestamp('expires_at', { mode: 'date', withTimezone: true }),\n\n // Revocation\n revokedAt: timestamp('revoked_at', { mode: 'date', withTimezone: true }),\n revokedReason: text('revoked_reason'),\n },\n (table) => [\n index('user_public_keys_user_id_idx').on(table.userId),\n index('user_public_keys_key_id_idx').on(table.keyId),\n index('user_public_keys_active_idx').on(table.isActive),\n index('user_public_keys_fingerprint_idx').on(table.fingerprint),\n ]\n);\n\nexport type UserPublicKey = typeof userPublicKeys.$inferSelect;\nexport type NewUserPublicKey = typeof userPublicKeys.$inferInsert;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const users = schema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const roles = schema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;"],"mappings":"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","names":["text","timestamp","boolean","index","id","createFunctionSchema","text","boolean","index","id","timestamps","createFunctionSchema","schema","createFunctionSchema","id","text","boolean","timestamps","index","schema","createFunctionSchema","id","text","boolean","timestamp","index"]}
1
+ {"version":3,"sources":["../../../src/server/entities/user-public-keys.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts","../../../src/server/entities/schema.ts"],"sourcesContent":["/**\n * @spfn/auth - User Public Keys Entity\n *\n * Stores client-generated public keys for JWT verification\n * Supports key rotation and multi-key management per user\n */\n\nimport { text, timestamp, boolean, index } from 'drizzle-orm/pg-core';\nimport { id, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\n/**\n * User Public Keys Table\n * Each user can have multiple public keys (for rotation)\n */\nexport const userPublicKeys = authSchema.table(\n 'user_public_keys',\n {\n id: id(),\n\n // User reference\n userId: foreignKey('user', () => users.id),\n\n // Key identification (client-generated UUID)\n keyId: text('key_id').notNull().unique(),\n\n // Public key in Base64-encoded DER format (SPKI)\n publicKey: text('public_key').notNull(),\n\n // Algorithm used (ES256 recommended, RS256 fallback)\n algorithm: text('algorithm', {\n enum: ['ES256', 'RS256']\n }).notNull().default('ES256'),\n\n // Key fingerprint (SHA-256 hash for quick identification)\n fingerprint: text('fingerprint').notNull(),\n\n // Key status\n isActive: boolean('is_active').notNull().default(true),\n\n // Timestamps\n createdAt: timestamp('created_at', { mode: 'date', withTimezone: true })\n .notNull()\n .defaultNow(),\n\n lastUsedAt: timestamp('last_used_at', { mode: 'date', withTimezone: true }),\n\n expiresAt: timestamp('expires_at', { mode: 'date', withTimezone: true }),\n\n // Revocation\n revokedAt: timestamp('revoked_at', { mode: 'date', withTimezone: true }),\n revokedReason: text('revoked_reason'),\n },\n (table) => [\n index('user_public_keys_user_id_idx').on(table.userId),\n index('user_public_keys_key_id_idx').on(table.keyId),\n index('user_public_keys_active_idx').on(table.isActive),\n index('user_public_keys_fingerprint_idx').on(table.fingerprint),\n ]\n);\n\nexport type UserPublicKey = typeof userPublicKeys.$inferSelect;\nexport type NewUserPublicKey = typeof userPublicKeys.$inferInsert;","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');"],"mappings":"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","names":["text","timestamp","boolean","index","id","text","boolean","index","id","timestamps","id","text","boolean","timestamps","index","id","text","boolean","timestamp","index"]}
@@ -1,17 +1,22 @@
1
1
  // src/server/entities/user-social-accounts.ts
2
2
  import { text as text3, timestamp as timestamp2, uniqueIndex } from "drizzle-orm/pg-core";
3
- import { id as id3, timestamps as timestamps3, foreignKey, createFunctionSchema as createFunctionSchema3 } from "@spfn/core/db";
3
+ import { id as id3, timestamps as timestamps3, foreignKey } from "@spfn/core/db";
4
4
 
5
5
  // src/server/entities/users.ts
6
6
  import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
7
- import { id as id2, timestamps as timestamps2, createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
7
+ import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
8
8
  import { sql } from "drizzle-orm";
9
9
 
10
10
  // src/server/entities/roles.ts
11
11
  import { text, boolean, integer, index } from "drizzle-orm/pg-core";
12
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
13
- var schema = createFunctionSchema("@spfn/auth");
14
- var roles = schema.table(
12
+ import { id, timestamps } from "@spfn/core/db";
13
+
14
+ // src/server/entities/schema.ts
15
+ import { createFunctionSchema } from "@spfn/core/db";
16
+ var authSchema = createFunctionSchema("@spfn/auth");
17
+
18
+ // src/server/entities/roles.ts
19
+ var roles = authSchema.table(
15
20
  "roles",
16
21
  {
17
22
  // Primary key
@@ -50,8 +55,7 @@ var roles = schema.table(
50
55
  );
51
56
 
52
57
  // src/server/entities/users.ts
53
- var schema2 = createFunctionSchema2("@spfn/auth");
54
- var users = schema2.table(
58
+ var users = authSchema.table(
55
59
  "users",
56
60
  {
57
61
  // Identity
@@ -113,8 +117,7 @@ var users = schema2.table(
113
117
  );
114
118
 
115
119
  // src/server/entities/user-social-accounts.ts
116
- var schema3 = createFunctionSchema3("@spfn/auth");
117
- var userSocialAccounts = schema3.table(
120
+ var userSocialAccounts = authSchema.table(
118
121
  "user_social_accounts",
119
122
  {
120
123
  id: id3(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/entities/user-social-accounts.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts"],"sourcesContent":["/**\n * @spfn/auth - User Social Accounts Entity\n *\n * Stores OAuth connections for social login providers\n */\n\nimport { text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';\nimport { id, timestamps, foreignKey, createFunctionSchema } from '@spfn/core/db';\nimport { users } from './users';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const userSocialAccounts = schema.table('user_social_accounts',\n {\n id: id(),\n\n // Foreign key to users\n userId: foreignKey('user', () => users.id),\n\n // Provider info\n provider: text(\n 'provider',\n {\n enum: ['google', 'github', 'kakao', 'naver']\n }\n ).notNull(),\n\n providerUserId: text('provider_user_id').notNull(),\n providerEmail: text('provider_email'),\n\n // OAuth tokens (encrypted in production)\n accessToken: text('access_token'),\n refreshToken: text('refresh_token'),\n tokenExpiresAt: timestamp('token_expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Unique constraint: one provider account per provider\n uniqueIndex('provider_user_unique_idx')\n .on(table.provider, table.providerUserId),\n ]\n);\n\n// Type exports\nexport type UserSocialAccount = typeof userSocialAccounts.$inferSelect;\nexport type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;\nexport type SocialProvider = 'google' | 'github' | 'kakao' | 'naver';","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const users = schema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const roles = schema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;"],"mappings":"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","names":["text","timestamp","id","timestamps","createFunctionSchema","text","boolean","index","id","timestamps","createFunctionSchema","schema","createFunctionSchema","id","text","boolean","timestamps","index","schema","createFunctionSchema","id","text","timestamp","timestamps"]}
1
+ {"version":3,"sources":["../../../src/server/entities/user-social-accounts.ts","../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts","../../../src/server/entities/schema.ts"],"sourcesContent":["/**\n * @spfn/auth - User Social Accounts Entity\n *\n * Stores OAuth connections for social login providers\n */\n\nimport { text, timestamp, uniqueIndex } from 'drizzle-orm/pg-core';\nimport { id, timestamps, foreignKey } from '@spfn/core/db';\nimport { users } from './users';\nimport { authSchema } from './schema';\n\nexport const userSocialAccounts = authSchema.table('user_social_accounts',\n {\n id: id(),\n\n // Foreign key to users\n userId: foreignKey('user', () => users.id),\n\n // Provider info\n provider: text(\n 'provider',\n {\n enum: ['google', 'github', 'kakao', 'naver']\n }\n ).notNull(),\n\n providerUserId: text('provider_user_id').notNull(),\n providerEmail: text('provider_email'),\n\n // OAuth tokens (encrypted in production)\n accessToken: text('access_token'),\n refreshToken: text('refresh_token'),\n tokenExpiresAt: timestamp('token_expires_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Unique constraint: one provider account per provider\n uniqueIndex('provider_user_unique_idx')\n .on(table.provider, table.providerUserId),\n ]\n);\n\n// Type exports\nexport type UserSocialAccount = typeof userSocialAccounts.$inferSelect;\nexport type NewUserSocialAccount = typeof userSocialAccounts.$inferInsert;\nexport type SocialProvider = 'google' | 'github' | 'kakao' | 'naver';","/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');"],"mappings":"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","names":["text","timestamp","id","timestamps","text","boolean","index","id","timestamps","id","text","boolean","timestamps","index","id","text","timestamp","timestamps"]}
@@ -1,13 +1,18 @@
1
1
  // src/server/entities/users.ts
2
2
  import { text as text2, timestamp, check, boolean as boolean2, bigint, index as index2 } from "drizzle-orm/pg-core";
3
- import { id as id2, timestamps as timestamps2, createFunctionSchema as createFunctionSchema2 } from "@spfn/core/db";
3
+ import { id as id2, timestamps as timestamps2 } from "@spfn/core/db";
4
4
  import { sql } from "drizzle-orm";
5
5
 
6
6
  // src/server/entities/roles.ts
7
7
  import { text, boolean, integer, index } from "drizzle-orm/pg-core";
8
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
9
- var schema = createFunctionSchema("@spfn/auth");
10
- var roles = schema.table(
8
+ import { id, timestamps } from "@spfn/core/db";
9
+
10
+ // src/server/entities/schema.ts
11
+ import { createFunctionSchema } from "@spfn/core/db";
12
+ var authSchema = createFunctionSchema("@spfn/auth");
13
+
14
+ // src/server/entities/roles.ts
15
+ var roles = authSchema.table(
11
16
  "roles",
12
17
  {
13
18
  // Primary key
@@ -46,8 +51,7 @@ var roles = schema.table(
46
51
  );
47
52
 
48
53
  // src/server/entities/users.ts
49
- var schema2 = createFunctionSchema2("@spfn/auth");
50
- var users = schema2.table(
54
+ var users = authSchema.table(
51
55
  "users",
52
56
  {
53
57
  // Identity
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts"],"sourcesContent":["/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const users = schema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const roles = schema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;"],"mappings":"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","names":["text","boolean","index","id","timestamps","createFunctionSchema","schema","createFunctionSchema","id","text","boolean","timestamps","index"]}
1
+ {"version":3,"sources":["../../../src/server/entities/users.ts","../../../src/server/entities/roles.ts","../../../src/server/entities/schema.ts"],"sourcesContent":["/**\n * @spfn/auth - Users Entity\n *\n * Main user table supporting multiple authentication methods\n *\n * Features:\n * - Email or phone-based registration\n * - Password authentication (bcrypt)\n * - OAuth support (nullable passwordHash)\n * - Role-based access control (RBAC)\n * - Account status management\n * - Email/phone verification\n */\n\nimport { text, timestamp, check, boolean, bigint, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { sql } from 'drizzle-orm';\nimport { roles } from './roles';\nimport { authSchema } from './schema';\n\nexport const users = authSchema.table('users',\n {\n // Identity\n id: id(),\n\n // Email address (unique identifier)\n // Used for: login, password reset, notifications\n email: text('email').unique(),\n\n // Phone number in E.164 international format\n // Format: +[country code][number] (e.g., +821012345678)\n // Used for: SMS login, 2FA, notifications\n phone: text('phone').unique(),\n\n // Authentication\n // Bcrypt password hash ($2b$10$[salt][hash], 60 chars)\n // Nullable to support OAuth-only accounts\n passwordHash: text('password_hash'),\n\n // Force password change on next login\n // Use cases: initial setup, security breach, policy violation\n passwordChangeRequired: boolean('password_change_required').notNull().default(false),\n\n // Authorization (Role-Based Access Control)\n // Foreign key to roles table\n // References built-in roles: user (default), admin, superadmin\n // Can also reference custom roles created at runtime\n roleId: bigint('role_id', { mode: 'number' })\n .references(() => roles.id)\n .notNull(),\n\n // Account status\n // - active: Normal operation (default)\n // - inactive: Deactivated (user request, dormant)\n // - suspended: Locked (security incident, ToS violation)\n status: text(\n 'status',\n {\n enum: ['active', 'inactive', 'suspended']\n }\n ).notNull().default('active'),\n\n // Verification timestamps\n // null = unverified, timestamp = verified at this time\n // Email verification (via verification code or magic link)\n emailVerifiedAt: timestamp('email_verified_at', { withTimezone: true }),\n\n // Phone verification (via SMS OTP)\n phoneVerifiedAt: timestamp('phone_verified_at', { withTimezone: true }),\n\n // Metadata\n // Last successful login timestamp\n // Used for: security auditing, dormant account detection\n lastLoginAt: timestamp('last_login_at', { withTimezone: true }),\n\n ...timestamps(),\n },\n (table) => [\n // Database constraints\n // Ensure at least one identifier exists (email OR phone)\n check(\n 'email_or_phone_check',\n sql`${table.email} IS NOT NULL OR ${table.phone} IS NOT NULL`\n ),\n\n // Indexes for query optimization\n index('users_email_idx').on(table.email),\n index('users_phone_idx').on(table.phone),\n index('users_status_idx').on(table.status),\n index('users_role_id_idx').on(table.roleId),\n ]\n);\n\n// Type exports\nexport type User = typeof users.$inferSelect;\nexport type NewUser = typeof users.$inferInsert;\nexport type UserStatus = 'active' | 'inactive' | 'suspended';\n\n// Helper type with computed verification status\nexport type UserWithVerification = User &\n{\n isEmailVerified: boolean;\n isPhoneVerified: boolean;\n};","/**\n * @spfn/auth - Roles Entity\n *\n * Role-based access control (RBAC) roles table\n *\n * Features:\n * - Built-in roles (user, admin, superadmin) - cannot be deleted\n * - System roles (preset roles) - can be deactivated\n * - Custom roles (runtime created) - fully manageable\n * - Priority-based hierarchy\n */\n\nimport { text, boolean, integer, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const roles = authSchema.table('roles',\n {\n // Primary key\n id: id(),\n\n // Role identifier (used in code, e.g., 'admin', 'editor')\n // Must be unique, lowercase, kebab-case recommended\n name: text('name').notNull().unique(),\n\n // Display name for UI (e.g., 'Administrator', 'Content Editor')\n displayName: text('display_name').notNull(),\n\n // Role description\n description: text('description'),\n\n // Built-in role flag\n // true: Core package roles (user, admin, superadmin) - cannot be deleted\n // false: Custom or preset roles - can be deleted\n isBuiltin: boolean('is_builtin').notNull().default(false),\n\n // System role flag\n // true: Defined in code (builtin or preset) - deletion restricted\n // false: Runtime created custom role - fully manageable\n isSystem: boolean('is_system').notNull().default(false),\n\n // Active status\n // false: Deactivated role (users cannot be assigned)\n isActive: boolean('is_active').notNull().default(true),\n\n // Priority level (higher = more privileged)\n // superadmin: 100, admin: 80, user: 10\n // Used for role hierarchy and conflict resolution\n priority: integer('priority').notNull().default(10),\n\n ...timestamps(),\n },\n (table) => [\n index('roles_name_idx').on(table.name),\n index('roles_is_system_idx').on(table.isSystem),\n index('roles_is_active_idx').on(table.isActive),\n index('roles_is_builtin_idx').on(table.isBuiltin),\n index('roles_priority_idx').on(table.priority),\n ]\n);\n\n// Type exports\nexport type RoleEntity = typeof roles.$inferSelect;\nexport type NewRoleEntity = typeof roles.$inferInsert;\n\n// Legacy alias for backward compatibility\nexport type Role = RoleEntity;\nexport type NewRole = NewRoleEntity;","/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');"],"mappings":";AAcA,SAAS,QAAAA,OAAM,WAAW,OAAO,WAAAC,UAAS,QAAQ,SAAAC,cAAa;AAC/D,SAAS,MAAAC,KAAI,cAAAC,mBAAkB;AAC/B,SAAS,WAAW;;;ACJpB,SAAS,MAAM,SAAS,SAAS,aAAa;AAC9C,SAAS,IAAI,kBAAkB;;;ACP/B,SAAS,4BAA4B;AAM9B,IAAM,aAAa,qBAAqB,YAAY;;;ADIpD,IAAM,QAAQ,WAAW;AAAA,EAAM;AAAA,EAClC;AAAA;AAAA,IAEI,IAAI,GAAG;AAAA;AAAA;AAAA,IAIP,MAAM,KAAK,MAAM,EAAE,QAAQ,EAAE,OAAO;AAAA;AAAA,IAGpC,aAAa,KAAK,cAAc,EAAE,QAAQ;AAAA;AAAA,IAG1C,aAAa,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,IAK/B,WAAW,QAAQ,YAAY,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA,IAKxD,UAAU,QAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA,IAItD,UAAU,QAAQ,WAAW,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA;AAAA;AAAA;AAAA,IAKrD,UAAU,QAAQ,UAAU,EAAE,QAAQ,EAAE,QAAQ,EAAE;AAAA,IAElD,GAAG,WAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA,IACP,MAAM,gBAAgB,EAAE,GAAG,MAAM,IAAI;AAAA,IACrC,MAAM,qBAAqB,EAAE,GAAG,MAAM,QAAQ;AAAA,IAC9C,MAAM,qBAAqB,EAAE,GAAG,MAAM,QAAQ;AAAA,IAC9C,MAAM,sBAAsB,EAAE,GAAG,MAAM,SAAS;AAAA,IAChD,MAAM,oBAAoB,EAAE,GAAG,MAAM,QAAQ;AAAA,EACjD;AACJ;;;ADvCO,IAAM,QAAQ,WAAW;AAAA,EAAM;AAAA,EAClC;AAAA;AAAA,IAEI,IAAIC,IAAG;AAAA;AAAA;AAAA,IAIP,OAAOC,MAAK,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,IAK5B,OAAOA,MAAK,OAAO,EAAE,OAAO;AAAA;AAAA;AAAA;AAAA,IAK5B,cAAcA,MAAK,eAAe;AAAA;AAAA;AAAA,IAIlC,wBAAwBC,SAAQ,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA,IAMnF,QAAQ,OAAO,WAAW,EAAE,MAAM,SAAS,CAAC,EACvC,WAAW,MAAM,MAAM,EAAE,EACzB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA,IAMb,QAAQD;AAAA,MACJ;AAAA,MACA;AAAA,QACI,MAAM,CAAC,UAAU,YAAY,WAAW;AAAA,MAC5C;AAAA,IACJ,EAAE,QAAQ,EAAE,QAAQ,QAAQ;AAAA;AAAA;AAAA;AAAA,IAK5B,iBAAiB,UAAU,qBAAqB,EAAE,cAAc,KAAK,CAAC;AAAA;AAAA,IAGtE,iBAAiB,UAAU,qBAAqB,EAAE,cAAc,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,IAKtE,aAAa,UAAU,iBAAiB,EAAE,cAAc,KAAK,CAAC;AAAA,IAE9D,GAAGE,YAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA;AAAA;AAAA,IAGP;AAAA,MACI;AAAA,MACA,MAAM,MAAM,KAAK,mBAAmB,MAAM,KAAK;AAAA,IACnD;AAAA;AAAA,IAGAC,OAAM,iBAAiB,EAAE,GAAG,MAAM,KAAK;AAAA,IACvCA,OAAM,iBAAiB,EAAE,GAAG,MAAM,KAAK;AAAA,IACvCA,OAAM,kBAAkB,EAAE,GAAG,MAAM,MAAM;AAAA,IACzCA,OAAM,mBAAmB,EAAE,GAAG,MAAM,MAAM;AAAA,EAC9C;AACJ;","names":["text","boolean","index","id","timestamps","id","text","boolean","timestamps","index"]}
@@ -1,8 +1,13 @@
1
1
  // src/server/entities/verification-codes.ts
2
2
  import { text, timestamp, index } from "drizzle-orm/pg-core";
3
- import { id, timestamps, createFunctionSchema } from "@spfn/core/db";
4
- var schema = createFunctionSchema("@spfn/auth");
5
- var verificationCodes = schema.table(
3
+ import { id, timestamps } from "@spfn/core/db";
4
+
5
+ // src/server/entities/schema.ts
6
+ import { createFunctionSchema } from "@spfn/core/db";
7
+ var authSchema = createFunctionSchema("@spfn/auth");
8
+
9
+ // src/server/entities/verification-codes.ts
10
+ var verificationCodes = authSchema.table(
6
11
  "verification_codes",
7
12
  {
8
13
  id: id(),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/entities/verification-codes.ts"],"sourcesContent":["/**\n * @spfn/auth - Verification Codes Entity\n *\n * Stores verification codes for email and phone verification\n * Codes expire after a configurable time period\n */\n\nimport { text, timestamp, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps, createFunctionSchema } from '@spfn/core/db';\n\nconst schema = createFunctionSchema('@spfn/auth');\n\nexport const verificationCodes = schema.table('verification_codes',\n {\n id: id(),\n\n // Target (email or phone)\n target: text('target').notNull(), // Email address or E.164 phone number\n targetType: text(\n 'target_type',\n {\n enum: ['email', 'phone']\n }\n ).notNull(),\n\n // Code\n code: text('code').notNull(), // 6-digit code by default (configurable)\n\n // Purpose\n purpose: text(\n 'purpose',\n {\n enum: ['registration', 'login', 'password_reset', 'email_change', 'phone_change']\n }\n ).notNull(),\n\n // Expiry\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Usage tracking\n usedAt: timestamp('used_at', { withTimezone: true }),\n attempts: text('attempts').notNull().default('0'), // Track failed verification attempts\n\n ...timestamps(),\n },\n (table) => [\n // Index for quick lookup by target and purpose\n index('target_purpose_idx')\n .on(table.target, table.purpose, table.expiresAt),\n ]\n);\n\n// Type exports\nexport type VerificationCode = typeof verificationCodes.$inferSelect;\nexport type NewVerificationCode = typeof verificationCodes.$inferInsert;\nexport type VerificationTargetType = 'email' | 'phone';\nexport type VerificationPurpose = 'registration' | 'login' | 'password_reset' | 'email_change' | 'phone_change';"],"mappings":";AAOA,SAAS,MAAM,WAAW,aAAa;AACvC,SAAS,IAAI,YAAY,4BAA4B;AAErD,IAAM,SAAS,qBAAqB,YAAY;AAEzC,IAAM,oBAAoB,OAAO;AAAA,EAAM;AAAA,EAC1C;AAAA,IACI,IAAI,GAAG;AAAA;AAAA,IAGP,QAAQ,KAAK,QAAQ,EAAE,QAAQ;AAAA;AAAA,IAC/B,YAAY;AAAA,MACR;AAAA,MACA;AAAA,QACI,MAAM,CAAC,SAAS,OAAO;AAAA,MAC3B;AAAA,IACJ,EAAE,QAAQ;AAAA;AAAA,IAGV,MAAM,KAAK,MAAM,EAAE,QAAQ;AAAA;AAAA;AAAA,IAG3B,SAAS;AAAA,MACL;AAAA,MACA;AAAA,QACI,MAAM,CAAC,gBAAgB,SAAS,kBAAkB,gBAAgB,cAAc;AAAA,MACpF;AAAA,IACJ,EAAE,QAAQ;AAAA;AAAA,IAGV,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ;AAAA;AAAA,IAGnE,QAAQ,UAAU,WAAW,EAAE,cAAc,KAAK,CAAC;AAAA,IACnD,UAAU,KAAK,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG;AAAA;AAAA,IAEhD,GAAG,WAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA;AAAA,IAEP,MAAM,oBAAoB,EACrB,GAAG,MAAM,QAAQ,MAAM,SAAS,MAAM,SAAS;AAAA,EACxD;AACJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/server/entities/verification-codes.ts","../../../src/server/entities/schema.ts"],"sourcesContent":["/**\n * @spfn/auth - Verification Codes Entity\n *\n * Stores verification codes for email and phone verification\n * Codes expire after a configurable time period\n */\n\nimport { text, timestamp, index } from 'drizzle-orm/pg-core';\nimport { id, timestamps } from '@spfn/core/db';\nimport { authSchema } from './schema';\n\nexport const verificationCodes = authSchema.table('verification_codes',\n {\n id: id(),\n\n // Target (email or phone)\n target: text('target').notNull(), // Email address or E.164 phone number\n targetType: text(\n 'target_type',\n {\n enum: ['email', 'phone']\n }\n ).notNull(),\n\n // Code\n code: text('code').notNull(), // 6-digit code by default (configurable)\n\n // Purpose\n purpose: text(\n 'purpose',\n {\n enum: ['registration', 'login', 'password_reset', 'email_change', 'phone_change']\n }\n ).notNull(),\n\n // Expiry\n expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),\n\n // Usage tracking\n usedAt: timestamp('used_at', { withTimezone: true }),\n attempts: text('attempts').notNull().default('0'), // Track failed verification attempts\n\n ...timestamps(),\n },\n (table) => [\n // Index for quick lookup by target and purpose\n index('target_purpose_idx')\n .on(table.target, table.purpose, table.expiresAt),\n ]\n);\n\n// Type exports\nexport type VerificationCode = typeof verificationCodes.$inferSelect;\nexport type NewVerificationCode = typeof verificationCodes.$inferInsert;\nexport type VerificationTargetType = 'email' | 'phone';\nexport type VerificationPurpose = 'registration' | 'login' | 'password_reset' | 'email_change' | 'phone_change';","/**\n * @spfn/auth - Database Schema Definition\n *\n * Defines the 'spfn_auth' PostgreSQL schema for all auth-related tables\n */\n\nimport { createFunctionSchema } from '@spfn/core/db';\n\n/**\n * Auth schema for all authentication and authorization tables\n * Tables: users, roles, permissions, user_invitations, etc.\n */\nexport const authSchema = createFunctionSchema('@spfn/auth');"],"mappings":";AAOA,SAAS,MAAM,WAAW,aAAa;AACvC,SAAS,IAAI,kBAAkB;;;ACF/B,SAAS,4BAA4B;AAM9B,IAAM,aAAa,qBAAqB,YAAY;;;ADDpD,IAAM,oBAAoB,WAAW;AAAA,EAAM;AAAA,EAC9C;AAAA,IACI,IAAI,GAAG;AAAA;AAAA,IAGP,QAAQ,KAAK,QAAQ,EAAE,QAAQ;AAAA;AAAA,IAC/B,YAAY;AAAA,MACR;AAAA,MACA;AAAA,QACI,MAAM,CAAC,SAAS,OAAO;AAAA,MAC3B;AAAA,IACJ,EAAE,QAAQ;AAAA;AAAA,IAGV,MAAM,KAAK,MAAM,EAAE,QAAQ;AAAA;AAAA;AAAA,IAG3B,SAAS;AAAA,MACL;AAAA,MACA;AAAA,QACI,MAAM,CAAC,gBAAgB,SAAS,kBAAkB,gBAAgB,cAAc;AAAA,MACpF;AAAA,IACJ,EAAE,QAAQ;AAAA;AAAA,IAGV,WAAW,UAAU,cAAc,EAAE,cAAc,KAAK,CAAC,EAAE,QAAQ;AAAA;AAAA,IAGnE,QAAQ,UAAU,WAAW,EAAE,cAAc,KAAK,CAAC;AAAA,IACnD,UAAU,KAAK,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG;AAAA;AAAA,IAEhD,GAAG,WAAW;AAAA,EAClB;AAAA,EACA,CAAC,UAAU;AAAA;AAAA,IAEP,MAAM,oBAAoB,EACrB,GAAG,MAAM,QAAQ,MAAM,SAAS,MAAM,SAAS;AAAA,EACxD;AACJ;","names":[]}