module.exports = [
"[externals]/crypto [external] (crypto, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("crypto", () => require("crypto"));

module.exports = mod;
}),
"[externals]/next/dist/compiled/@opentelemetry/api [external] (next/dist/compiled/@opentelemetry/api, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/compiled/@opentelemetry/api", () => require("next/dist/compiled/@opentelemetry/api"));

module.exports = mod;
}),
"[externals]/next/dist/compiled/next-server/app-page-turbo.runtime.dev.js [external] (next/dist/compiled/next-server/app-page-turbo.runtime.dev.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/compiled/next-server/app-page-turbo.runtime.dev.js", () => require("next/dist/compiled/next-server/app-page-turbo.runtime.dev.js"));

module.exports = mod;
}),
"[externals]/next/dist/compiled/next-server/app-route-turbo.runtime.dev.js [external] (next/dist/compiled/next-server/app-route-turbo.runtime.dev.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/compiled/next-server/app-route-turbo.runtime.dev.js", () => require("next/dist/compiled/next-server/app-route-turbo.runtime.dev.js"));

module.exports = mod;
}),
"[externals]/next/dist/server/app-render/action-async-storage.external.js [external] (next/dist/server/app-render/action-async-storage.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/server/app-render/action-async-storage.external.js", () => require("next/dist/server/app-render/action-async-storage.external.js"));

module.exports = mod;
}),
"[externals]/next/dist/server/app-render/after-task-async-storage.external.js [external] (next/dist/server/app-render/after-task-async-storage.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/server/app-render/after-task-async-storage.external.js", () => require("next/dist/server/app-render/after-task-async-storage.external.js"));

module.exports = mod;
}),
"[externals]/next/dist/server/app-render/work-async-storage.external.js [external] (next/dist/server/app-render/work-async-storage.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/server/app-render/work-async-storage.external.js", () => require("next/dist/server/app-render/work-async-storage.external.js"));

module.exports = mod;
}),
"[externals]/next/dist/server/app-render/work-unit-async-storage.external.js [external] (next/dist/server/app-render/work-unit-async-storage.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/server/app-render/work-unit-async-storage.external.js", () => require("next/dist/server/app-render/work-unit-async-storage.external.js"));

module.exports = mod;
}),
"[externals]/next/dist/server/runtime-reacts.external.js [external] (next/dist/server/runtime-reacts.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/server/runtime-reacts.external.js", () => require("next/dist/server/runtime-reacts.external.js"));

module.exports = mod;
}),
"[externals]/next/dist/shared/lib/no-fallback-error.external.js [external] (next/dist/shared/lib/no-fallback-error.external.js, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("next/dist/shared/lib/no-fallback-error.external.js", () => require("next/dist/shared/lib/no-fallback-error.external.js"));

module.exports = mod;
}),
"[externals]/node:stream [external] (node:stream, cjs)", ((__turbopack_context__, module, exports) => {

var mod = __turbopack_context__.x("node:stream", () => require("node:stream"));

module.exports = mod;
}),
"[project]/src/app/api/auth/[...nextauth]/route.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

return __turbopack_context__.a(async (__turbopack_handle_async_dependencies__, __turbopack_async_result__) => { try {
__turbopack_context__.s([
    "GET",
    ()=>GET,
    "POST",
    ()=>POST
]);
// Auth.js's own catch-all route — handles POST /api/auth/callback/credentials
// (login), POST /api/auth/signout (logout), GET /api/auth/session, etc.
// See spec/iam/tier-b-implementation.md's API surface table and src/auth.ts
// for the Credentials-provider wiring.
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$auth$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/auth.ts [app-route] (ecmascript)");
var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__([
    __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$auth$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__
]);
[__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$auth$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__] = __turbopack_async_dependencies__.then ? (await __turbopack_async_dependencies__)() : __turbopack_async_dependencies__;
;
const { GET, POST } = __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$auth$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["handlers"];
__turbopack_async_result__();
} catch(e) { __turbopack_async_result__(e); } }, false);}),
"[project]/src/auth.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

