@vreko/cli 3.1.6 → 3.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <p align="center">
2
2
  <picture>
3
- <source media="(prefers-color-scheme: dark)" srcset="./assets/lockup-white.png" width="400">
4
- <img alt="Vreko" src="./assets/lockup-dark.png" width="400">
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vreko-dev/vreko-cli/main/lockup-white.png" width="400">
4
+ <img alt="Vreko" src="https://raw.githubusercontent.com/vreko-dev/vreko-cli/main/lockup-dark.png" width="400">
5
5
  </picture>
6
6
  </p>
7
7
 
@@ -2,75 +2,75 @@
2
2
  > @vreko/auth@0.1.2 build /Users/user1/WebstormProjects/Vreko-Site/packages/auth
3
3
  > tsup && tsc --build --force && node ../../scripts/build-utils/add-js-extensions.mjs
4
4
 
5
- CLI Building entry: src/auth.ts, src/client.ts, src/index.ts, src/workspace.ts, src/lib/audit.ts, src/lib/helper.ts, src/lib/organization.ts, src/lib/tier-utils.ts, src/security/api-key-security.ts, src/security/csrf-protection.ts, src/security/hmac-header.ts, src/security/rate-limiting.ts, src/security/session-security.ts
5
+ CLI Building entry: src/auth.ts, src/client.ts, src/index.ts, src/workspace.ts, src/security/api-key-security.ts, src/security/csrf-protection.ts, src/security/hmac-header.ts, src/security/rate-limiting.ts, src/security/session-security.ts, src/lib/audit.ts, src/lib/helper.ts, src/lib/organization.ts, src/lib/tier-utils.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.5.0
8
8
  CLI Using tsup config: /Users/user1/WebstormProjects/Vreko-Site/packages/auth/tsup.config.ts
9
9
  CLI Target: es2022
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
- dist/security/api-key-security.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/api-key-security.js" was ignored.
13
- dist/security/csrf-protection.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/csrf-protection.js" was ignored.
14
- dist/security/hmac-header.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/hmac-header.js" was ignored.
15
- dist/security/rate-limiting.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/rate-limiting.js" was ignored.
16
- dist/auth.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/auth.js" was ignored.
12
+ dist/lib/audit.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/audit.js" was ignored.
13
+ dist/lib/helper.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/helper.js" was ignored.
14
+ dist/lib/organization.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/organization.js" was ignored.
17
15
  dist/security/session-security.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/session-security.js" was ignored.
16
+ dist/auth.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/auth.js" was ignored.
17
+ dist/lib/tier-utils.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/tier-utils.js" was ignored.
18
18
  dist/client.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/client.js" was ignored.
19
19
  dist/client.js (2:0): Module level directives cause errors when bundled, "use client" in "dist/client.js" was ignored.
20
20
  dist/index.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/index.js" was ignored.
21
- dist/chunk-F4AZ5GM7.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-F4AZ5GM7.js" was ignored.
22
- dist/chunk-Z3PKMANB.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-Z3PKMANB.js" was ignored.
23
- dist/chunk-LCQDZ4PZ.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-LCQDZ4PZ.js" was ignored.
24
- dist/lib/audit.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/audit.js" was ignored.
25
- dist/chunk-LX4G2BOS.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-LX4G2BOS.js" was ignored.
26
- dist/lib/helper.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/helper.js" was ignored.
27
- dist/lib/organization.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/organization.js" was ignored.
21
+ dist/chunk-3OCLD7XA.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-3OCLD7XA.js" was ignored.
28
22
  dist/chunk-W3CIYHBC.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-W3CIYHBC.js" was ignored.
29
23
  dist/chunk-4WMVVIVE.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-4WMVVIVE.js" was ignored.
30
24
  dist/workspace.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/workspace.js" was ignored.
31
- dist/lib/tier-utils.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/lib/tier-utils.js" was ignored.
25
+ dist/security/api-key-security.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/api-key-security.js" was ignored.
26
+ dist/chunk-LX4G2BOS.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-LX4G2BOS.js" was ignored.
27
+ dist/chunk-LCQDZ4PZ.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-LCQDZ4PZ.js" was ignored.
28
+ dist/security/csrf-protection.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/csrf-protection.js" was ignored.
29
+ dist/security/hmac-header.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/hmac-header.js" was ignored.
30
+ dist/chunk-F4AZ5GM7.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-F4AZ5GM7.js" was ignored.
32
31
  dist/chunk-OIVAH7IC.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/chunk-OIVAH7IC.js" was ignored.
33
- ESM dist/security/hmac-header.js 198.00 B
34
- ESM dist/security/api-key-security.js 389.00 B
35
- ESM dist/security/csrf-protection.js 6.38 KB
36
- ESM dist/security/rate-limiting.js 5.99 KB
32
+ dist/security/rate-limiting.js (1:0): Module level directives cause errors when bundled, "use client" in "dist/security/rate-limiting.js" was ignored.
33
+ ESM dist/lib/helper.js 699.00 B
34
+ ESM dist/lib/organization.js 190.00 B
37
35
  ESM dist/security/session-security.js 7.45 KB
38
- ESM dist/index.js 36.51 KB
39
- ESM dist/chunk-F4AZ5GM7.js 2.06 KB
40
- ESM dist/client.js 1.27 KB
41
36
  ESM dist/auth.js 276.00 B
42
- ESM dist/chunk-Z3PKMANB.js 22.59 KB
43
- ESM dist/chunk-LX4G2BOS.js 4.54 KB
44
- ESM dist/chunk-LCQDZ4PZ.js 4.88 KB
45
37
  ESM dist/lib/audit.js 193.00 B
46
- ESM dist/lib/organization.js 190.00 B
47
- ESM dist/chunk-4WMVVIVE.js 1.14 KB
38
+ ESM dist/lib/tier-utils.js 1.94 KB
39
+ ESM dist/client.js 1.27 KB
48
40
  ESM dist/chunk-W3CIYHBC.js 3.48 KB
49
41
  ESM dist/workspace.js 22.54 KB
50
- ESM dist/lib/tier-utils.js 1.94 KB
42
+ ESM dist/chunk-3OCLD7XA.js 22.59 KB
43
+ ESM dist/chunk-LX4G2BOS.js 4.54 KB
44
+ ESM dist/chunk-4WMVVIVE.js 1.14 KB
45
+ ESM dist/chunk-LCQDZ4PZ.js 4.88 KB
46
+ ESM dist/security/api-key-security.js 389.00 B
47
+ ESM dist/index.js 36.51 KB
48
+ ESM dist/security/csrf-protection.js 6.38 KB
49
+ ESM dist/security/hmac-header.js 198.00 B
50
+ ESM dist/chunk-F4AZ5GM7.js 2.06 KB
51
51
  ESM dist/chunk-OIVAH7IC.js 234.00 B
52
- ESM dist/lib/helper.js 699.00 B
53
- ESM dist/security/hmac-header.js.map 75.00 B
54
- ESM dist/security/csrf-protection.js.map 19.00 KB
55
- ESM dist/security/api-key-security.js.map 80.00 B
56
- ESM dist/security/rate-limiting.js.map 18.25 KB
52
+ ESM dist/security/rate-limiting.js 5.99 KB
53
+ ESM dist/lib/helper.js.map 1.49 KB
54
+ ESM dist/lib/organization.js.map 76.00 B
55
+ ESM dist/lib/audit.js.map 69.00 B
56
+ ESM dist/lib/tier-utils.js.map 5.21 KB
57
+ ESM dist/auth.js.map 68.00 B
57
58
  ESM dist/client.js.map 5.40 KB
58
- ESM dist/chunk-Z3PKMANB.js.map 55.56 KB
59
- ESM dist/chunk-LX4G2BOS.js.map 12.44 KB
60
- ESM dist/chunk-LCQDZ4PZ.js.map 17.12 KB
59
+ ESM dist/workspace.js.map 56.83 KB
61
60
  ESM dist/security/session-security.js.map 21.93 KB
62
- ESM dist/chunk-F4AZ5GM7.js.map 7.19 KB
63
- ESM dist/index.js.map 109.37 KB
64
- ESM dist/auth.js.map 68.00 B
65
- ESM dist/lib/organization.js.map 76.00 B
66
61
  ESM dist/chunk-4WMVVIVE.js.map 2.14 KB
67
- ESM dist/workspace.js.map 56.83 KB
68
- ESM dist/lib/tier-utils.js.map 5.21 KB
69
- ESM dist/chunk-OIVAH7IC.js.map 78.00 B
70
- ESM dist/lib/helper.js.map 1.49 KB
71
- ESM dist/lib/audit.js.map 69.00 B
72
62
  ESM dist/chunk-W3CIYHBC.js.map 11.08 KB
73
- ESM ⚡️ Build success in 337ms
63
+ ESM dist/chunk-LCQDZ4PZ.js.map 17.12 KB
64
+ ESM dist/chunk-3OCLD7XA.js.map 55.56 KB
65
+ ESM dist/chunk-LX4G2BOS.js.map 12.44 KB
66
+ ESM dist/index.js.map 109.37 KB
67
+ ESM dist/security/api-key-security.js.map 80.00 B
68
+ ESM dist/security/csrf-protection.js.map 19.00 KB
69
+ ESM dist/security/hmac-header.js.map 75.00 B
70
+ ESM dist/chunk-F4AZ5GM7.js.map 7.19 KB
71
+ ESM dist/chunk-OIVAH7IC.js.map 78.00 B
72
+ ESM dist/security/rate-limiting.js.map 18.25 KB
73
+ ESM ⚡️ Build success in 363ms
74
74
 
75
75
  > @vreko/auth@0.1.2 postbuild /Users/user1/WebstormProjects/Vreko-Site/packages/auth
76
76
  > echo 'Auth package built successfully'
@@ -1,30 +1,30 @@
1
1
 
2
- > @vreko/auth@0.1.1 test /Users/user1/WebstormProjects/Vreko-Site/packages/auth
2
+ > @vreko/auth@0.1.2 test /Users/user1/WebstormProjects/Vreko-Site/packages/auth
3
3
  > vitest
4
4
 
5
- 3:58:48 PM [vite] warning: `esbuild` option was specified by "vitest" plugin. This option is deprecated, please use `oxc` instead.
5
+ 3:45:12 PM [vite] warning: `esbuild` option was specified by "vitest" plugin. This option is deprecated, please use `oxc` instead.
6
6
  The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
7
7
 
8
8
  RUN v3.2.6 /Users/user1/WebstormProjects/Vreko-Site/packages/auth
9
9
 
10
- ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > returns 401 UNAUTHORIZED when no session is present 1999ms
11
- ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > does not insert a row when unauthenticated (auth gate is not skipped) 2ms
10
+ ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > returns 401 UNAUTHORIZED when no session is present 1933ms
11
+ ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > does not insert a row when unauthenticated (auth gate is not skipped) 1ms
12
12
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > stores the session userId — not any value provided externally (IDOR prevention) 2ms
13
13
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > createApiKey > returns 200 with a key when session is valid 1ms
14
14
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > returns 401 UNAUTHORIZED when no session is present 1ms
15
15
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > does not call DB update when unauthenticated (no bypass possible) 1ms
16
- ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > returns 404 NOT_FOUND when the key belongs to a different user (ownership check) 4ms
16
+ ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > returns 404 NOT_FOUND when the key belongs to a different user (ownership check) 3ms
17
17
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > runs the DB UPDATE even when ownership fails — the check is in the WHERE, not pre-flight 1ms
18
- ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > returns 200 and revokes when the caller owns the key 3ms
18
+ ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > deleteApiKey > returns 200 and revokes when the caller owns the key 2ms
19
19
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > accepts unauthenticated requests — intentional service-to-service design 1ms
20
20
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:false for a key that does not exist 1ms
21
21
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:false for a revoked key (WHERE revokedAt IS NULL filters it out) 1ms
22
- ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:false for an expired key (WHERE expiresAt >= now filters it out) 1ms
22
+ ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:false for an expired key (WHERE expiresAt >= now filters it out) 0ms
23
23
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:false when prefix matches but Argon2id hash verification fails 1ms
24
24
  ✓ |@vreko/auth| test/security/api-key-plugin.security.test.ts > apiKeyPlugin — security properties > verifyApiKey > returns isValid:true with userId and key metadata when verification succeeds 1ms
25
25
 
26
26
  Test Files 1 passed (1)
27
27
  Tests 15 passed (15)
28
- Start at 15:58:49
29
- Duration 4.46s (transform 339ms, setup 0ms, collect 321ms, tests 2.02s, environment 0ms, prepare 306ms)
28
+ Start at 15:45:13
29
+ Duration 5.01s (transform 372ms, setup 0ms, collect 441ms, tests 1.95s, environment 0ms, prepare 434ms)
30
30
 
@@ -1,4 +1,4 @@
1
1
 
