@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 +2 -2
- package/node_modules/@vreko/auth/.turbo/turbo-build.log +45 -45
- package/node_modules/@vreko/auth/.turbo/turbo-test.log +9 -9
- package/node_modules/@vreko/auth/.turbo/turbo-type-check.log +1 -1
- package/node_modules/@vreko/auth/dist/{chunk-Z3PKMANB.js → chunk-3OCLD7XA.js} +3 -3
- package/node_modules/@vreko/auth/dist/{chunk-Z3PKMANB.js.map → chunk-3OCLD7XA.js.map} +1 -1
- package/node_modules/@vreko/claims-ledger/.turbo/turbo-build.log +1 -1
- package/node_modules/@vreko/claims-ledger/.turbo/turbo-test.log +25 -25
- package/node_modules/@vreko/contracts/README.md +2 -2
- package/node_modules/@vreko/local-service-client/README.md +2 -2
- package/node_modules/@vreko/mcp/dist/._.tsbuildinfo +0 -0
- package/node_modules/@vreko/mcp/dist/.tsbuildinfo +1 -0
- package/package.json +3 -3
- /package/node_modules/@vreko/auth/dist/{._chunk-Z3PKMANB.js → ._chunk-3OCLD7XA.js} +0 -0
- /package/node_modules/@vreko/auth/dist/{._chunk-Z3PKMANB.js.map → ._chunk-3OCLD7XA.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<picture>
|
|
3
|
-
<source media="(prefers-color-scheme: dark)" srcset="
|
|
4
|
-
<img alt="Vreko" src="
|
|
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/
|
|
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/
|
|
13
|
-
dist/
|
|
14
|
-
dist/
|
|
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-
|
|
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/
|
|
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
|
-
|
|
34
|
-
ESM dist/
|
|
35
|
-
ESM dist/
|
|
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/
|
|
47
|
-
ESM dist/
|
|
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/
|
|
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/
|
|
53
|
-
ESM dist/
|
|
54
|
-
ESM dist/
|
|
55
|
-
ESM dist/
|
|
56
|
-
ESM dist/
|
|
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/
|
|
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
|
|
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.
|
|
2
|
+
> @vreko/auth@0.1.2 test /Users/user1/WebstormProjects/Vreko-Site/packages/auth
|
|
3
3
|
> vitest
|
|
4
4
|
|
|
5
|
-
3:
|
|
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
|
|
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)
|
|
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)
|
|
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
|
|
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)
|
|
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:
|
|
29
|
-
Duration
|
|
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,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-
|
|
660
|
-
//# sourceMappingURL=chunk-
|
|
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
|
|
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.
|
|
2
|
+
> @vreko/claims-ledger@0.1.2 test /Users/user1/WebstormProjects/Vreko-Site/packages/claims-ledger
|
|
3
3
|
> vitest run
|
|
4
4
|
|
|
5
|
-
3:
|
|
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
|
|
11
|
-
✓ |@vreko/claims-ledger| test/sweeper.test.ts > ExpiredClaimSweeper > sweep > should mark deferred-observational claims as expired after 7 days
|
|
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
|
|
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
|
|
19
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > writer appends > should write multiple claims
|
|
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
|
|
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
|
|
26
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect since filter
|
|
27
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by claimant > should respect limit
|
|
28
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 100% honesty rate
|
|
29
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 0% honesty rate
|
|
30
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should compute 50% honesty rate
|
|
31
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should handle expired claims
|
|
32
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > honestyRate > should exclude unresolved from rate calculation
|
|
33
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should resolve a claim
|
|
34
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should mark claim as expired
|
|
35
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > outcome resolution > should emit onOutcomeRecorded callback
|
|
36
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should return unresolved claims
|
|
37
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > unresolved claims > should filter by olderThan
|
|
38
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > query by subject > should return claims by subject
|
|
39
|
-
✓ |@vreko/claims-ledger| test/ledger.test.ts > claims-ledger > stats > should return storage stats
|
|
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:
|
|
44
|
-
Duration 3.
|
|
43
|
+
Start at 15:45:13
|
|
44
|
+
Duration 3.44s (transform 598ms, setup 0ms, collect 670ms, tests 277ms, environment 0ms, prepare 844ms)
|
|
45
45
|
|