return __turbopack_context__.a(async (__turbopack_handle_async_dependencies__, __turbopack_async_result__) => { try {
__turbopack_context__.s([
    "auth",
    ()=>auth,
    "handlers",
    ()=>handlers,
    "signIn",
    ()=>signIn,
    "signOut",
    ()=>signOut
]);
// Auth.js (NextAuth v5) wiring for T-501.
//
// ADR-0004 calls for a Credentials provider with "database session
// strategy" backed by IAM's own users/sessions tables. Auth.js's own
// session-strategy option is more restrictive than that phrasing suggests:
// Auth.js explicitly REJECTS `session.strategy: "database"` when the only
// configured provider is Credentials (see
// node_modules/@auth/core/lib/utils/assert.js: "Signing in with
// credentials only supported if JWT strategy is enabled" /
// UnsupportedStrategy). Auth.js's built-in "database" strategy also
// assumes its own Adapter schema shape (Account/Session/User/
// VerificationToken with specific field names like `expires` rather than
// IAM's `expiresAt`), which doesn't match the schema IAM's contract
// functions already implement and fully own.
//
// Rather than fight that framework constraint or duplicate IAM's session
// logic behind a second, Adapter-shaped implementation, this config keeps
// Auth.js's *transport* (the signed cookie) on the JWT strategy, but
// treats IAM's own `sessionToken` (from login()/logout()/verifySession())
// as the actual source of truth for whether a session is valid:
//   - `authorize()` calls IAM's login(); on success, the sessionToken IAM
//     issued is embedded in the JWT payload (not a Auth.js-invented id).
//   - the `jwt` callback re-validates that sessionToken against the DB via
//     IAM's verifySession() on every subsequent request, so a session
//     revoked by logout() or confirmPasswordReset() (which deletes
//     Session rows) stops working immediately — genuine server-side
//     revocation, not just "valid until the JWT's own expiry."
//   - the `signOut` event calls IAM's logout() so the DB row is actually
//     removed, not just orphaned until it naturally expires.
//
// This satisfies the ADR's actual intent (revocable, DB-backed sessions
// reusing IAM's own tables/contract functions) within Auth.js's
// documented Credentials-provider constraint. Documented here since it's
// a deliberate deviation from the ADR's literal wording, not an oversight.
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2d$auth$2f$index$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/node_modules/next-auth/index.js [app-route] (ecmascript) <locals>");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2d$auth$2f$providers$2f$credentials$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__$3c$locals$3e$__ = __turbopack_context__.i("[project]/node_modules/next-auth/providers/credentials.js [app-route] (ecmascript) <locals>");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$auth$2f$core$2f$providers$2f$credentials$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/@auth/core/providers/credentials.js [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/prisma.ts [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$login$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/modules/iam/login.ts [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$logout$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/modules/iam/logout.ts [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$verifySession$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/modules/iam/verifySession.ts [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$rate$2d$limit$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/rate-limit.ts [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$client$2d$ip$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/lib/client-ip.ts [app-route] (ecmascript)");
var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__([
    __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__
]);
[__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__] = __turbopack_async_dependencies__.then ? (await __turbopack_async_dependencies__)() : __turbopack_async_dependencies__;
;
;
;
;
;
;
;
;
function parseIdentifier(raw) {
    if (typeof raw !== "string" || raw.trim().length === 0) return null;
    const type = raw.includes("@") ? "EMAIL" : "PHONE";
    return {
        type,
        value: raw
    };
}
// Security fix (T-501 review, HIGH): login() had zero throttling —
// this is the primary, most-targeted entry point for credential-stuffing/
// password-brute-force bots, and login.ts's own timing-parity defense
// only prevents user-enumeration, not unlimited online guessing. Same
// class of protection as reset/confirm's limiter (see rate-limit.ts's
// doc comment for the accepted single-instance/V1 scope), a more
// generous window since legitimate users mistype passwords far more
// often than they need 6-digit codes.
const LOGIN_LIMIT = 10;
const LOGIN_WINDOW_MS = 15 * 60 * 1000; // 15 minutes
const loginLimiter = new __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$rate$2d$limit$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["FixedWindowRateLimiter"](LOGIN_LIMIT, LOGIN_WINDOW_MS);
const { handlers, auth, signIn, signOut } = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$next$2d$auth$2f$index$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__$3c$locals$3e$__["default"])({
    session: {
        strategy: "jwt"
    },
    pages: {
        signIn: "/login"
    },
    providers: [
        (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$auth$2f$core$2f$providers$2f$credentials$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__["default"])({
            credentials: {
                identifier: {
                    label: "Email or phone",
                    type: "text"
                },
                password: {
                    label: "Password",
                    type: "password"
                }
            },
            async authorize (credentials, request) {
                const identifier = parseIdentifier(credentials?.identifier);
                const password = typeof credentials?.password === "string" ? credentials.password : "";
                if (!identifier || !password) return null;
                // Rate-limit BEFORE calling login(), so a throttled attempt never
                // even reaches bcrypt.compare — keyed by identifier+IP, same
                // spirit as reset/confirm's limiter (identifier alone would let
                // one attacker lock out a victim; IP alone would let a botnet
                // spread guesses across addresses).
                const rateLimitKey = `${identifier.type}:${identifier.value.trim().toLowerCase()}|${(0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$client$2d$ip$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["clientIp"])(request)}`;
                const { allowed } = loginLimiter.check(rateLimitKey);
                if (!allowed) return null;
                const result = await (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$login$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["login"])(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["prisma"], identifier, password);
                if (!result.ok) return null;
                // Everything after `id` is a custom field carried through to the
                // `jwt` callback's `user` argument on initial sign-in only.
                return {
                    id: result.userId,
                    sessionToken: result.sessionToken
                };
            }
        })
    ],
    callbacks: {
        async jwt ({ token, user }) {
            if (user) {
                // Initial sign-in: `user` is what authorize() returned above.
                token.sessionToken = user.sessionToken;
                token.userId = user.id;
                return token;
            }
            // Subsequent requests: re-check IAM's own session store so that
            // logout()/confirmPasswordReset() (both of which delete Session
            // rows) take effect immediately, not just once the JWT expires.
            if (typeof token.sessionToken === "string") {
                const userId = await (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$verifySession$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["verifySession"])(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["prisma"], token.sessionToken);
                if (!userId) {
                    // Session was revoked server-side — strip credentials so the
                    // `session` callback below reports "signed out."
                    delete token.sessionToken;
                    delete token.userId;
                }
            }
            return token;
        },
        async session ({ session, token }) {
            if (typeof token.userId === "string") {
                session.user.id = token.userId;
            } else {
                // No valid IAM session behind this JWT (revoked or never set) —
                // surface as unauthenticated rather than a half-populated user.
                return {
                    ...session,
                    user: undefined,
                    expires: session.expires
                };
            }
            return session;
        }
    },
    events: {
        async signOut (message) {
            // With the "jwt" strategy, Auth.js's signOut event passes `{ token }`
            // rather than `{ session }`. Clean up the underlying IAM Session row
            // so it doesn't linger until its natural expiry.
            if ("token" in message && typeof message.token?.sessionToken === "string") {
                await (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$logout$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["logout"])(__TURBOPACK__imported__module__$5b$project$5d2f$src$2f$lib$2f$prisma$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["prisma"], message.token.sessionToken);
            }
        }
    }
});
__turbopack_async_result__();
} catch(e) { __turbopack_async_result__(e); } }, false);}),
"[project]/src/lib/client-ip.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// Shared client-IP extraction for rate-limiting purposes.
//
// Security fix (T-501 review, HIGH): a naive implementation that reads
// the FIRST entry of X-Forwarded-For is trivially spoofable — proxies
// (including Vercel) APPEND the real connecting client's address to the
// end of the list; the first entry is whatever the client itself sent in
// its own request and is fully attacker-controlled. Reading the LAST
// entry instead reflects the address the immediate, trusted proxy hop
// actually observed, which a client cannot forge (they can prepend fake
// entries, but not remove or rewrite the one their own connecting proxy
// appends).
//
// This is still not a perfect IP for a multi-hop/CDN setup (a compromised
// or misconfigured intermediate proxy could still lie), but it is not
// attacker-forgeable in the common single-reverse-proxy deployment this
// app targets (Vercel, per ADR-0003) the way "first entry" is.
__turbopack_context__.s([
    "clientIp",
    ()=>clientIp
]);
function clientIp(request) {
    const forwarded = request.headers.get("x-forwarded-for");
    if (!forwarded) return "unknown";
    const hops = forwarded.split(",").map((h)=>h.trim()).filter(Boolean);
    return hops.length > 0 ? hops[hops.length - 1] : "unknown";
}
}),
"[project]/src/lib/prisma.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