2
- > @vreko/auth@0.1.1 type-check /Users/user1/WebstormProjects/Vreko-Site/packages/auth
2
+ > @vreko/auth@0.1.2 type-check /Users/user1/WebstormProjects/Vreko-Site/packages/auth
3
3
  > tsc --noEmit
4
4
 
@@ -1,5 +1,5 @@
1
- import { verifyAPIKeyHash, generateAPIKey, hashAPIKey } from './chunk-LCQDZ4PZ.js';
2
1
  import { trackEvent } from './chunk-W3CIYHBC.js';
2
+ import { verifyAPIKeyHash, generateAPIKey, hashAPIKey } from './chunk-LCQDZ4PZ.js';
3
3
  import { __name } from './chunk-OIVAH7IC.js';
4
4
  import { getBaseUrl } from '@vreko/config/server';
5
5
  import { sendEmail } from '@vreko/email/resend';
@@ -656,5 +656,5 @@ var _auth = betterAuth({
656
656
  var auth = _auth;
657
657
 
658
658
  export { auth };
659
- //# sourceMappingURL=chunk-Z3PKMANB.js.map
660
- //# sourceMappingURL=chunk-Z3PKMANB.js.map
659
+ //# sourceMappingURL=chunk-3OCLD7XA.js.map
660
+ //# sourceMappingURL=chunk-3OCLD7XA.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/plugins/api-key-plugin.ts","../src/auth.ts"],"names":["createApiKeyBodySchema","z","object","name","string","optional","default","organizationId","permissions","maxSnapshots","number","cloudBackup","boolean","advancedDetection","customRules","teamSharing","rateLimitEnabled","rateLimitMax","int","rateLimitTimeWindow","expiresAt","coerce","date","metadata","record","unknown","verifyApiKeyBodySchema","key","array","deleteApiKeyBodySchema","keyId","apiKeyPlugin","id","endpoints","createApiKey","createAuthEndpoint","method","body","openapi","operationId","description","responses","content","schema","type","properties","createdAt","format","nullable","ctx","sessionData","getSessionFromCtx","user","APIError","message","userId","plainKey","generateAPIKey","keyHash","hashAPIKey","start","substring","keyPreview","nanoid","now","Date","db","insert","apiKeys","values","prefix","enabled","updatedAt","logger","info","json","verifyApiKey","isValid","error","candidates","select","from","where","and","eq","isNull","revokedAt","or","gte","limit","matchedKey","candidate","matches","verifyAPIKeyHash","update","set","lastUsedAt","catch","err","warn","valid","remaining","lastRefillAt","deleteApiKey","result","rowCount","success","appUrl","process","env","APP_URL","getBaseUrl","authBaseUrl","BETTER_AUTH_URL","BETTER_AUTH_BASE_URL","PORT","isLocalDev","includes","isDevelopment","NODE_ENV","trustedOrigins","filter","url","index","arr","indexOf","_auth","betterAuth","baseURL","additionalFields","onboardingComplete","required","defaultValue","deviceFingerprint","appName","disablePaths","GET","handler","Response","status","emailAndPassword","socialProviders","GITHUB_CLIENT_ID","GITHUB_CLIENT_SECRET","github","clientId","clientSecret","disableImplicitSignUp","GOOGLE_CLIENT_ID","GOOGLE_CLIENT_SECRET","google","database","drizzleAdapter","provider","combinedSchema","session","expiresIn","updateAge","storeSessionInDatabase","account","accountLinking","trustedProviders","advanced","useSecureCookies","crossSiteRequestForgery","checkOrigin","generateId","defaultFindManyLimit","ipAddress","ipAddressHeaders","disableIpTracking","crossSubDomainCookies","domain","undefined","defaultCookieAttributes","sameSite","secure","httpOnly","path","cookies","state","attributes","maxAge","pkce","cookiePrefix","rateLimit","window","max","storage","databaseHooks","create","after","sessionId","trackEvent","timestamp","toISOString","sql","execute","rotatedCount","reason","Error","String","delete","before","oauthState","getOAuthState","inviteCode","__inviteCode","email","pendingApiKeys","pendingApiKeysTable","inviteCodes","inviteCodesTable","burnInviteCode","gt","claim","purpose","length","codeRow","invitedEmail","currentUses","maxUses","data","code","entry","burned","returning","startsWith","syncUserToHubSpot","signupSource","plugins","bearer","deviceAuthorization","verificationUri","admin","openAPI","magicLink","sendMagicLink","sendEmail","to","subject","html","organization","onAPIError","onError","errorDetails","context","errorCode","errorMessage","statusCode","isOAuthError","request","requestUrl","requestMethod","split","oauthProvider","errorType","then","captureError","tags","extra","auth"],"mappings":"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","file":"chunk-Z3PKMANB.js","sourcesContent":["/**\n * Custom API Key Plugin for Better Auth v1.6.5\n *\n * better-auth v1.6.5 removed the built-in apiKey() plugin.\n * This plugin re-implements the createApiKey / verifyApiKey / deleteApiKey\n * surface using Better Auth's plugin endpoint API so that\n * `auth.api.createApiKey`, `auth.api.verifyApiKey`, and\n * `auth.api.deleteApiKey` are defined at runtime.\n *\n * Storage: uses @vreko/platform's Drizzle `apiKeys` table directly.\n * Key generation: uses packages/auth/src/security/api-key-security.ts.\n */\n\nimport { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { logger } from \"@vreko/infrastructure\";\nimport { apiKeys, db } from \"@vreko/platform\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { and, eq, gte, isNull, or } from \"drizzle-orm\";\nimport { nanoid } from \"nanoid\";\nimport { z } from \"zod\";\nimport { generateAPIKey, hashAPIKey, verifyAPIKeyHash } from \"../security/api-key-security\";\n\n// ─── Schemas ────────────────────────────────────────────────────────────────\n\nconst createApiKeyBodySchema = z.object({\n\t/** User-facing name for this key */\n\tname: z.string().optional().default(\"Default Key\"),\n\t/** Optional org scoping */\n\torganizationId: z.string().optional(),\n\t/** Structured permissions object — mirrors the apiKeys.permissions column type */\n\tpermissions: z\n\t\t.object({\n\t\t\tmaxSnapshots: z.number().optional(),\n\t\t\tcloudBackup: z.boolean().optional(),\n\t\t\tadvancedDetection: z.boolean().optional(),\n\t\t\tcustomRules: z.boolean().optional(),\n\t\t\tteamSharing: z.boolean().optional(),\n\t\t})\n\t\t.optional(),\n\t/** Whether rate limiting is enabled for this key */\n\trateLimitEnabled: z.boolean().optional().default(false),\n\t/** Max requests per rate-limit window */\n\trateLimitMax: z.number().int().optional(),\n\t/** Rate limit window in milliseconds */\n\trateLimitTimeWindow: z.number().int().optional(),\n\t/** Optional expiry date */\n\texpiresAt: z.coerce.date().optional(),\n\t/** Arbitrary metadata */\n\tmetadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst verifyApiKeyBodySchema = z.object({\n\t/** The plaintext API key to verify */\n\tkey: z.string(),\n\t/** Optional permissions to check against */\n\tpermissions: z.record(z.string(), z.array(z.string())).optional(),\n});\n\nconst deleteApiKeyBodySchema = z.object({\n\t/** The DB id of the key to delete / revoke */\n\tkeyId: z.string(),\n});\n\n// ─── Plugin ─────────────────────────────────────────────────────────────────\n\n/**\n * Better Auth plugin that provides API key management endpoints.\n *\n * Registers:\n * POST /api/auth/api-key/create → auth.api.createApiKey\n * POST /api/auth/api-key/verify → auth.api.verifyApiKey\n * POST /api/auth/api-key/delete → auth.api.deleteApiKey\n */\nexport function apiKeyPlugin() {\n\treturn {\n\t\tid: \"vreko-api-key\" as const,\n\n\t\tendpoints: {\n\t\t\tcreateApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/create\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: createApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"createApiKey\",\n\t\t\t\t\t\t\tdescription: \"Create a new API key for the given user\",\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\t\t\tdescription: \"API key created\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tcreatedAt: { type: \"string\", format: \"date-time\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\texpiresAt: { type: \"string\", format: \"date-time\", nullable: true },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst sessionData = await getSessionFromCtx(ctx);\n\t\t\t\t\tif (!sessionData?.user?.id) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", { message: \"Unauthorized\" });\n\t\t\t\t\t}\n\t\t\t\t\tconst userId = sessionData.user.id;\n\n\t\t\t\t\tconst {\n\t\t\t\t\t\tname,\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\tpermissions,\n\t\t\t\t\t\trateLimitEnabled,\n\t\t\t\t\t\trateLimitMax,\n\t\t\t\t\t\trateLimitTimeWindow,\n\t\t\t\t\t\texpiresAt,\n\t\t\t\t\t\tmetadata,\n\t\t\t\t\t} = ctx.body;\n\n\t\t\t\t\t// Generate a plaintext key (only returned once)\n\t\t\t\t\tconst plainKey = generateAPIKey(true);\n\t\t\t\t\t// Hash with Argon2id for storage\n\t\t\t\t\tconst keyHash = await hashAPIKey(plainKey);\n\n\t\t\t\t\t// \"start\" = first 8 chars of the plaintext key (Better Auth convention for lookup)\n\t\t\t\t\tconst start = plainKey.substring(0, 8);\n\t\t\t\t\t// preview = first 12 chars for display (includes prefix + a few chars)\n\t\t\t\t\tconst keyPreview = plainKey.substring(0, 12);\n\n\t\t\t\t\tconst id = nanoid();\n\t\t\t\t\tconst now = new Date();\n\n\t\t\t\t\tawait db.insert(apiKeys).values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\torganizationId: organizationId ?? null,\n\t\t\t\t\t\tname: name ?? \"Default Key\",\n\t\t\t\t\t\tkey: keyHash,\n\t\t\t\t\t\tstart,\n\t\t\t\t\t\tprefix: \"sk_live_\",\n\t\t\t\t\t\tkeyPreview,\n\t\t\t\t\t\tpermissions: permissions ?? null,\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\trateLimitEnabled: rateLimitEnabled ?? false,\n\t\t\t\t\t\trateLimitMax: rateLimitMax ?? null,\n\t\t\t\t\t\trateLimitTimeWindow: rateLimitTimeWindow ?? null,\n\t\t\t\t\t\texpiresAt: expiresAt ?? null,\n\t\t\t\t\t\tmetadata: metadata ?? null,\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t});\n\n\t\t\t\t\tlogger.info(\"API key created\", { userId, keyId: id });\n\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tkey: plainKey, // plaintext returned only once\n\t\t\t\t\t\tname: name ?? \"Default Key\",\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\texpiresAt: expiresAt ?? null,\n\t\t\t\t\t\tpermissions: permissions ?? null,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\n\t\t\tverifyApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/verify\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: verifyApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"verifyApiKey\",\n\t\t\t\t\t\t\tdescription: \"Verify an API key and return its metadata\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst { key } = ctx.body;\n\n\t\t\t\t\tif (!key) {\n\t\t\t\t\t\treturn ctx.json({ isValid: false, error: { message: \"Key is required\" } });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Derive the `start` prefix from the submitted key for fast lookup.\n\t\t\t\t\t// Better Auth v1.x convention: `start` = first 8 chars of the plaintext key.\n\t\t\t\t\tconst start = key.substring(0, 8);\n\n\t\t\t\t\t// Find candidates by prefix + not revoked + not expired\n\t\t\t\t\tconst candidates = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiKeys)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\teq(apiKeys.start, start),\n\t\t\t\t\t\t\t\tisNull(apiKeys.revokedAt),\n\t\t\t\t\t\t\t\tor(isNull(apiKeys.expiresAt), gte(apiKeys.expiresAt, new Date())),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.limit(5); // at most a handful of keys share the same 8-char prefix\n\n\t\t\t\t\t// Find the matching key via Argon2id verification\n\t\t\t\t\tlet matchedKey: (typeof candidates)[0] | null = null;\n\t\t\t\t\tfor (const candidate of candidates) {\n\t\t\t\t\t\tconst matches = await verifyAPIKeyHash(key, candidate.key);\n\t\t\t\t\t\tif (matches) {\n\t\t\t\t\t\t\tmatchedKey = candidate;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!matchedKey) {\n\t\t\t\t\t\treturn ctx.json({ isValid: false, error: { message: \"Invalid API key\" } });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update lastUsedAt (non-blocking)\n\t\t\t\t\tdb.update(apiKeys)\n\t\t\t\t\t\t.set({ lastUsedAt: new Date(), updatedAt: new Date() })\n\t\t\t\t\t\t.where(eq(apiKeys.id, matchedKey.id))\n\t\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t\tlogger.warn(\"Failed to update lastUsedAt for API key\", {\n\t\t\t\t\t\t\t\tkeyId: matchedKey!.id,\n\t\t\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tisValid: true,\n\t\t\t\t\t\tvalid: true, // legacy alias for consumers checking `.valid`\n\t\t\t\t\t\tuserId: matchedKey.userId,\n\t\t\t\t\t\tkey: {\n\t\t\t\t\t\t\tid: matchedKey.id,\n\t\t\t\t\t\t\tuserId: matchedKey.userId,\n\t\t\t\t\t\t\tname: matchedKey.name,\n\t\t\t\t\t\t\tpermissions: matchedKey.permissions,\n\t\t\t\t\t\t\trateLimitEnabled: matchedKey.rateLimitEnabled,\n\t\t\t\t\t\t\trateLimitMax: matchedKey.rateLimitMax,\n\t\t\t\t\t\t\tremaining: matchedKey.remaining,\n\t\t\t\t\t\t\tlastRefillAt: matchedKey.lastRefillAt,\n\t\t\t\t\t\t\tmetadata: matchedKey.metadata,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\n\t\t\tdeleteApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/delete\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: deleteApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"deleteApiKey\",\n\t\t\t\t\t\t\tdescription: \"Revoke (soft-delete) an API key by ID\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst sessionData = await getSessionFromCtx(ctx);\n\t\t\t\t\tif (!sessionData?.user?.id) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", { message: \"Unauthorized\" });\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { keyId } = ctx.body;\n\n\t\t\t\t\tconst result = await db\n\t\t\t\t\t\t.update(apiKeys)\n\t\t\t\t\t\t.set({ revokedAt: new Date(), updatedAt: new Date() })\n\t\t\t\t\t\t.where(and(eq(apiKeys.id, keyId), eq(apiKeys.userId, sessionData.user.id)));\n\n\t\t\t\t\t// Zero rows affected means the key didn't exist or didn't belong to caller\n\t\t\t\t\tif (!result.rowCount) {\n\t\t\t\t\t\tthrow new APIError(\"NOT_FOUND\", { message: \"Not found\" });\n\t\t\t\t\t}\n\n\t\t\t\t\tlogger.info(\"API key revoked\", { keyId, userId: sessionData.user.id });\n\n\t\t\t\t\treturn ctx.json({ success: true });\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t} satisfies import(\"@better-auth/core\").BetterAuthPlugin;\n}\n","import { getBaseUrl } from \"@vreko/config/server\";\nimport { sendEmail } from \"@vreko/email/resend\";\nimport { logger } from \"@vreko/infrastructure\";\nimport { syncUserToHubSpot } from \"@vreko/integrations\";\nimport { combinedSchema, db } from \"@vreko/platform\";\n\nimport { betterAuth } from \"better-auth\";\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\";\nimport { getOAuthState } from \"better-auth/api\";\nimport { admin, bearer, deviceAuthorization, magicLink, openAPI, organization } from \"better-auth/plugins\";\nimport { nanoid } from \"nanoid\";\nimport { trackEvent } from \"./lib/audit\";\nimport { apiKeyPlugin } from \"./plugins/api-key-plugin\";\n\n// Frontend app URL (for OAuth redirects and CORS)\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\nconst appUrl = process.env.APP_URL || getBaseUrl();\n\n// Better Auth base URL (where auth endpoints are served)\n// CRITICAL: Auth routes are in the WEB app, NOT the API server!\n// - Web app: Has /api/auth/* routes (this is where Better Auth runs)\n// - API server: Backend API only, no auth routes\n//\n// Priority: BETTER_AUTH_URL (explicit) > APP_URL > fallback\n// In dev: BETTER_AUTH_URL should be :3000 (web app with auth routes)\n// APP_URL might be :3003 (API server - no auth routes)\n//\n// IMPORTANT: This must be set or Better Auth will log warnings\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\nconst authBaseUrl =\n\tprocess.env.BETTER_AUTH_URL ||\n\tprocess.env.BETTER_AUTH_BASE_URL ||\n\tprocess.env.APP_URL ||\n\t`http://localhost:${process.env.PORT || 3000}`;\n\n// In development, trust all localhost ports to handle Next.js dynamic port assignment\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\n// Detect local development regardless of NODE_ENV.\n// Doppler prd config sets NODE_ENV=production even when running locally,\n// so we also check if BETTER_AUTH_URL is a localhost URL.\nconst isLocalDev = (process.env.BETTER_AUTH_URL || \"\").includes(\"localhost\");\nconst isDevelopment = process.env.NODE_ENV !== \"production\" || isLocalDev;\nconst trustedOrigins = isDevelopment\n\t? [\n\t\t\tappUrl,\n\t\t\tauthBaseUrl,\n\t\t\t\"http://localhost:3000\",\n\t\t\t\"http://localhost:3001\",\n\t\t\t\"http://localhost:3002\",\n\t\t\t\"http://localhost:3003\",\n\t\t]\n\t: [appUrl, authBaseUrl, \"https://console.vreko.dev\"].filter((url, index, arr) => arr.indexOf(url) === index);\n\nconst _auth = betterAuth({\n\t// ✅ Base URL for callbacks and redirects (required by Better Auth)\n\t// Uses BETTER_AUTH_URL env var, falling back to localhost with PORT\n\tbaseURL: authBaseUrl,\n\n\t// Custom user fields returned by getSession().\n\t// Must be under user.additionalFields — schema.user.fields only affects migrations,\n\t// not the session output path (Better Auth reads options.user.additionalFields in\n\t// getFields() / parseUserOutput()).\n\tuser: {\n\t\tadditionalFields: {\n\t\t\tonboardingComplete: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tdeviceFingerprint: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n\tappName: \"Vreko\",\n\n\t// ✅ SECURITY: Prevent account enumeration attacks\n\t// OWASP ASVS 2.2.1 - Don't reveal whether username exists\n\tdisablePaths: [\"/is-username-available\"],\n\n\tendpoints: {\n\t\tGET: {\n\t\t\t\"/health\": {\n\t\t\t\tasync handler() {\n\t\t\t\t\treturn new Response(\"OK\", { status: 200 });\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\temailAndPassword: {\n\t\tenabled: true,\n\t\t// Account lockout hooks are handled via middleware (see account-lockout.ts)\n\t\t// - incrementFailedAttempts() called on failed login\n\t\t// - resetFailedAttempts() called on successful login\n\t},\n\tsocialProviders: {\n\t\t// Only include social providers if credentials are configured\n\t\t// This prevents Better Auth from logging warnings that corrupt MCP stdio\n\t\t// Note: Access process.env directly for Vercel compatibility (t3-env wrapper issue)\n\t\t...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET\n\t\t\t? {\n\t\t\t\t\tgithub: {\n\t\t\t\t\t\tclientId: process.env.GITHUB_CLIENT_ID,\n\t\t\t\t\t\tclientSecret: process.env.GITHUB_CLIENT_SECRET,\n\t\t\t\t\t\t// Block OAuth auto-signup - require invite code flow\n\t\t\t\t\t\tdisableImplicitSignUp: true,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t\t...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET\n\t\t\t? {\n\t\t\t\t\tgoogle: {\n\t\t\t\t\t\tclientId: process.env.GOOGLE_CLIENT_ID,\n\t\t\t\t\t\tclientSecret: process.env.GOOGLE_CLIENT_SECRET,\n\t\t\t\t\t\t// Block OAuth auto-signup - require invite code flow\n\t\t\t\t\t\tdisableImplicitSignUp: true,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t},\n\tdatabase: drizzleAdapter(db, {\n\t\tprovider: \"pg\",\n\t\tschema: combinedSchema,\n\t}),\n\tsession: {\n\t\texpiresIn: 60 * 60 * 24 * 7, // 7 days\n\t\tupdateAge: 60 * 60 * 24, // 1 day\n\t\t// Always persist sessions in DB (no secondary storage)\n\t\tstoreSessionInDatabase: true,\n\t},\n\taccount: {\n\t\taccountLinking: {\n\t\t\tenabled: true,\n\t\t\ttrustedProviders: [\"google\", \"github\"],\n\t\t},\n\t},\n\ttrustedOrigins,\n\n\t// ✅ OPTIMIZATION: Redis secondary storage removed for alpha (DB-backed sessions)\n\t// Re-add if rate-limit distribution becomes a requirement post-launch.\n\n\tadvanced: {\n\t\t// ✅ FIX: Use isDevelopment (not NODE_ENV) so Doppler prd config running locally\n\t\t// still gets dev-friendly cookie settings. isDevelopment checks BETTER_AUTH_URL\n\t\t// for localhost, which Doppler overrides correctly.\n\t\tuseSecureCookies: !isDevelopment,\n\n\t\tcrossSiteRequestForgery: {\n\t\t\tenabled: true,\n\t\t\t// Verify origin header matches trusted origins\n\t\t\tcheckOrigin: true,\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: Explicit ID generation using nanoid\n\t\tdatabase: {\n\t\t\tgenerateId: () => nanoid(),\n\t\t\tdefaultFindManyLimit: 100, // Prevent unbounded queries\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: IP tracking configuration for security audit\n\t\tipAddress: {\n\t\t\tipAddressHeaders: [\n\t\t\t\t\"cf-connecting-ip\", // Cloudflare (highest priority)\n\t\t\t\t\"x-real-ip\", // Nginx proxy\n\t\t\t\t\"x-forwarded-for\", // Standard proxy header\n\t\t\t\t\"x-client-ip\",\n\t\t\t],\n\t\t\tdisableIpTracking: false, // Enable IP tracking for security\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: Enhanced cookie configuration\n\t\t// ✅ FIX: Use isDevelopment consistently (not env.NODE_ENV === \"production\")\n\t\t// Doppler prd sets NODE_ENV=production even when running locally, which would\n\t\t// incorrectly set domain=\".vreko.dev\" on localhost cookies.\n\t\tcrossSubDomainCookies: {\n\t\t\tenabled: !isDevelopment,\n\t\t\tdomain: !isDevelopment ? \".vreko.dev\" : undefined,\n\t\t},\n\n\t\tdefaultCookieAttributes: {\n\t\t\tsameSite: \"lax\",\n\t\t\tsecure: !isDevelopment,\n\t\t\thttpOnly: true,\n\t\t\tpath: \"/\",\n\t\t},\n\n\t\t// ✅ FIX: OAuth state/pkce cookies need SameSite=None for cross-site redirects\n\t\t// See: https://github.com/better-auth/better-auth/issues/6483\n\t\t// Note: In development (localhost), browsers allow SameSite=None without Secure\n\t\tcookies: {\n\t\t\tstate: {\n\t\t\t\tname: \"vreko.state\",\n\t\t\t\tattributes: {\n\t\t\t\t\tsameSite: isDevelopment ? \"lax\" : \"none\",\n\t\t\t\t\tsecure: !isDevelopment,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t\tmaxAge: 600, // 10 minutes (OAuth flow timeout)\n\t\t\t\t},\n\t\t\t},\n\t\t\tpkce: {\n\t\t\t\tname: \"vreko.pkce\",\n\t\t\t\tattributes: {\n\t\t\t\t\tsameSite: isDevelopment ? \"lax\" : \"none\",\n\t\t\t\t\tsecure: !isDevelopment,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t\tmaxAge: 600,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\tcookiePrefix: \"vreko\", // Namespace cookies\n\t},\n\t// Rate limiting configuration (replaces 340+ lines of custom rate limit code)\n\trateLimit: {\n\t\twindow: 60,\n\t\tmax: 100,\n\t\t// In-memory storage - sufficient for alpha single-instance deployment\n\t\tstorage: \"memory\",\n\t\tcustomRules: {\n\t\t\t\"/sign-in/email\": { window: 10, max: 3 },\n\t\t\t\"/sign-in/social\": { window: 10, max: 5 },\n\t\t\t\"/sign-up\": { window: 60, max: 5 },\n\t\t\t\"/health\": false,\n\t\t\t\"/health/ready\": false,\n\t\t\t\"/health/live\": false,\n\t\t},\n\t},\n\t// Use database hooks for audit logging (replaces 371 lines of custom auth-audit.ts)\n\t// Also includes rate limiting configuration (replaces 340+ lines of custom rate limit code)\n\tdatabaseHooks: {\n\t\tsession: {\n\t\t\tcreate: {\n\t\t\t\tafter: async (session: Record<string, unknown>) => {\n\t\t\t\t\tconst { userId, id: sessionId } = session as { userId: string; id: string };\n\t\t\t\t\t// Track session creation (indicates successful auth)\n\t\t\t\t\tawait trackEvent(\"session.created\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Also track auth.signin for returning users (session without new user)\n\t\t\t\t\t// This fires on every login (new session = successful signin)\n\t\t\t\t\tawait trackEvent(\"auth.signin\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\n\t\t\t\t\t// ✅ SECURITY: Session regeneration on login (prevents session fixation)\n\t\t\t\t\t// Invalidate any old sessions to prevent fixation attacks\n\t\t\t\t\t// OWASP A07:2021 - Session Fixation Prevention\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Delete all previous sessions for this user EXCEPT the current one\n\t\t\t\t\t\tconst { db } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tif (db) {\n\t\t\t\t\t\t\tconst result = await db.execute(sql`\n\t\t\t\t\t\t\t\tDELETE FROM session\n\t\t\t\t\t\t\t\tWHERE \"userId\" = ${userId}\n\t\t\t\t\t\t\t\tAND id != ${sessionId}\n\t\t\t\t\t\t\t`);\n\n\t\t\t\t\t\t\tconst rotatedCount = result.rowCount || 0;\n\t\t\t\t\t\t\tif (rotatedCount > 0) {\n\t\t\t\t\t\t\t\tlogger.info(\"Session regenerated on login - old sessions invalidated\", {\n\t\t\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\t\trotatedCount,\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tawait trackEvent(\"session.regenerated\", {\n\t\t\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\t\t\treason: \"login\",\n\t\t\t\t\t\t\t\t\trotatedCount,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Don't fail login if session rotation fails\n\t\t\t\t\t\tlogger.warn(\"Session regeneration failed on login\", {\n\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tdelete: {\n\t\t\t\tafter: async (session: Record<string, unknown>) => {\n\t\t\t\t\tconst { userId } = session as { userId: string };\n\t\t\t\t\t// Track logout/session revocation\n\t\t\t\t\tawait trackEvent(\"session.revoked\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Also track auth.signout for analytics funnel\n\t\t\t\t\tawait trackEvent(\"auth.signout\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tuser: {\n\t\t\tcreate: {\n\t\t\t\tbefore: async (user: Record<string, unknown>) => {\n\t\t\t\t\t// ✅ INVITE GATE: Block account creation unless a valid invite code was passed\n\t\t\t\t\t// via OAuth additionalData (threaded through OAuth state) or email sign-up.\n\t\t\t\t\t// The code is burned atomically - concurrent double-redemption is impossible.\n\n\t\t\t\t\t// For OAuth flows: Better Auth stores additionalData in AsyncLocalStorage via\n\t\t\t\t\t// setOAuthState() during parseState(). getOAuthState() reads it back here.\n\t\t\t\t\t// For email sign-up: getOAuthState() returns null; fall back to __inviteCode.\n\t\t\t\t\tconst oauthState = (await getOAuthState().catch(() => null)) as Record<string, unknown> | null;\n\t\t\t\t\tconst inviteCode =\n\t\t\t\t\t\t(oauthState?.inviteCode as string | undefined) ?? (user.__inviteCode as string | undefined);\n\n\t\t\t\t\t// Magic link pioneer path: check pendingApiKeys for a pioneer_magic_claim\n\t\t\t\t\tif (!inviteCode && user.email) {\n\t\t\t\t\t\tconst {\n\t\t\t\t\t\t\tpendingApiKeys: pendingApiKeysTable,\n\t\t\t\t\t\t\tinviteCodes: inviteCodesTable,\n\t\t\t\t\t\t\tburnInviteCode,\n\t\t\t\t\t\t} = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { and, eq, gt, isNull, sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tconst claim = await db\n\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t.from(pendingApiKeysTable)\n\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\teq(pendingApiKeysTable.userId, user.email as string),\n\t\t\t\t\t\t\t\t\teq(pendingApiKeysTable.purpose, \"pioneer_magic_claim\"),\n\t\t\t\t\t\t\t\t\tgt(pendingApiKeysTable.expiresAt, new Date()),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\tif (claim.length > 0) {\n\t\t\t\t\t\t\tconst codeRow = await db\n\t\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t\t.from(inviteCodesTable)\n\t\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\t\tsql`lower(${inviteCodesTable.invitedEmail}) = lower(${user.email as string})`,\n\t\t\t\t\t\t\t\t\t\tsql`${inviteCodesTable.currentUses} < ${inviteCodesTable.maxUses}`,\n\t\t\t\t\t\t\t\t\t\tisNull(inviteCodesTable.revokedAt),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\t\tif (codeRow.length > 0) {\n\t\t\t\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: length > 0 checked above\n\t\t\t\t\t\t\t\tawait burnInviteCode(db, codeRow[0]!.id);\n\t\t\t\t\t\t\t\tawait db\n\t\t\t\t\t\t\t\t\t.delete(pendingApiKeysTable)\n\t\t\t\t\t\t\t\t\t.where(eq(pendingApiKeysTable.userId, user.email as string));\n\t\t\t\t\t\t\t\treturn { data: user }; // allow account creation\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!inviteCode) {\n\t\t\t\t\t\tthrow new Error(\"INVITE_REQUIRED: An invite code is required to create an account.\");\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { db } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { inviteCodes } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { and, eq, sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tconst code = await db\n\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t.from(inviteCodes)\n\t\t\t\t\t\t\t.where(eq(inviteCodes.code, inviteCode))\n\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\tif (code.length === 0) {\n\t\t\t\t\t\t\tthrow new Error(\"INVALID_INVITE_CODE\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: length === 0 checked above\n\t\t\t\t\t\tconst entry = code[0]!;\n\t\t\t\t\t\tif (entry.expiresAt && entry.expiresAt < new Date()) {\n\t\t\t\t\t\t\tthrow new Error(\"INVITE_CODE_EXPIRED\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic burn: UPDATE WHERE currentUses < maxUses RETURNING *\n\t\t\t\t\t\tconst burned = await db\n\t\t\t\t\t\t\t.update(inviteCodes)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tcurrentUses: sql`${inviteCodes.currentUses} + 1`,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\teq(inviteCodes.id, entry.id),\n\t\t\t\t\t\t\t\t\tsql`${inviteCodes.currentUses} < ${inviteCodes.maxUses}`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.returning();\n\n\t\t\t\t\t\tif (burned.length === 0) {\n\t\t\t\t\t\t\tthrow new Error(\"INVITE_CODE_EXHAUSTED\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (error instanceof Error && error.message.startsWith(\"INVITE\")) {\n\t\t\t\t\t\t\tthrow error; // propagate gate errors\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlogger.error(\"Invite gate DB error\", { error });\n\t\t\t\t\t\tthrow new Error(\"INVITE_GATE_ERROR\");\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { data: user };\n\t\t\t\t},\n\t\t\t\tafter: async (user: Record<string, unknown>) => {\n\t\t\t\t\tconst { id: userId, email } = user as { id: string; email: string };\n\t\t\t\t\t// Track new user signup\n\t\t\t\t\tawait trackEvent(\"auth.signup\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Fire-and-forget HubSpot contact creation - outages must not block signup\n\t\t\t\t\tsyncUserToHubSpot({ userId, email, signupSource: \"app\" }).catch((err) => {\n\t\t\t\t\t\tlogger.warn(\"HubSpot signup sync failed (non-blocking)\", {\n\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\t// API key creation on signup disabled - apiKey plugin removed in better-auth v1.6.5\n\t\t\t\t\t// Users can create API keys manually from the dashboard.\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tplugins: [\n\t\tbearer(),\n\t\tdeviceAuthorization({\n\t\t\tverificationUri: `${appUrl}/link`,\n\t\t\tschema: {},\n\t\t}),\n\t\tadmin(),\n\t\topenAPI(),\n\t\t// magicLink() - server-side handler for POST /api/auth/sign-in/magic-link\n\t\tmagicLink({\n\t\t\tsendMagicLink: async ({ email, url }) => {\n\t\t\t\tawait sendEmail({\n\t\t\t\t\tfrom: \"Vreko <noreply@vreko.dev>\",\n\t\t\t\t\tto: email,\n\t\t\t\t\tsubject: \"Sign in to Vreko\",\n\t\t\t\t\thtml: `<p>Click <a href=\"${url}\">here</a> to sign in. This link expires in 5 minutes.</p>`,\n\t\t\t\t});\n\t\t\t},\n\t\t}),\n\t\t// organization() - server-side org endpoints (schema: packages/platform/src/db/schema/postgres.ts)\n\t\torganization(),\n\t\t// API key management (createApiKey / verifyApiKey / deleteApiKey)\n\t\t// better-auth v1.6.5 removed the built-in apiKey() plugin; this custom\n\t\t// plugin re-implements the same surface backed by the api_keys DB table.\n\t\tapiKeyPlugin(),\n\t],\n\tonAPIError: {\n\t\tonError(error: unknown, ctx: unknown) {\n\t\t\t// Enhanced error logging with OAuth-specific context\n\t\t\tconst errorDetails: Record<string, unknown> = {\n\t\t\t\terror,\n\t\t\t\tcontext: ctx,\n\t\t\t};\n\n\t\t\t// Extract additional context from error object\n\t\t\tif (error && typeof error === \"object\") {\n\t\t\t\tif (\"code\" in error) {\n\t\t\t\t\terrorDetails.errorCode = error.code;\n\t\t\t\t}\n\t\t\t\tif (\"message\" in error) {\n\t\t\t\t\terrorDetails.errorMessage = error.message;\n\t\t\t\t}\n\t\t\t\tif (\"statusCode\" in error) {\n\t\t\t\t\terrorDetails.statusCode = error.statusCode;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Extract context information (e.g., request path, method)\n\t\t\tlet isOAuthError = false;\n\t\t\tif (ctx && typeof ctx === \"object\") {\n\t\t\t\tif (\"request\" in ctx) {\n\t\t\t\t\tconst request = ctx.request as { url?: string; method?: string };\n\t\t\t\t\terrorDetails.requestUrl = request.url;\n\t\t\t\t\terrorDetails.requestMethod = request.method;\n\n\t\t\t\t\t// Check if this is an OAuth callback error\n\t\t\t\t\tif (request.url?.includes(\"/api/auth/callback/\")) {\n\t\t\t\t\t\tconst provider = request.url.split(\"/callback/\")[1]?.split(\"?\")[0];\n\t\t\t\t\t\terrorDetails.oauthProvider = provider;\n\t\t\t\t\t\terrorDetails.errorType = \"OAuth Callback Error\";\n\t\t\t\t\t\tisOAuthError = true;\n\n\t\t\t\t\t\tlogger.error(\"OAuth Callback Error\", errorDetails);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Log auth error\n\t\t\tif (!isOAuthError) {\n\t\t\t\tlogger.error(\"Better Auth API Error\", errorDetails);\n\t\t\t}\n\n\t\t\t// Track failed auth in PostHog (non-blocking)\n\t\t\ttrackEvent(\"auth.signin_failed\", {\n\t\t\t\terrorCode: errorDetails.errorCode as string | undefined,\n\t\t\t\terrorMessage: errorDetails.errorMessage as string | undefined,\n\t\t\t\tpath: errorDetails.requestUrl as string | undefined,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t}).catch(() => {\n\t\t\t\t// Ignore tracking errors\n\t\t\t});\n\n\t\t\t// Send to Sentry for monitoring (lazy import to avoid startup overhead)\n\t\t\timport(\"@vreko/infrastructure\")\n\t\t\t\t.then(({ captureError }) => {\n\t\t\t\t\tif (captureError && error instanceof Error) {\n\t\t\t\t\t\tcaptureError(error, {\n\t\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\t\terrorType: isOAuthError ? \"oauth_callback\" : \"auth_api\",\n\t\t\t\t\t\t\t\t...(errorDetails.errorCode ? { errorCode: String(errorDetails.errorCode) } : {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\textra: errorDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {\n\t\t\t\t\t// Sentry not available, already logged\n\t\t\t\t});\n\t\t},\n\t},\n});\n\n// ============================================================================\n// Plugin API Type Declarations\n// ============================================================================\n// Better Auth's InferAPI doesn't emit stable types for plugins, causing\n// \"Maximum call stack size exceeded\" during builds. We define explicit types\n// for the plugin APIs we use.\n\n/** API Key plugin types */\nexport interface CreateApiKeyParams {\n\tbody: {\n\t\tname: string;\n\t\tuserId: string;\n\t\tpermissions?: Record<string, string[]>;\n\t\trateLimitEnabled?: boolean;\n\t\trateLimitMax?: number;\n\t\trateLimitTimeWindow?: number;\n\t\texpiresAt?: Date;\n\t\tmetadata?: Record<string, unknown>;\n\t};\n}\n\nexport interface CreateApiKeyResult {\n\tid: string;\n\tkey: string;\n\tname: string;\n\tcreatedAt: Date;\n}\n\n/** verifyApiKey accepts key directly or in body wrapper (support both patterns) */\nexport interface VerifyApiKeyParams {\n\tkey?: string;\n\tpermissions?: Record<string, string[]>;\n\tbody?: {\n\t\tkey: string;\n\t\tpermissions?: Record<string, string[]>;\n\t};\n}\n\nexport interface VerifyApiKeyResult {\n\tvalid?: boolean;\n\tisValid?: boolean; // Alternative property name used by some consumers\n\tuserId?: string;\n\tkey?: {\n\t\tid: string;\n\t\tuserId: string;\n\t\tname: string;\n\t\tpermissions?: Record<string, string[]>;\n\t\trateLimitEnabled?: boolean;\n\t\trateLimitMax?: number;\n\t\tremaining?: number;\n\t\tlastRefillAt?: Date;\n\t\tmetadata?: Record<string, unknown>;\n\t};\n\tmetadata?: Record<string, unknown>;\n\tpermissions?: Record<string, string[]>;\n\terror?: { message: string; code?: string };\n}\n\nexport interface DeleteApiKeyParams {\n\tbody: { keyId: string };\n}\n\n/** Sign-in types */\nexport interface SignInEmailParams {\n\tbody: {\n\t\temail: string;\n\t\tpassword: string;\n\t};\n}\n\nexport interface SignInEmailResult {\n\tuser?: { id: string; email: string };\n\tsession?: { id: string };\n}\n\n/** Extended auth.api type with plugin methods */\nexport interface VrekoAuthAPI {\n\t// Core methods (from base better-auth)\n\tgetSession: (params: { headers: Headers }) => Promise<{\n\t\tsession: { id: string; userId: string; expiresAt: Date } | null;\n\t\tuser: {\n\t\t\tid: string;\n\t\t\temail: string;\n\t\t\tname: string;\n\t\t\temailVerified: boolean;\n\t\t\timage?: string | null;\n\t\t\tcreatedAt: Date;\n\t\t\tupdatedAt: Date;\n\t\t} | null;\n\t}>;\n\n\t// OpenAPI schema generation (Better Auth built-in)\n\tgenerateOpenAPISchema: () => Promise<Record<string, unknown>>;\n\n\t// Sign-in method\n\tsignInEmail: (params: SignInEmailParams) => Promise<SignInEmailResult>;\n\n\t// API Key plugin (primary auth mechanism for CLI/Extension)\n\tcreateApiKey: (params: CreateApiKeyParams) => Promise<CreateApiKeyResult>;\n\tverifyApiKey: (params: VerifyApiKeyParams) => Promise<VerifyApiKeyResult>;\n\tdeleteApiKey: (params: DeleteApiKeyParams) => Promise<void>;\n}\n\n/** Password context from Better Auth's internal AuthContext */\nexport interface VrekoPasswordContext {\n\thash: (password: string) => Promise<string>;\n\tverify: (args: { hash: string; password: string }) => Promise<boolean>;\n}\n\n/** Minimal AuthContext type for $context access */\nexport interface VrekoAuthContext {\n\tpassword: VrekoPasswordContext;\n}\n\n/** Auth instance type with plugin APIs */\nexport interface VrekoAuthInstance {\n\t// biome-ignore lint/suspicious/noExplicitAny: better-auth api shape is complex; typed as any for call-site flexibility\n\tapi: any;\n\thandler: (request: Request) => Promise<Response>;\n\t/** Internal Better Auth context - use for password hashing in scripts */\n\t$context: Promise<VrekoAuthContext>;\n}\n\n/** Typed auth export with plugin APIs - cast to avoid complex type inference */\nexport const auth = _auth as unknown as VrekoAuthInstance;\n\nexport * from \"./lib/organization\";\n\n// Type exports moved to appropriate locations:\n// - Session: Use 'better-auth/types' or '@vreko/contracts/auth/session'\n// - Organization types: Use Better Auth organization plugin types\n// Removed dangerous 'any' stub exports (audit finding #1)\n"]}
1
+ {"version":3,"sources":["../src/plugins/api-key-plugin.ts","../src/auth.ts"],"names":["createApiKeyBodySchema","z","object","name","string","optional","default","organizationId","permissions","maxSnapshots","number","cloudBackup","boolean","advancedDetection","customRules","teamSharing","rateLimitEnabled","rateLimitMax","int","rateLimitTimeWindow","expiresAt","coerce","date","metadata","record","unknown","verifyApiKeyBodySchema","key","array","deleteApiKeyBodySchema","keyId","apiKeyPlugin","id","endpoints","createApiKey","createAuthEndpoint","method","body","openapi","operationId","description","responses","content","schema","type","properties","createdAt","format","nullable","ctx","sessionData","getSessionFromCtx","user","APIError","message","userId","plainKey","generateAPIKey","keyHash","hashAPIKey","start","substring","keyPreview","nanoid","now","Date","db","insert","apiKeys","values","prefix","enabled","updatedAt","logger","info","json","verifyApiKey","isValid","error","candidates","select","from","where","and","eq","isNull","revokedAt","or","gte","limit","matchedKey","candidate","matches","verifyAPIKeyHash","update","set","lastUsedAt","catch","err","warn","valid","remaining","lastRefillAt","deleteApiKey","result","rowCount","success","appUrl","process","env","APP_URL","getBaseUrl","authBaseUrl","BETTER_AUTH_URL","BETTER_AUTH_BASE_URL","PORT","isLocalDev","includes","isDevelopment","NODE_ENV","trustedOrigins","filter","url","index","arr","indexOf","_auth","betterAuth","baseURL","additionalFields","onboardingComplete","required","defaultValue","deviceFingerprint","appName","disablePaths","GET","handler","Response","status","emailAndPassword","socialProviders","GITHUB_CLIENT_ID","GITHUB_CLIENT_SECRET","github","clientId","clientSecret","disableImplicitSignUp","GOOGLE_CLIENT_ID","GOOGLE_CLIENT_SECRET","google","database","drizzleAdapter","provider","combinedSchema","session","expiresIn","updateAge","storeSessionInDatabase","account","accountLinking","trustedProviders","advanced","useSecureCookies","crossSiteRequestForgery","checkOrigin","generateId","defaultFindManyLimit","ipAddress","ipAddressHeaders","disableIpTracking","crossSubDomainCookies","domain","undefined","defaultCookieAttributes","sameSite","secure","httpOnly","path","cookies","state","attributes","maxAge","pkce","cookiePrefix","rateLimit","window","max","storage","databaseHooks","create","after","sessionId","trackEvent","timestamp","toISOString","sql","execute","rotatedCount","reason","Error","String","delete","before","oauthState","getOAuthState","inviteCode","__inviteCode","email","pendingApiKeys","pendingApiKeysTable","inviteCodes","inviteCodesTable","burnInviteCode","gt","claim","purpose","length","codeRow","invitedEmail","currentUses","maxUses","data","code","entry","burned","returning","startsWith","syncUserToHubSpot","signupSource","plugins","bearer","deviceAuthorization","verificationUri","admin","openAPI","magicLink","sendMagicLink","sendEmail","to","subject","html","organization","onAPIError","onError","errorDetails","context","errorCode","errorMessage","statusCode","isOAuthError","request","requestUrl","requestMethod","split","oauthProvider","errorType","then","captureError","tags","extra","auth"],"mappings":"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","file":"chunk-3OCLD7XA.js","sourcesContent":["/**\n * Custom API Key Plugin for Better Auth v1.6.5\n *\n * better-auth v1.6.5 removed the built-in apiKey() plugin.\n * This plugin re-implements the createApiKey / verifyApiKey / deleteApiKey\n * surface using Better Auth's plugin endpoint API so that\n * `auth.api.createApiKey`, `auth.api.verifyApiKey`, and\n * `auth.api.deleteApiKey` are defined at runtime.\n *\n * Storage: uses @vreko/platform's Drizzle `apiKeys` table directly.\n * Key generation: uses packages/auth/src/security/api-key-security.ts.\n */\n\nimport { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { logger } from \"@vreko/infrastructure\";\nimport { apiKeys, db } from \"@vreko/platform\";\nimport { getSessionFromCtx } from \"better-auth/api\";\nimport { and, eq, gte, isNull, or } from \"drizzle-orm\";\nimport { nanoid } from \"nanoid\";\nimport { z } from \"zod\";\nimport { generateAPIKey, hashAPIKey, verifyAPIKeyHash } from \"../security/api-key-security\";\n\n// ─── Schemas ────────────────────────────────────────────────────────────────\n\nconst createApiKeyBodySchema = z.object({\n\t/** User-facing name for this key */\n\tname: z.string().optional().default(\"Default Key\"),\n\t/** Optional org scoping */\n\torganizationId: z.string().optional(),\n\t/** Structured permissions object — mirrors the apiKeys.permissions column type */\n\tpermissions: z\n\t\t.object({\n\t\t\tmaxSnapshots: z.number().optional(),\n\t\t\tcloudBackup: z.boolean().optional(),\n\t\t\tadvancedDetection: z.boolean().optional(),\n\t\t\tcustomRules: z.boolean().optional(),\n\t\t\tteamSharing: z.boolean().optional(),\n\t\t})\n\t\t.optional(),\n\t/** Whether rate limiting is enabled for this key */\n\trateLimitEnabled: z.boolean().optional().default(false),\n\t/** Max requests per rate-limit window */\n\trateLimitMax: z.number().int().optional(),\n\t/** Rate limit window in milliseconds */\n\trateLimitTimeWindow: z.number().int().optional(),\n\t/** Optional expiry date */\n\texpiresAt: z.coerce.date().optional(),\n\t/** Arbitrary metadata */\n\tmetadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nconst verifyApiKeyBodySchema = z.object({\n\t/** The plaintext API key to verify */\n\tkey: z.string(),\n\t/** Optional permissions to check against */\n\tpermissions: z.record(z.string(), z.array(z.string())).optional(),\n});\n\nconst deleteApiKeyBodySchema = z.object({\n\t/** The DB id of the key to delete / revoke */\n\tkeyId: z.string(),\n});\n\n// ─── Plugin ─────────────────────────────────────────────────────────────────\n\n/**\n * Better Auth plugin that provides API key management endpoints.\n *\n * Registers:\n * POST /api/auth/api-key/create → auth.api.createApiKey\n * POST /api/auth/api-key/verify → auth.api.verifyApiKey\n * POST /api/auth/api-key/delete → auth.api.deleteApiKey\n */\nexport function apiKeyPlugin() {\n\treturn {\n\t\tid: \"vreko-api-key\" as const,\n\n\t\tendpoints: {\n\t\t\tcreateApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/create\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: createApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"createApiKey\",\n\t\t\t\t\t\t\tdescription: \"Create a new API key for the given user\",\n\t\t\t\t\t\t\tresponses: {\n\t\t\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\t\t\tdescription: \"API key created\",\n\t\t\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tid: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tname: { type: \"string\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\tcreatedAt: { type: \"string\", format: \"date-time\" },\n\t\t\t\t\t\t\t\t\t\t\t\t\texpiresAt: { type: \"string\", format: \"date-time\", nullable: true },\n\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst sessionData = await getSessionFromCtx(ctx);\n\t\t\t\t\tif (!sessionData?.user?.id) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", { message: \"Unauthorized\" });\n\t\t\t\t\t}\n\t\t\t\t\tconst userId = sessionData.user.id;\n\n\t\t\t\t\tconst {\n\t\t\t\t\t\tname,\n\t\t\t\t\t\torganizationId,\n\t\t\t\t\t\tpermissions,\n\t\t\t\t\t\trateLimitEnabled,\n\t\t\t\t\t\trateLimitMax,\n\t\t\t\t\t\trateLimitTimeWindow,\n\t\t\t\t\t\texpiresAt,\n\t\t\t\t\t\tmetadata,\n\t\t\t\t\t} = ctx.body;\n\n\t\t\t\t\t// Generate a plaintext key (only returned once)\n\t\t\t\t\tconst plainKey = generateAPIKey(true);\n\t\t\t\t\t// Hash with Argon2id for storage\n\t\t\t\t\tconst keyHash = await hashAPIKey(plainKey);\n\n\t\t\t\t\t// \"start\" = first 8 chars of the plaintext key (Better Auth convention for lookup)\n\t\t\t\t\tconst start = plainKey.substring(0, 8);\n\t\t\t\t\t// preview = first 12 chars for display (includes prefix + a few chars)\n\t\t\t\t\tconst keyPreview = plainKey.substring(0, 12);\n\n\t\t\t\t\tconst id = nanoid();\n\t\t\t\t\tconst now = new Date();\n\n\t\t\t\t\tawait db.insert(apiKeys).values({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\torganizationId: organizationId ?? null,\n\t\t\t\t\t\tname: name ?? \"Default Key\",\n\t\t\t\t\t\tkey: keyHash,\n\t\t\t\t\t\tstart,\n\t\t\t\t\t\tprefix: \"sk_live_\",\n\t\t\t\t\t\tkeyPreview,\n\t\t\t\t\t\tpermissions: permissions ?? null,\n\t\t\t\t\t\tenabled: true,\n\t\t\t\t\t\trateLimitEnabled: rateLimitEnabled ?? false,\n\t\t\t\t\t\trateLimitMax: rateLimitMax ?? null,\n\t\t\t\t\t\trateLimitTimeWindow: rateLimitTimeWindow ?? null,\n\t\t\t\t\t\texpiresAt: expiresAt ?? null,\n\t\t\t\t\t\tmetadata: metadata ?? null,\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\tupdatedAt: now,\n\t\t\t\t\t});\n\n\t\t\t\t\tlogger.info(\"API key created\", { userId, keyId: id });\n\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tid,\n\t\t\t\t\t\tkey: plainKey, // plaintext returned only once\n\t\t\t\t\t\tname: name ?? \"Default Key\",\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t\texpiresAt: expiresAt ?? null,\n\t\t\t\t\t\tpermissions: permissions ?? null,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\n\t\t\tverifyApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/verify\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: verifyApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"verifyApiKey\",\n\t\t\t\t\t\t\tdescription: \"Verify an API key and return its metadata\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst { key } = ctx.body;\n\n\t\t\t\t\tif (!key) {\n\t\t\t\t\t\treturn ctx.json({ isValid: false, error: { message: \"Key is required\" } });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Derive the `start` prefix from the submitted key for fast lookup.\n\t\t\t\t\t// Better Auth v1.x convention: `start` = first 8 chars of the plaintext key.\n\t\t\t\t\tconst start = key.substring(0, 8);\n\n\t\t\t\t\t// Find candidates by prefix + not revoked + not expired\n\t\t\t\t\tconst candidates = await db\n\t\t\t\t\t\t.select()\n\t\t\t\t\t\t.from(apiKeys)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\teq(apiKeys.start, start),\n\t\t\t\t\t\t\t\tisNull(apiKeys.revokedAt),\n\t\t\t\t\t\t\t\tor(isNull(apiKeys.expiresAt), gte(apiKeys.expiresAt, new Date())),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.limit(5); // at most a handful of keys share the same 8-char prefix\n\n\t\t\t\t\t// Find the matching key via Argon2id verification\n\t\t\t\t\tlet matchedKey: (typeof candidates)[0] | null = null;\n\t\t\t\t\tfor (const candidate of candidates) {\n\t\t\t\t\t\tconst matches = await verifyAPIKeyHash(key, candidate.key);\n\t\t\t\t\t\tif (matches) {\n\t\t\t\t\t\t\tmatchedKey = candidate;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!matchedKey) {\n\t\t\t\t\t\treturn ctx.json({ isValid: false, error: { message: \"Invalid API key\" } });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Update lastUsedAt (non-blocking)\n\t\t\t\t\tdb.update(apiKeys)\n\t\t\t\t\t\t.set({ lastUsedAt: new Date(), updatedAt: new Date() })\n\t\t\t\t\t\t.where(eq(apiKeys.id, matchedKey.id))\n\t\t\t\t\t\t.catch((err: Error) => {\n\t\t\t\t\t\t\tlogger.warn(\"Failed to update lastUsedAt for API key\", {\n\t\t\t\t\t\t\t\tkeyId: matchedKey!.id,\n\t\t\t\t\t\t\t\terror: err.message,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t});\n\n\t\t\t\t\treturn ctx.json({\n\t\t\t\t\t\tisValid: true,\n\t\t\t\t\t\tvalid: true, // legacy alias for consumers checking `.valid`\n\t\t\t\t\t\tuserId: matchedKey.userId,\n\t\t\t\t\t\tkey: {\n\t\t\t\t\t\t\tid: matchedKey.id,\n\t\t\t\t\t\t\tuserId: matchedKey.userId,\n\t\t\t\t\t\t\tname: matchedKey.name,\n\t\t\t\t\t\t\tpermissions: matchedKey.permissions,\n\t\t\t\t\t\t\trateLimitEnabled: matchedKey.rateLimitEnabled,\n\t\t\t\t\t\t\trateLimitMax: matchedKey.rateLimitMax,\n\t\t\t\t\t\t\tremaining: matchedKey.remaining,\n\t\t\t\t\t\t\tlastRefillAt: matchedKey.lastRefillAt,\n\t\t\t\t\t\t\tmetadata: matchedKey.metadata,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t),\n\n\t\t\tdeleteApiKey: createAuthEndpoint(\n\t\t\t\t\"/api-key/delete\",\n\t\t\t\t{\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\tbody: deleteApiKeyBodySchema,\n\t\t\t\t\tmetadata: {\n\t\t\t\t\t\topenapi: {\n\t\t\t\t\t\t\toperationId: \"deleteApiKey\",\n\t\t\t\t\t\t\tdescription: \"Revoke (soft-delete) an API key by ID\",\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tasync (ctx) => {\n\t\t\t\t\tconst sessionData = await getSessionFromCtx(ctx);\n\t\t\t\t\tif (!sessionData?.user?.id) {\n\t\t\t\t\t\tthrow new APIError(\"UNAUTHORIZED\", { message: \"Unauthorized\" });\n\t\t\t\t\t}\n\n\t\t\t\t\tconst { keyId } = ctx.body;\n\n\t\t\t\t\tconst result = await db\n\t\t\t\t\t\t.update(apiKeys)\n\t\t\t\t\t\t.set({ revokedAt: new Date(), updatedAt: new Date() })\n\t\t\t\t\t\t.where(and(eq(apiKeys.id, keyId), eq(apiKeys.userId, sessionData.user.id)));\n\n\t\t\t\t\t// Zero rows affected means the key didn't exist or didn't belong to caller\n\t\t\t\t\tif (!result.rowCount) {\n\t\t\t\t\t\tthrow new APIError(\"NOT_FOUND\", { message: \"Not found\" });\n\t\t\t\t\t}\n\n\t\t\t\t\tlogger.info(\"API key revoked\", { keyId, userId: sessionData.user.id });\n\n\t\t\t\t\treturn ctx.json({ success: true });\n\t\t\t\t},\n\t\t\t),\n\t\t},\n\t} satisfies import(\"@better-auth/core\").BetterAuthPlugin;\n}\n","import { getBaseUrl } from \"@vreko/config/server\";\nimport { sendEmail } from \"@vreko/email/resend\";\nimport { logger } from \"@vreko/infrastructure\";\nimport { syncUserToHubSpot } from \"@vreko/integrations\";\nimport { combinedSchema, db } from \"@vreko/platform\";\n\nimport { betterAuth } from \"better-auth\";\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\";\nimport { getOAuthState } from \"better-auth/api\";\nimport { admin, bearer, deviceAuthorization, magicLink, openAPI, organization } from \"better-auth/plugins\";\nimport { nanoid } from \"nanoid\";\nimport { trackEvent } from \"./lib/audit\";\nimport { apiKeyPlugin } from \"./plugins/api-key-plugin\";\n\n// Frontend app URL (for OAuth redirects and CORS)\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\nconst appUrl = process.env.APP_URL || getBaseUrl();\n\n// Better Auth base URL (where auth endpoints are served)\n// CRITICAL: Auth routes are in the WEB app, NOT the API server!\n// - Web app: Has /api/auth/* routes (this is where Better Auth runs)\n// - API server: Backend API only, no auth routes\n//\n// Priority: BETTER_AUTH_URL (explicit) > APP_URL > fallback\n// In dev: BETTER_AUTH_URL should be :3000 (web app with auth routes)\n// APP_URL might be :3003 (API server - no auth routes)\n//\n// IMPORTANT: This must be set or Better Auth will log warnings\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\nconst authBaseUrl =\n\tprocess.env.BETTER_AUTH_URL ||\n\tprocess.env.BETTER_AUTH_BASE_URL ||\n\tprocess.env.APP_URL ||\n\t`http://localhost:${process.env.PORT || 3000}`;\n\n// In development, trust all localhost ports to handle Next.js dynamic port assignment\n// Access process.env directly for Vercel compatibility (t3-env wrapper issue)\n// Detect local development regardless of NODE_ENV.\n// Doppler prd config sets NODE_ENV=production even when running locally,\n// so we also check if BETTER_AUTH_URL is a localhost URL.\nconst isLocalDev = (process.env.BETTER_AUTH_URL || \"\").includes(\"localhost\");\nconst isDevelopment = process.env.NODE_ENV !== \"production\" || isLocalDev;\nconst trustedOrigins = isDevelopment\n\t? [\n\t\t\tappUrl,\n\t\t\tauthBaseUrl,\n\t\t\t\"http://localhost:3000\",\n\t\t\t\"http://localhost:3001\",\n\t\t\t\"http://localhost:3002\",\n\t\t\t\"http://localhost:3003\",\n\t\t]\n\t: [appUrl, authBaseUrl, \"https://console.vreko.dev\"].filter((url, index, arr) => arr.indexOf(url) === index);\n\nconst _auth = betterAuth({\n\t// ✅ Base URL for callbacks and redirects (required by Better Auth)\n\t// Uses BETTER_AUTH_URL env var, falling back to localhost with PORT\n\tbaseURL: authBaseUrl,\n\n\t// Custom user fields returned by getSession().\n\t// Must be under user.additionalFields — schema.user.fields only affects migrations,\n\t// not the session output path (Better Auth reads options.user.additionalFields in\n\t// getFields() / parseUserOutput()).\n\tuser: {\n\t\tadditionalFields: {\n\t\t\tonboardingComplete: {\n\t\t\t\ttype: \"boolean\",\n\t\t\t\trequired: false,\n\t\t\t\tdefaultValue: false,\n\t\t\t},\n\t\t\tdeviceFingerprint: {\n\t\t\t\ttype: \"string\",\n\t\t\t\trequired: false,\n\t\t\t},\n\t\t},\n\t},\n\tappName: \"Vreko\",\n\n\t// ✅ SECURITY: Prevent account enumeration attacks\n\t// OWASP ASVS 2.2.1 - Don't reveal whether username exists\n\tdisablePaths: [\"/is-username-available\"],\n\n\tendpoints: {\n\t\tGET: {\n\t\t\t\"/health\": {\n\t\t\t\tasync handler() {\n\t\t\t\t\treturn new Response(\"OK\", { status: 200 });\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\temailAndPassword: {\n\t\tenabled: true,\n\t\t// Account lockout hooks are handled via middleware (see account-lockout.ts)\n\t\t// - incrementFailedAttempts() called on failed login\n\t\t// - resetFailedAttempts() called on successful login\n\t},\n\tsocialProviders: {\n\t\t// Only include social providers if credentials are configured\n\t\t// This prevents Better Auth from logging warnings that corrupt MCP stdio\n\t\t// Note: Access process.env directly for Vercel compatibility (t3-env wrapper issue)\n\t\t...(process.env.GITHUB_CLIENT_ID && process.env.GITHUB_CLIENT_SECRET\n\t\t\t? {\n\t\t\t\t\tgithub: {\n\t\t\t\t\t\tclientId: process.env.GITHUB_CLIENT_ID,\n\t\t\t\t\t\tclientSecret: process.env.GITHUB_CLIENT_SECRET,\n\t\t\t\t\t\t// Block OAuth auto-signup - require invite code flow\n\t\t\t\t\t\tdisableImplicitSignUp: true,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t\t...(process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET\n\t\t\t? {\n\t\t\t\t\tgoogle: {\n\t\t\t\t\t\tclientId: process.env.GOOGLE_CLIENT_ID,\n\t\t\t\t\t\tclientSecret: process.env.GOOGLE_CLIENT_SECRET,\n\t\t\t\t\t\t// Block OAuth auto-signup - require invite code flow\n\t\t\t\t\t\tdisableImplicitSignUp: true,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t: {}),\n\t},\n\tdatabase: drizzleAdapter(db, {\n\t\tprovider: \"pg\",\n\t\tschema: combinedSchema,\n\t}),\n\tsession: {\n\t\texpiresIn: 60 * 60 * 24 * 7, // 7 days\n\t\tupdateAge: 60 * 60 * 24, // 1 day\n\t\t// Always persist sessions in DB (no secondary storage)\n\t\tstoreSessionInDatabase: true,\n\t},\n\taccount: {\n\t\taccountLinking: {\n\t\t\tenabled: true,\n\t\t\ttrustedProviders: [\"google\", \"github\"],\n\t\t},\n\t},\n\ttrustedOrigins,\n\n\t// ✅ OPTIMIZATION: Redis secondary storage removed for alpha (DB-backed sessions)\n\t// Re-add if rate-limit distribution becomes a requirement post-launch.\n\n\tadvanced: {\n\t\t// ✅ FIX: Use isDevelopment (not NODE_ENV) so Doppler prd config running locally\n\t\t// still gets dev-friendly cookie settings. isDevelopment checks BETTER_AUTH_URL\n\t\t// for localhost, which Doppler overrides correctly.\n\t\tuseSecureCookies: !isDevelopment,\n\n\t\tcrossSiteRequestForgery: {\n\t\t\tenabled: true,\n\t\t\t// Verify origin header matches trusted origins\n\t\t\tcheckOrigin: true,\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: Explicit ID generation using nanoid\n\t\tdatabase: {\n\t\t\tgenerateId: () => nanoid(),\n\t\t\tdefaultFindManyLimit: 100, // Prevent unbounded queries\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: IP tracking configuration for security audit\n\t\tipAddress: {\n\t\t\tipAddressHeaders: [\n\t\t\t\t\"cf-connecting-ip\", // Cloudflare (highest priority)\n\t\t\t\t\"x-real-ip\", // Nginx proxy\n\t\t\t\t\"x-forwarded-for\", // Standard proxy header\n\t\t\t\t\"x-client-ip\",\n\t\t\t],\n\t\t\tdisableIpTracking: false, // Enable IP tracking for security\n\t\t},\n\n\t\t// ✅ OPTIMIZATION: Enhanced cookie configuration\n\t\t// ✅ FIX: Use isDevelopment consistently (not env.NODE_ENV === \"production\")\n\t\t// Doppler prd sets NODE_ENV=production even when running locally, which would\n\t\t// incorrectly set domain=\".vreko.dev\" on localhost cookies.\n\t\tcrossSubDomainCookies: {\n\t\t\tenabled: !isDevelopment,\n\t\t\tdomain: !isDevelopment ? \".vreko.dev\" : undefined,\n\t\t},\n\n\t\tdefaultCookieAttributes: {\n\t\t\tsameSite: \"lax\",\n\t\t\tsecure: !isDevelopment,\n\t\t\thttpOnly: true,\n\t\t\tpath: \"/\",\n\t\t},\n\n\t\t// ✅ FIX: OAuth state/pkce cookies need SameSite=None for cross-site redirects\n\t\t// See: https://github.com/better-auth/better-auth/issues/6483\n\t\t// Note: In development (localhost), browsers allow SameSite=None without Secure\n\t\tcookies: {\n\t\t\tstate: {\n\t\t\t\tname: \"vreko.state\",\n\t\t\t\tattributes: {\n\t\t\t\t\tsameSite: isDevelopment ? \"lax\" : \"none\",\n\t\t\t\t\tsecure: !isDevelopment,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t\tmaxAge: 600, // 10 minutes (OAuth flow timeout)\n\t\t\t\t},\n\t\t\t},\n\t\t\tpkce: {\n\t\t\t\tname: \"vreko.pkce\",\n\t\t\t\tattributes: {\n\t\t\t\t\tsameSite: isDevelopment ? \"lax\" : \"none\",\n\t\t\t\t\tsecure: !isDevelopment,\n\t\t\t\t\thttpOnly: true,\n\t\t\t\t\tpath: \"/\",\n\t\t\t\t\tmaxAge: 600,\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\n\t\tcookiePrefix: \"vreko\", // Namespace cookies\n\t},\n\t// Rate limiting configuration (replaces 340+ lines of custom rate limit code)\n\trateLimit: {\n\t\twindow: 60,\n\t\tmax: 100,\n\t\t// In-memory storage - sufficient for alpha single-instance deployment\n\t\tstorage: \"memory\",\n\t\tcustomRules: {\n\t\t\t\"/sign-in/email\": { window: 10, max: 3 },\n\t\t\t\"/sign-in/social\": { window: 10, max: 5 },\n\t\t\t\"/sign-up\": { window: 60, max: 5 },\n\t\t\t\"/health\": false,\n\t\t\t\"/health/ready\": false,\n\t\t\t\"/health/live\": false,\n\t\t},\n\t},\n\t// Use database hooks for audit logging (replaces 371 lines of custom auth-audit.ts)\n\t// Also includes rate limiting configuration (replaces 340+ lines of custom rate limit code)\n\tdatabaseHooks: {\n\t\tsession: {\n\t\t\tcreate: {\n\t\t\t\tafter: async (session: Record<string, unknown>) => {\n\t\t\t\t\tconst { userId, id: sessionId } = session as { userId: string; id: string };\n\t\t\t\t\t// Track session creation (indicates successful auth)\n\t\t\t\t\tawait trackEvent(\"session.created\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Also track auth.signin for returning users (session without new user)\n\t\t\t\t\t// This fires on every login (new session = successful signin)\n\t\t\t\t\tawait trackEvent(\"auth.signin\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\n\t\t\t\t\t// ✅ SECURITY: Session regeneration on login (prevents session fixation)\n\t\t\t\t\t// Invalidate any old sessions to prevent fixation attacks\n\t\t\t\t\t// OWASP A07:2021 - Session Fixation Prevention\n\t\t\t\t\ttry {\n\t\t\t\t\t\t// Delete all previous sessions for this user EXCEPT the current one\n\t\t\t\t\t\tconst { db } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tif (db) {\n\t\t\t\t\t\t\tconst result = await db.execute(sql`\n\t\t\t\t\t\t\t\tDELETE FROM session\n\t\t\t\t\t\t\t\tWHERE \"userId\" = ${userId}\n\t\t\t\t\t\t\t\tAND id != ${sessionId}\n\t\t\t\t\t\t\t`);\n\n\t\t\t\t\t\t\tconst rotatedCount = result.rowCount || 0;\n\t\t\t\t\t\t\tif (rotatedCount > 0) {\n\t\t\t\t\t\t\t\tlogger.info(\"Session regenerated on login - old sessions invalidated\", {\n\t\t\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t\t\t\trotatedCount,\n\t\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\tawait trackEvent(\"session.regenerated\", {\n\t\t\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\t\t\treason: \"login\",\n\t\t\t\t\t\t\t\t\trotatedCount,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t// Don't fail login if session rotation fails\n\t\t\t\t\t\tlogger.warn(\"Session regeneration failed on login\", {\n\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t},\n\t\t\tdelete: {\n\t\t\t\tafter: async (session: Record<string, unknown>) => {\n\t\t\t\t\tconst { userId } = session as { userId: string };\n\t\t\t\t\t// Track logout/session revocation\n\t\t\t\t\tawait trackEvent(\"session.revoked\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t});\n\n\t\t\t\t\t// Also track auth.signout for analytics funnel\n\t\t\t\t\tawait trackEvent(\"auth.signout\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tuser: {\n\t\t\tcreate: {\n\t\t\t\tbefore: async (user: Record<string, unknown>) => {\n\t\t\t\t\t// ✅ INVITE GATE: Block account creation unless a valid invite code was passed\n\t\t\t\t\t// via OAuth additionalData (threaded through OAuth state) or email sign-up.\n\t\t\t\t\t// The code is burned atomically - concurrent double-redemption is impossible.\n\n\t\t\t\t\t// For OAuth flows: Better Auth stores additionalData in AsyncLocalStorage via\n\t\t\t\t\t// setOAuthState() during parseState(). getOAuthState() reads it back here.\n\t\t\t\t\t// For email sign-up: getOAuthState() returns null; fall back to __inviteCode.\n\t\t\t\t\tconst oauthState = (await getOAuthState().catch(() => null)) as Record<string, unknown> | null;\n\t\t\t\t\tconst inviteCode =\n\t\t\t\t\t\t(oauthState?.inviteCode as string | undefined) ?? (user.__inviteCode as string | undefined);\n\n\t\t\t\t\t// Magic link pioneer path: check pendingApiKeys for a pioneer_magic_claim\n\t\t\t\t\tif (!inviteCode && user.email) {\n\t\t\t\t\t\tconst {\n\t\t\t\t\t\t\tpendingApiKeys: pendingApiKeysTable,\n\t\t\t\t\t\t\tinviteCodes: inviteCodesTable,\n\t\t\t\t\t\t\tburnInviteCode,\n\t\t\t\t\t\t} = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { and, eq, gt, isNull, sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tconst claim = await db\n\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t.from(pendingApiKeysTable)\n\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\teq(pendingApiKeysTable.userId, user.email as string),\n\t\t\t\t\t\t\t\t\teq(pendingApiKeysTable.purpose, \"pioneer_magic_claim\"),\n\t\t\t\t\t\t\t\t\tgt(pendingApiKeysTable.expiresAt, new Date()),\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\tif (claim.length > 0) {\n\t\t\t\t\t\t\tconst codeRow = await db\n\t\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t\t.from(inviteCodesTable)\n\t\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\t\tsql`lower(${inviteCodesTable.invitedEmail}) = lower(${user.email as string})`,\n\t\t\t\t\t\t\t\t\t\tsql`${inviteCodesTable.currentUses} < ${inviteCodesTable.maxUses}`,\n\t\t\t\t\t\t\t\t\t\tisNull(inviteCodesTable.revokedAt),\n\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\t\tif (codeRow.length > 0) {\n\t\t\t\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: length > 0 checked above\n\t\t\t\t\t\t\t\tawait burnInviteCode(db, codeRow[0]!.id);\n\t\t\t\t\t\t\t\tawait db\n\t\t\t\t\t\t\t\t\t.delete(pendingApiKeysTable)\n\t\t\t\t\t\t\t\t\t.where(eq(pendingApiKeysTable.userId, user.email as string));\n\t\t\t\t\t\t\t\treturn { data: user }; // allow account creation\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!inviteCode) {\n\t\t\t\t\t\tthrow new Error(\"INVITE_REQUIRED: An invite code is required to create an account.\");\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst { db } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { inviteCodes } = await import(\"@vreko/platform\");\n\t\t\t\t\t\tconst { and, eq, sql } = await import(\"drizzle-orm\");\n\n\t\t\t\t\t\tconst code = await db\n\t\t\t\t\t\t\t.select()\n\t\t\t\t\t\t\t.from(inviteCodes)\n\t\t\t\t\t\t\t.where(eq(inviteCodes.code, inviteCode))\n\t\t\t\t\t\t\t.limit(1);\n\n\t\t\t\t\t\tif (code.length === 0) {\n\t\t\t\t\t\t\tthrow new Error(\"INVALID_INVITE_CODE\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// biome-ignore lint/style/noNonNullAssertion: length === 0 checked above\n\t\t\t\t\t\tconst entry = code[0]!;\n\t\t\t\t\t\tif (entry.expiresAt && entry.expiresAt < new Date()) {\n\t\t\t\t\t\t\tthrow new Error(\"INVITE_CODE_EXPIRED\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Atomic burn: UPDATE WHERE currentUses < maxUses RETURNING *\n\t\t\t\t\t\tconst burned = await db\n\t\t\t\t\t\t\t.update(inviteCodes)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tcurrentUses: sql`${inviteCodes.currentUses} + 1`,\n\t\t\t\t\t\t\t\tupdatedAt: new Date(),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\t\teq(inviteCodes.id, entry.id),\n\t\t\t\t\t\t\t\t\tsql`${inviteCodes.currentUses} < ${inviteCodes.maxUses}`,\n\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t.returning();\n\n\t\t\t\t\t\tif (burned.length === 0) {\n\t\t\t\t\t\t\tthrow new Error(\"INVITE_CODE_EXHAUSTED\");\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tif (error instanceof Error && error.message.startsWith(\"INVITE\")) {\n\t\t\t\t\t\t\tthrow error; // propagate gate errors\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlogger.error(\"Invite gate DB error\", { error });\n\t\t\t\t\t\tthrow new Error(\"INVITE_GATE_ERROR\");\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { data: user };\n\t\t\t\t},\n\t\t\t\tafter: async (user: Record<string, unknown>) => {\n\t\t\t\t\tconst { id: userId, email } = user as { id: string; email: string };\n\t\t\t\t\t// Track new user signup\n\t\t\t\t\tawait trackEvent(\"auth.signup\", {\n\t\t\t\t\t\tuserId,\n\t\t\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t\t\t});\n\n\t\t\t\t\t// Fire-and-forget HubSpot contact creation - outages must not block signup\n\t\t\t\t\tsyncUserToHubSpot({ userId, email, signupSource: \"app\" }).catch((err) => {\n\t\t\t\t\t\tlogger.warn(\"HubSpot signup sync failed (non-blocking)\", {\n\t\t\t\t\t\t\tuserId,\n\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\n\t\t\t\t\t// API key creation on signup disabled - apiKey plugin removed in better-auth v1.6.5\n\t\t\t\t\t// Users can create API keys manually from the dashboard.\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t},\n\tplugins: [\n\t\tbearer(),\n\t\tdeviceAuthorization({\n\t\t\tverificationUri: `${appUrl}/link`,\n\t\t\tschema: {},\n\t\t}),\n\t\tadmin(),\n\t\topenAPI(),\n\t\t// magicLink() - server-side handler for POST /api/auth/sign-in/magic-link\n\t\tmagicLink({\n\t\t\tsendMagicLink: async ({ email, url }) => {\n\t\t\t\tawait sendEmail({\n\t\t\t\t\tfrom: \"Vreko <noreply@vreko.dev>\",\n\t\t\t\t\tto: email,\n\t\t\t\t\tsubject: \"Sign in to Vreko\",\n\t\t\t\t\thtml: `<p>Click <a href=\"${url}\">here</a> to sign in. This link expires in 5 minutes.</p>`,\n\t\t\t\t});\n\t\t\t},\n\t\t}),\n\t\t// organization() - server-side org endpoints (schema: packages/platform/src/db/schema/postgres.ts)\n\t\torganization(),\n\t\t// API key management (createApiKey / verifyApiKey / deleteApiKey)\n\t\t// better-auth v1.6.5 removed the built-in apiKey() plugin; this custom\n\t\t// plugin re-implements the same surface backed by the api_keys DB table.\n\t\tapiKeyPlugin(),\n\t],\n\tonAPIError: {\n\t\tonError(error: unknown, ctx: unknown) {\n\t\t\t// Enhanced error logging with OAuth-specific context\n\t\t\tconst errorDetails: Record<string, unknown> = {\n\t\t\t\terror,\n\t\t\t\tcontext: ctx,\n\t\t\t};\n\n\t\t\t// Extract additional context from error object\n\t\t\tif (error && typeof error === \"object\") {\n\t\t\t\tif (\"code\" in error) {\n\t\t\t\t\terrorDetails.errorCode = error.code;\n\t\t\t\t}\n\t\t\t\tif (\"message\" in error) {\n\t\t\t\t\terrorDetails.errorMessage = error.message;\n\t\t\t\t}\n\t\t\t\tif (\"statusCode\" in error) {\n\t\t\t\t\terrorDetails.statusCode = error.statusCode;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Extract context information (e.g., request path, method)\n\t\t\tlet isOAuthError = false;\n\t\t\tif (ctx && typeof ctx === \"object\") {\n\t\t\t\tif (\"request\" in ctx) {\n\t\t\t\t\tconst request = ctx.request as { url?: string; method?: string };\n\t\t\t\t\terrorDetails.requestUrl = request.url;\n\t\t\t\t\terrorDetails.requestMethod = request.method;\n\n\t\t\t\t\t// Check if this is an OAuth callback error\n\t\t\t\t\tif (request.url?.includes(\"/api/auth/callback/\")) {\n\t\t\t\t\t\tconst provider = request.url.split(\"/callback/\")[1]?.split(\"?\")[0];\n\t\t\t\t\t\terrorDetails.oauthProvider = provider;\n\t\t\t\t\t\terrorDetails.errorType = \"OAuth Callback Error\";\n\t\t\t\t\t\tisOAuthError = true;\n\n\t\t\t\t\t\tlogger.error(\"OAuth Callback Error\", errorDetails);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Log auth error\n\t\t\tif (!isOAuthError) {\n\t\t\t\tlogger.error(\"Better Auth API Error\", errorDetails);\n\t\t\t}\n\n\t\t\t// Track failed auth in PostHog (non-blocking)\n\t\t\ttrackEvent(\"auth.signin_failed\", {\n\t\t\t\terrorCode: errorDetails.errorCode as string | undefined,\n\t\t\t\terrorMessage: errorDetails.errorMessage as string | undefined,\n\t\t\t\tpath: errorDetails.requestUrl as string | undefined,\n\t\t\t\ttimestamp: new Date().toISOString(),\n\t\t\t}).catch(() => {\n\t\t\t\t// Ignore tracking errors\n\t\t\t});\n\n\t\t\t// Send to Sentry for monitoring (lazy import to avoid startup overhead)\n\t\t\timport(\"@vreko/infrastructure\")\n\t\t\t\t.then(({ captureError }) => {\n\t\t\t\t\tif (captureError && error instanceof Error) {\n\t\t\t\t\t\tcaptureError(error, {\n\t\t\t\t\t\t\ttags: {\n\t\t\t\t\t\t\t\terrorType: isOAuthError ? \"oauth_callback\" : \"auth_api\",\n\t\t\t\t\t\t\t\t...(errorDetails.errorCode ? { errorCode: String(errorDetails.errorCode) } : {}),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\textra: errorDetails,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {\n\t\t\t\t\t// Sentry not available, already logged\n\t\t\t\t});\n\t\t},\n\t},\n});\n\n// ============================================================================\n// Plugin API Type Declarations\n// ============================================================================\n// Better Auth's InferAPI doesn't emit stable types for plugins, causing\n// \"Maximum call stack size exceeded\" during builds. We define explicit types\n// for the plugin APIs we use.\n\n/** API Key plugin types */\nexport interface CreateApiKeyParams {\n\tbody: {\n\t\tname: string;\n\t\tuserId: string;\n\t\tpermissions?: Record<string, string[]>;\n\t\trateLimitEnabled?: boolean;\n\t\trateLimitMax?: number;\n\t\trateLimitTimeWindow?: number;\n\t\texpiresAt?: Date;\n\t\tmetadata?: Record<string, unknown>;\n\t};\n}\n\nexport interface CreateApiKeyResult {\n\tid: string;\n\tkey: string;\n\tname: string;\n\tcreatedAt: Date;\n}\n\n/** verifyApiKey accepts key directly or in body wrapper (support both patterns) */\nexport interface VerifyApiKeyParams {\n\tkey?: string;\n\tpermissions?: Record<string, string[]>;\n\tbody?: {\n\t\tkey: string;\n\t\tpermissions?: Record<string, string[]>;\n\t};\n}\n\nexport interface VerifyApiKeyResult {\n\tvalid?: boolean;\n\tisValid?: boolean; // Alternative property name used by some consumers\n\tuserId?: string;\n\tkey?: {\n\t\tid: string;\n\t\tuserId: string;\n\t\tname: string;\n\t\tpermissions?: Record<string, string[]>;\n\t\trateLimitEnabled?: boolean;\n\t\trateLimitMax?: number;\n\t\tremaining?: number;\n\t\tlastRefillAt?: Date;\n\t\tmetadata?: Record<string, unknown>;\n\t};\n\tmetadata?: Record<string, unknown>;\n\tpermissions?: Record<string, string[]>;\n\terror?: { message: string; code?: string };\n}\n\nexport interface DeleteApiKeyParams {\n\tbody: { keyId: string };\n}\n\n/** Sign-in types */\nexport interface SignInEmailParams {\n\tbody: {\n\t\temail: string;\n\t\tpassword: string;\n\t};\n}\n\nexport interface SignInEmailResult {\n\tuser?: { id: string; email: string };\n\tsession?: { id: string };\n}\n\n/** Extended auth.api type with plugin methods */\nexport interface VrekoAuthAPI {\n\t// Core methods (from base better-auth)\n\tgetSession: (params: { headers: Headers }) => Promise<{\n\t\tsession: { id: string; userId: string; expiresAt: Date } | null;\n\t\tuser: {\n\t\t\tid: string;\n\t\t\temail: string;\n\t\t\tname: string;\n\t\t\temailVerified: boolean;\n\t\t\timage?: string | null;\n\t\t\tcreatedAt: Date;\n\t\t\tupdatedAt: Date;\n\t\t} | null;\n\t}>;\n\n\t// OpenAPI schema generation (Better Auth built-in)\n\tgenerateOpenAPISchema: () => Promise<Record<string, unknown>>;\n\n\t// Sign-in method\n\tsignInEmail: (params: SignInEmailParams) => Promise<SignInEmailResult>;\n\n\t// API Key plugin (primary auth mechanism for CLI/Extension)\n\tcreateApiKey: (params: CreateApiKeyParams) => Promise<CreateApiKeyResult>;\n\tverifyApiKey: (params: VerifyApiKeyParams) => Promise<VerifyApiKeyResult>;\n\tdeleteApiKey: (params: DeleteApiKeyParams) => Promise<void>;\n}\n\n/** Password context from Better Auth's internal AuthContext */\nexport interface VrekoPasswordContext {\n\thash: (password: string) => Promise<string>;\n\tverify: (args: { hash: string; password: string }) => Promise<boolean>;\n}\n\n/** Minimal AuthContext type for $context access */\nexport interface VrekoAuthContext {\n\tpassword: VrekoPasswordContext;\n}\n\n/** Auth instance type with plugin APIs */\nexport interface VrekoAuthInstance {\n\t// biome-ignore lint/suspicious/noExplicitAny: better-auth api shape is complex; typed as any for call-site flexibility\n\tapi: any;\n\thandler: (request: Request) => Promise<Response>;\n\t/** Internal Better Auth context - use for password hashing in scripts */\n\t$context: Promise<VrekoAuthContext>;\n}\n\n/** Typed auth export with plugin APIs - cast to avoid complex type inference */\nexport const auth = _auth as unknown as VrekoAuthInstance;\n\nexport * from \"./lib/organization\";\n\n// Type exports moved to appropriate locations:\n// - Session: Use 'better-auth/types' or '@vreko/contracts/auth/session'\n// - Organization types: Use Better Auth organization plugin types\n// Removed dangerous 'any' stub exports (audit finding #1)\n"]}
@@ -11,7 +11,7 @@ CLI Cleaning output folder
11
11
  ESM Build start
12
12
  ESM dist/index.js 13.94 KB
13
13
  ESM dist/index.js.map 35.00 KB
14
- ESM ⚡️ Build success in 49ms
14
+ ESM ⚡️ Build success in 37ms
15
15
 
16
16
  > @vreko/claims-ledger@0.1.2 postbuild /Users/user1/WebstormProjects/Vreko-Site/packages/claims-ledger
17
17
  > tsc --project tsconfig.build.json && test -f dist/index.d.ts
@@ -1,45 +1,45 @@
1
1
 
2
- > @vreko/claims-ledger@0.1.1 test /Users/user1/WebstormProjects/Vreko-Site/packages/claims-ledger
2
+ > @vreko/claims-ledger@0.1.2 test /Users/user1/WebstormProjects/Vreko-Site/packages/claims-ledger
3
3
  > vitest run
4
4
 
5
- 3:58:48 PM [vite] warning: `esbuild` option was specified by "vitest" plugin. This option is deprecated, please use `oxc` instead.
5
+ 3:45:12 PM [vite] warning: `esbuild` option was specified by "vitest" plugin. This option is deprecated, please use `oxc` instead.
6
6
  The plugin "vite-tsconfig-paths" is detected. Vite now supports tsconfig paths resolution natively via the resolve.tsconfigPaths option. You can remove the plugin and set resolve.tsconfigPaths: true in your Vite config instead.
7
7
 
8
8
  RUN v3.2.6 /Users/user1/WebstormProjects/Vreko-Site/packages/claims-ledger
9
9
 
10
- ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should mark deferred-manual claims as expired after 30 days 18ms
11
- ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should mark deferred-observational claims as expired after 7 days 3ms
10
+ ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should mark deferred-manual claims as expired after 30 days 47ms
11
+ ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should mark deferred-observational claims as expired after 7 days 1ms
12
12
  ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should not mark immediate claims as expired 0ms
13
13
  ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should not mark already-resolved claims 0ms
14
14
  ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should handle mixed claims correctly 1ms
15
15
  ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should call onExpired callback with expired IDs 1ms
16
- ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > startPeriodic > should run initial sweep immediately 12ms
16
+ ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > startPeriodic > should run initial sweep immediately 15ms
17
17
  ✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > custom TTL > should respect custom TTL configuration 0ms
18
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should append a valid claim 10ms
19
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should write multiple claims 5ms
18
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should append a valid claim 12ms
19
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should write multiple claims 13ms
20
20
  ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should emit onClaimMade callback 2ms
21
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer rejects invalid > should reject unregistered claimant 3ms
21
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer rejects invalid > should reject unregistered claimant 2ms
22
22
  ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer rejects invalid > should reject invalid claim structure 1ms
23
23
  ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer rejects invalid > should reject unregistered claim type 1ms
24
24
  ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should return claims by claimant 3ms
25
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should return newest first 3ms
26
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect since filter 3ms
27
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect limit 10ms
28
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 100% honesty rate 117ms
29
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 0% honesty rate 43ms
30
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 50% honesty rate 118ms
31
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should handle expired claims 10ms
32
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should exclude unresolved from rate calculation 9ms
33
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should resolve a claim 7ms
34
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should mark claim as expired 2ms
35
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should emit onOutcomeRecorded callback 1ms
36
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should return unresolved claims 4ms
37
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should filter by olderThan 2ms
38
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by subject > should return claims by subject 2ms
39
- ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > stats > should return storage stats 40ms
25
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should return newest first 10ms
26
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect since filter 8ms
27
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect limit 13ms
28
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 100% honesty rate 13ms
29
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 0% honesty rate 3ms
30
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 50% honesty rate 5ms
31
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should handle expired claims 3ms
32
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should exclude unresolved from rate calculation 11ms
33
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should resolve a claim 8ms
34
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should mark claim as expired 3ms
35
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should emit onOutcomeRecorded callback 3ms
36
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should return unresolved claims 5ms
37
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should filter by olderThan 29ms
38
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by subject > should return claims by subject 5ms
39
+ ✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > stats > should return storage stats 52ms
40
40
 
41
41
  Test Files 2 passed (2)
42
42
  Tests 30 passed (30)
43
- Start at 15:58:49
44
- Duration 3.49s (transform 880ms, setup 0ms, collect 1.35s, tests 443ms, environment 0ms, prepare 685ms)
43
+ Start at 15:45:13
44
+ Duration 3.44s (transform 598ms, setup 0ms, collect 670ms, tests 277ms, environment 0ms, prepare 844ms)
45
45