return __turbopack_context__.a(async (__turbopack_handle_async_dependencies__, __turbopack_async_result__) => { try {
__turbopack_context__.s([
    "prisma",
    ()=>prisma
]);
// Standard Next.js + Prisma singleton pattern: in dev, Next.js hot-reloads
// modules on every file change, which would otherwise create a fresh
// PrismaClient (and a fresh DB connection pool) on every reload and
// eventually exhaust Postgres's connection limit. Stashing the instance on
// `globalThis` (which survives hot-reload, unlike module-local state)
// avoids that. In production there's only ever one module load, so this
// is a no-op there beyond the initial construction.
//
// Queries run through @prisma/adapter-pg (a plain-JS `pg` Pool) rather than
// Prisma's default Rust query engine — the Rust engine's Tokio runtime
// spawns one OS worker thread per reported CPU core, which exceeds the
// process/thread ceiling on constrained shared hosting and crashes with
// "OS can't spawn worker thread: Resource temporarily unavailable" on the
// very first request. The adapter has no native multi-threaded runtime.
var __TURBOPACK__imported__module__$5b$externals$5d2f40$prisma$2f$client__$5b$external$5d$__$2840$prisma$2f$client$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f40$prisma$2f$client$29$__ = __turbopack_context__.i("[externals]/@prisma/client [external] (@prisma/client, cjs, [project]/node_modules/@prisma/client)");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$prisma$2f$adapter$2d$pg$2f$dist$2f$index$2e$mjs__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/@prisma/adapter-pg/dist/index.mjs [app-route] (ecmascript)");
var __turbopack_async_dependencies__ = __turbopack_handle_async_dependencies__([
    __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$prisma$2f$adapter$2d$pg$2f$dist$2f$index$2e$mjs__$5b$app$2d$route$5d$__$28$ecmascript$29$__
]);
[__TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$prisma$2f$adapter$2d$pg$2f$dist$2f$index$2e$mjs__$5b$app$2d$route$5d$__$28$ecmascript$29$__] = __turbopack_async_dependencies__.then ? (await __turbopack_async_dependencies__)() : __turbopack_async_dependencies__;
;
;
const globalForPrisma = globalThis;
const adapter = new __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f40$prisma$2f$adapter$2d$pg$2f$dist$2f$index$2e$mjs__$5b$app$2d$route$5d$__$28$ecmascript$29$__["PrismaPg"]({
    connectionString: process.env.DATABASE_URL
});
const prisma = globalForPrisma.prisma ?? new __TURBOPACK__imported__module__$5b$externals$5d2f40$prisma$2f$client__$5b$external$5d$__$2840$prisma$2f$client$2c$__cjs$2c$__$5b$project$5d2f$node_modules$2f40$prisma$2f$client$29$__["PrismaClient"]({
    adapter
});
if ("TURBOPACK compile-time truthy", 1) {
    globalForPrisma.prisma = prisma;
}
__turbopack_async_result__();
} catch(e) { __turbopack_async_result__(e); } }, false);}),
"[project]/src/lib/rate-limit.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// Simple in-memory fixed-window rate limiter.
//
// Scope: V1-appropriate protection for a low-traffic single-instance app
// (per WORKPLAN.md's environment notes, this deploys to a small number of
// serverless instances, not a large fleet) — NOT a distributed limiter.
// It resets if the process restarts and does not coordinate across
// multiple server instances. That's an accepted tradeoff for V1: the
// spec's must-do is "have SOME rate limiting on reset/confirm, a handful
// of attempts per 15-minute window," not "build a production-grade
// distributed limiter." A Redis-backed limiter would be a natural
// follow-up if this app ever runs on more than one instance.
//
// Extracted as a pure class (no Next.js/Request coupling) so it's testable
// in isolation without spinning up a route handler.
__turbopack_context__.s([
    "FixedWindowRateLimiter",
    ()=>FixedWindowRateLimiter
]);
class FixedWindowRateLimiter {
    limit;
    windowMs;
    hits;
    constructor(limit, windowMs){
        this.limit = limit;
        this.windowMs = windowMs;
        this.hits = new Map();
    }
    /**
   * Records an attempt for `key` and reports whether it's allowed under the
   * limit. Pass `now` explicitly in tests to control window boundaries.
   */ check(key, now = Date.now()) {
        const existing = this.hits.get(key);
        if (!existing || now - existing.windowStart >= this.windowMs) {
            // New window (first attempt ever for this key, or the previous
            // window has fully elapsed).
            this.hits.set(key, {
                count: 1,
                windowStart: now
            });
            return {
                allowed: true,
                remaining: this.limit - 1
            };
        }
        if (existing.count >= this.limit) {
            return {
                allowed: false,
                remaining: 0
            };
        }
        existing.count += 1;
        return {
            allowed: true,
            remaining: this.limit - existing.count
        };
    }
    /** Test/diagnostic helper — not used by route handlers. */ reset() {
        this.hits.clear();
    }
}
}),
"[project]/src/modules/iam/identifier.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// Shared identifier type + normalization for IAM's email-or-phone login
// scheme. Extracted so register.ts and login.ts can't silently drift
// (review follow-up from T-102).
__turbopack_context__.s([
    "normalizeIdentifier",
    ()=>normalizeIdentifier
]);
function normalizeIdentifier(identifier) {
    const trimmed = identifier.value.trim();
    return identifier.type === "EMAIL" ? trimmed.toLowerCase() : trimmed;
}
}),
"[project]/src/modules/iam/login.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

__turbopack_context__.s([
    "login",
    ()=>login
]);
// T-102 — IAM login() contract function.
// See spec/iam/tier-a-contract.md (signature, INV-IAM-4) and
// spec/iam/tier-b-implementation.md (schema, session expiry, "same generic
// error for unknown identifier vs wrong password" edge case).
var __TURBOPACK__imported__module__$5b$externals$5d2f$crypto__$5b$external$5d$__$28$crypto$2c$__cjs$29$__ = __turbopack_context__.i("[externals]/crypto [external] (crypto, cjs)");
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$bcryptjs$2f$index$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/bcryptjs/index.js [app-route] (ecmascript)");
var __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$identifier$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/src/modules/iam/identifier.ts [app-route] (ecmascript)");
;
;
;
const SESSION_LIFETIME_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
const SESSION_TOKEN_BYTES = 32; // 32 bytes -> 64 hex chars, well over the 32-char floor
// Dummy hash to compare against when the identifier doesn't match a User,
// so a failed lookup still pays the bcrypt.compare cost — mitigates
// timing-based user enumeration (same code path either way).
const DUMMY_HASH_FOR_TIMING_PARITY = "$2a$10$CwTycUXWue0Thq9StjUM0uJ8i1Ax/OhpFrOTdmKGKQ8pC/S8v7B9m";
async function login(prisma, identifier, password) {
    const value = (0, __TURBOPACK__imported__module__$5b$project$5d2f$src$2f$modules$2f$iam$2f$identifier$2e$ts__$5b$app$2d$route$5d$__$28$ecmascript$29$__["normalizeIdentifier"])(identifier);
    // Same field-scoped lookup pattern as register.ts: email checked
    // against email, phone against phone — never a combined OR.
    const user = identifier.type === "EMAIL" ? await prisma.user.findFirst({
        where: {
            email: value
        }
    }) : await prisma.user.findFirst({
        where: {
            phone: value
        }
    });
    const passwordMatches = await __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$bcryptjs$2f$index$2e$js__$5b$app$2d$route$5d$__$28$ecmascript$29$__["default"].compare(password, user?.passwordHash ?? DUMMY_HASH_FOR_TIMING_PARITY);
    if (!user || !passwordMatches) {
        return {
            ok: false,
            error: "INVALID_CREDENTIALS"
        };
    }
    const sessionToken = __TURBOPACK__imported__module__$5b$externals$5d2f$crypto__$5b$external$5d$__$28$crypto$2c$__cjs$29$__["default"].randomBytes(SESSION_TOKEN_BYTES).toString("hex");
    const expiresAt = new Date(Date.now() + SESSION_LIFETIME_MS);
    await prisma.session.create({
        data: {
            sessionToken,
            userId: user.id,
            expiresAt
        }
    });
    return {
        ok: true,
        sessionToken,
        userId: user.id
    };
}
}),
"[project]/src/modules/iam/logout.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// T-102 — IAM logout() contract function.
// See spec/iam/tier-a-contract.md (signature, INV-IAM-4).
__turbopack_context__.s([
    "logout",
    ()=>logout
]);
async function logout(prisma, sessionToken) {
    // Judgment call (Tier B doesn't specify delete vs. expire-flag): delete
    // the row — simplest reading that satisfies INV-IAM-4 (a subsequent
    // verifySession lookup finds nothing).
    await prisma.session.deleteMany({
        where: {
            sessionToken
        }
    });
}
}),
"[project]/src/modules/iam/verifySession.ts [app-route] (ecmascript)", ((__turbopack_context__) => {
"use strict";

// T-102 — IAM verifySession() contract function.
// See spec/iam/tier-a-contract.md (signature, INV-IAM-4) and
// spec/iam/tier-b-implementation.md (session expiry logic — expired
// sessions must be treated as invalid, same as nonexistent).
__turbopack_context__.s([
    "verifySession",
    ()=>verifySession
]);
async function verifySession(prisma, sessionToken) {
    const session = await prisma.session.findUnique({
        where: {
            sessionToken
        }
    });
    if (!session) return null;
    if (session.expiresAt.getTime() <= Date.now()) return null;
    return session.userId;
}
}),
];

//# sourceMappingURL=%5Broot-of-the-server%5D__18o9oiy._.js.map