next-api-layer 0.1.5
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/LICENSE +21 -0
- package/README.md +743 -0
- package/dist/api.cjs +2 -0
- package/dist/api.cjs.map +1 -0
- package/dist/api.d.cts +42 -0
- package/dist/api.d.ts +42 -0
- package/dist/api.js +2 -0
- package/dist/api.js.map +1 -0
- package/dist/chunk-6ENVQMWQ.cjs +2 -0
- package/dist/chunk-6ENVQMWQ.cjs.map +1 -0
- package/dist/chunk-NBYI46RO.js +2 -0
- package/dist/chunk-NBYI46RO.js.map +1 -0
- package/dist/chunk-OXXKU4OM.cjs +2 -0
- package/dist/chunk-OXXKU4OM.cjs.map +1 -0
- package/dist/chunk-XBAO7FJN.js +2 -0
- package/dist/chunk-XBAO7FJN.js.map +1 -0
- package/dist/cli/init.d.ts +1 -0
- package/dist/cli/init.js +14 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/client.cjs +3 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +169 -0
- package/dist/client.d.ts +169 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/createApiClient-CIDYcpNI.d.cts +383 -0
- package/dist/createApiClient-CIDYcpNI.d.ts +383 -0
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +300 -0
- package/dist/index.d.ts +300 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +2 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +77 -0
- package/dist/server.d.ts +77 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/package.json +124 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/config.ts","../src/proxy/tokenValidation.ts","../src/proxy/handlers.ts","../src/proxy/csrf.ts","../src/proxy/rateLimit.ts","../src/proxy/audit.ts","../src/proxy/createAuthProxy.ts","../src/proxy/createProxyHandler.ts"],"names":["generateCsrfSecret","defaultRateLimitKeyFn","req","resolveProxyConfig","config","apiBaseUrl","cookieOptions","DEFAULT_COOKIE_OPTIONS","endpoints","DEFAULT_ENDPOINTS","csrf","DEFAULT_CSRF_CONFIG","rateLimit","DEFAULT_RATE_LIMIT_CONFIG","audit","DEFAULT_AUDIT_CONFIG","defaultMappers","response","res","createTokenValidation","_resolved","responseMappers","mappers","validateToken","token","invalidResult","rawResponse","parsed","getTokenInfo","refreshToken","oldToken","newToken","createGuestToken","guestConfig","extractLocale","pathname","i18n","locales","defaultLocale","firstSegment","createHandlers","validation","cookies","guestToken","access","safeDeleteCookie","cookieName","deleteAllAuthCookies","jsonError","message","status","NextResponse","isAuthPage","route","isProtectedRoute","isPublicRoute","isTokenTypeAllowed","tokenType","allowedTypes","handleNoToken","isApiRoute","origin","guestAccessToken","handleValidationResult","tokenInfo","isUserToken","currentToken","isValid","userData","isGuest","TOKEN_TYPES","refreshResult","newTokenInfo","requestHeaders","HEADERS","locale","createCsrfValidator","generateToken","sessionId","randomValue","generateRandomValue","computeHmac","validateRequest","method","strategy","fetchResult","validateFetchMetadata","validateDoubleSubmit","secFetchSite","cookieToken","headerToken","constantTimeEqual","attachCsrfCookie","array","b","secret","encoder","keyData","messageData","key","signature","hash","str","i","char","a","result","createRateLimiter","store","cleanupInterval","now","entry","shouldSkip","pattern","check","remaining","applyHeaders","createLimitedResponse","reset","clear","size","createAuditLogger","isEnabled","type","getIp","emit","options","event","error","authSuccess","userId","metadata","authFail","authRefresh","authGuest","accessDenied","csrfFail","rateLimitExceeded","err","createAuthProxy","userConfig","handlers","rateLimiter","authProxy","rateLimitResult","csrfResult","beforeResult","path","applyAfterAuth","userToken","authResult","finalResponse","matchesPattern","endpoint","patterns","normalizedEndpoint","regexPattern","createProxyHandler","userCookieName","guestCookieName","skipAuthByDefault","publicEndpoints","forwardHeaders","excludeHeaders","transformRequest","transformResponse","baseUrl","shouldSkipAuth","handler","url","backendUrl","headers","headerName","value","lowerKey","finalHeaders","body","contentType","backendResponse","responseBody","responseHeaders"],"mappings":"4XA6BA,SAASA,IAA6B,CAEpC,OAAI,OAAO,MAAA,CAAW,GAAA,EAAe,OAAO,UAAA,CACnC,MAAA,CAAO,YAAW,CAAI,MAAA,CAAO,YAAW,CAG1C,CAAA,EAAG,IAAA,CAAK,GAAA,EAAK,CAAA,CAAA,EAAI,KAAK,MAAA,EAAO,CAAE,SAAS,EAAE,CAAA,CAAE,UAAU,CAAC,CAAC,CAAA,CACjE,CAKA,SAASC,EAAAA,CAAsBC,EAA0B,CAKvD,OAAO,MAJWA,CAAAA,CAAI,OAAA,CAAQ,IAAI,iBAAiB,CAAA,EAC7B,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,GAAG,IAAA,EAAK,EAC/BA,EAAI,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,EAC3B,SACI,EACjB,CAKO,SAASC,EAAmBC,CAAAA,CAA8C,CAE/E,GAAI,CAACA,CAAAA,CAAO,WACV,MAAM,IAAI,KAAA,CAAM,wCAAwC,CAAA,CAG1D,GAAI,CAACA,CAAAA,CAAO,OAAA,EAAS,MAAQ,CAACA,CAAAA,CAAO,SAAS,KAAA,CAC5C,MAAM,IAAI,KAAA,CAAM,6DAA6D,CAAA,CAI/E,IAAMC,CAAAA,CAAaD,CAAAA,CAAO,WAAW,QAAA,CAAS,GAAG,EAC7CA,CAAAA,CAAO,UAAA,CACP,CAAA,EAAGA,CAAAA,CAAO,UAAU,CAAA,CAAA,CAAA,CAGlBE,EAAuC,CAC3C,GAAGC,EACH,GAAGH,CAAAA,CAAO,QAAQ,OACpB,CAAA,CAGMI,EAAsC,CAC1C,GAAGC,EACH,GAAGL,CAAAA,CAAO,SACZ,CAAA,CAGMM,CAAAA,CAA2B,CAC/B,OAAA,CAASN,CAAAA,CAAO,IAAA,EAAM,OAAA,EAAW,KAAA,CACjC,QAAA,CAAUA,EAAO,IAAA,EAAM,QAAA,EAAYO,EAAoB,QAAA,CACvD,MAAA,CAAQP,EAAO,IAAA,EAAM,MAAA,EAAUJ,EAAAA,EAAmB,CAClD,UAAA,CAAYI,CAAAA,CAAO,MAAM,UAAA,EAAcO,CAAAA,CAAoB,WAC3D,UAAA,CAAYP,CAAAA,CAAO,MAAM,UAAA,EAAcO,CAAAA,CAAoB,UAAA,CAC3D,aAAA,CAAeP,CAAAA,CAAO,IAAA,EAAM,eAAiBO,CAAAA,CAAoB,aAAA,CACjE,cAAeP,CAAAA,CAAO,IAAA,EAAM,eAAiBO,CAAAA,CAAoB,aACnE,EAGMC,CAAAA,CAAqC,CACzC,QAASR,CAAAA,CAAO,SAAA,EAAW,SAAW,KAAA,CACtC,QAAA,CAAUA,EAAO,SAAA,EAAW,QAAA,EAAYS,CAAAA,CAA0B,QAAA,CAClE,WAAA,CAAaT,CAAAA,CAAO,WAAW,WAAA,EAAeS,CAAAA,CAA0B,YACxE,KAAA,CAAOT,CAAAA,CAAO,WAAW,KAAA,EAASH,EAAAA,CAClC,UAAA,CAAYG,CAAAA,CAAO,SAAA,EAAW,UAAA,EAAcS,EAA0B,UAAA,CACtE,aAAA,CAAeT,EAAO,SAAA,EAAW,aACnC,EAGMU,CAAAA,CAA6B,CACjC,OAAA,CAASV,CAAAA,CAAO,KAAA,EAAO,OAAA,EAAW,MAClC,MAAA,CAAQA,CAAAA,CAAO,OAAO,MAAA,EAAU,CAAC,GAAGW,CAAAA,CAAqB,MAAM,EAC/D,MAAA,CAAQX,CAAAA,CAAO,OAAO,MACxB,CAAA,CAEA,OAAO,CACL,GAAGA,EACH,UAAA,CAAAC,CAAAA,CACA,SAAA,CAAW,CACT,aAAA,CAAAC,CAAAA,CACA,UAAAE,CAAAA,CACA,IAAA,CAAAE,EACA,SAAA,CAAAE,CAAAA,CACA,MAAAE,CACF,CACF,CACF,CCrGA,IAAME,CAAAA,CAA4C,CAEhD,WAAA,CAAcC,CAAAA,EAAwC,CACpD,IAAMC,CAAAA,CAAMD,EACZ,OAAI,CAACC,CAAAA,EAAK,OAAA,EAAW,CAACA,CAAAA,EAAK,KAClB,IAAA,CAEF,CACL,QAAS,IAAA,CACT,SAAA,CAAWA,EAAI,IAAA,CAAK,IAAA,EAAQ,OAC5B,GAAA,CAAKA,CAAAA,CAAI,KAAK,GAAA,EAAO,IAAA,CACrB,SAAUA,CAAAA,CAAI,IAChB,CACF,CAAA,CAGA,iBAAA,CAAoBD,CAAAA,EAAqC,CACvD,IAAMC,CAAAA,CAAMD,EACZ,OAAOC,CAAAA,EAAK,SAAWA,CAAAA,EAAK,IAAA,EAAM,YAAcA,CAAAA,CAAI,IAAA,CAAK,WAAA,CAAc,IACzE,CAAA,CAGA,eAAA,CAAkBD,GACJA,CAAAA,EACA,IAAA,EAAM,aAAe,IAErC,CAAA,CAKO,SAASE,CAAAA,CACdf,CAAAA,CACA,CACA,GAAM,CAAE,UAAA,CAAAC,EAAY,SAAA,CAAAe,CAAAA,CAAW,gBAAAC,CAAgB,CAAA,CAAIjB,EAC7C,CAAE,SAAA,CAAAI,CAAU,CAAA,CAAIY,CAAAA,CAGhBE,EAAqC,CACzC,WAAA,CAAaD,GAAiB,WAAA,EAAeL,CAAAA,CAAe,YAC5D,iBAAA,CAAmBK,CAAAA,EAAiB,iBAAA,EAAqBL,CAAAA,CAAe,iBAAA,CACxE,eAAA,CAAiBK,GAAiB,eAAA,EAAmBL,CAAAA,CAAe,eACtE,CAAA,CAKA,eAAeO,EAAcC,CAAAA,CAAmC,CAC9D,IAAMC,CAAAA,CAA2B,CAAE,OAAA,CAAS,MAAO,SAAA,CAAW,IAAA,CAAM,IAAK,IAAA,CAAM,QAAA,CAAU,IAAK,CAAA,CAE9F,GAAI,CACF,IAAMP,CAAAA,CAAM,MAAM,MAAM,CAAA,EAAGb,CAAU,GAAGG,CAAAA,CAAU,QAAQ,GAAI,CAC5D,OAAA,CAAS,CACP,aAAA,CAAiB,CAAA,OAAA,EAAUgB,CAAK,GAChC,cAAA,CAAgB,kBAClB,EACA,KAAA,CAAO,UACT,CAAC,CAAA,CAED,GAAI,CAACN,CAAAA,CAAI,EAAA,CACP,OAAOO,EAGT,IAAMC,CAAAA,CAAuB,MAAMR,CAAAA,CAAI,IAAA,GAAO,KAAA,CAAM,IAAM,IAAI,CAAA,CAGxDS,CAAAA,CAASL,CAAAA,CAAQ,YAAYI,CAAW,CAAA,CAE9C,OAAI,CAACC,CAAAA,EAAU,CAACA,CAAAA,CAAO,OAAA,CACdF,CAAAA,CAGFE,CACT,CAAA,KAAQ,CACN,OAAOF,CACT,CACF,CAKA,eAAeG,CAAAA,CAAaJ,EAAmC,CAC7D,OAAOD,EAAcC,CAAK,CAC5B,CAKA,eAAeK,CAAAA,CAAaC,EAA0C,CACpE,GAAI,CACF,IAAMZ,CAAAA,CAAM,MAAM,KAAA,CAAM,CAAA,EAAGb,CAAU,GAAGG,CAAAA,CAAU,OAAO,GAAI,CAC3D,MAAA,CAAQ,OACR,OAAA,CAAS,CACP,aAAA,CAAiB,CAAA,OAAA,EAAUsB,CAAQ,CAAA,CAAA,CACnC,eAAgB,kBAClB,CAAA,CACA,MAAO,UACT,CAAC,EAED,GAAI,CAACZ,CAAAA,CAAI,EAAA,CACP,OAAO,CAAE,QAAS,CAAA,CAAA,CAAO,QAAA,CAAU,IAAK,CAAA,CAG1C,IAAMQ,EAAuB,MAAMR,CAAAA,CAAI,MAAK,CAAE,KAAA,CAAM,IAAM,IAAI,CAAA,CAGxDa,EAAWT,CAAAA,CAAQ,iBAAA,CAAkBI,CAAW,CAAA,CAEtD,OAAKK,CAAAA,CAIE,CAAE,OAAA,CAAS,CAAA,CAAA,CAAM,SAAAA,CAAS,CAAA,CAHxB,CAAE,OAAA,CAAS,CAAA,CAAA,CAAO,SAAU,IAAK,CAI5C,CAAA,KAAQ,CACN,OAAO,CAAE,QAAS,KAAA,CAAO,QAAA,CAAU,IAAK,CAC1C,CACF,CAKA,eAAeC,CAAAA,EAA2C,CACxD,IAAMC,CAAAA,CAAc7B,CAAAA,CAAO,WAE3B,GAAI,CAAC6B,GAAa,OAAA,EAAW,CAACA,EAAY,WAAA,CACxC,OAAO,KAGT,GAAI,CACF,IAAMf,CAAAA,CAAM,MAAM,MAAM,CAAA,EAAGb,CAAU,GAAGG,CAAAA,CAAU,KAAK,CAAA,CAAA,CAAI,CACzD,MAAA,CAAQ,MAAA,CACR,QAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAA,CAC9C,IAAA,CAAM,KAAK,SAAA,CAAU,CACnB,QAAA,CAAUyB,CAAAA,CAAY,WAAA,CAAY,QAAA,CAClC,SAAUA,CAAAA,CAAY,WAAA,CAAY,QACpC,CAAC,CAAA,CACD,MAAO,UACT,CAAC,CAAA,CAED,GAAI,CAACf,CAAAA,CAAI,GACP,OAAO,IAAA,CAGT,IAAMQ,CAAAA,CAAuB,MAAMR,EAAI,IAAA,EAAK,CAAE,KAAA,CAAM,IAAM,IAAI,CAAA,CAG9D,OAAOI,CAAAA,CAAQ,eAAA,CAAgBI,CAAW,CAC5C,CAAA,KAAQ,CACN,OAAO,IACT,CACF,CAEA,OAAO,CACL,cAAAH,CAAAA,CACA,YAAA,CAAAK,EACA,YAAA,CAAAC,CAAAA,CACA,iBAAAG,CACF,CACF,CCjKA,SAASE,EAAAA,CAAcC,EAAkBC,CAAAA,CAAmD,CAC1F,GAAI,CAACA,CAAAA,EAAM,OAAA,CAAS,OAAO,IAAA,CAE3B,IAAMC,EAAUD,CAAAA,CAAK,OAAA,EAAW,EAAC,CAC3BE,CAAAA,CAAgBF,EAAK,aAAA,CAIrBG,CAAAA,CADWJ,EAAS,KAAA,CAAM,GAAG,EAAE,MAAA,CAAO,OAAO,EACrB,CAAC,CAAA,CAG/B,OAAII,CAAAA,EAAgBF,CAAAA,CAAQ,QAAA,CAASE,CAAY,CAAA,CACxCA,CAAAA,CAIFD,GAAiB,IAC1B,CAKO,SAASE,CAAAA,CACdpC,GAAAA,CACAqC,EACA,CACA,GAAM,CAAE,OAAA,CAAAC,CAAAA,CAAS,UAAA,CAAAC,EAAY,MAAA,CAAAC,CAAAA,CAAQ,KAAAR,CAAAA,CAAM,SAAA,CAAAhB,CAAU,CAAA,CAAIhB,GAAAA,CACnD,CAAE,aAAA,CAAAE,GAAc,CAAA,CAAIc,EAM1B,SAASyB,CAAAA,CAAiB3C,EAAkBe,CAAAA,CAAwB6B,CAAAA,CAA0B,CACxF5C,CAAAA,CAAI,OAAA,CAAQ,IAAI4C,CAAU,CAAA,EAAG,OAC/B7B,CAAAA,CAAS,OAAA,CAAQ,OAAO6B,CAAU,EAEtC,CAKA,SAASC,CAAAA,CAAqB7C,CAAAA,CAAkBe,CAAAA,CAAsC,CACpF,OAAA4B,EAAiB3C,CAAAA,CAAKe,CAAAA,CAAUyB,EAAQ,KAAK,CAAA,CAC7CG,EAAiB3C,CAAAA,CAAKe,CAAAA,CAAUyB,CAAAA,CAAQ,IAAI,CAAA,CACrCzB,CACT,CAKA,SAAS+B,CAAAA,CAAUC,EAAiBC,CAAAA,CAAS,GAAA,CAAmB,CAC9D,OAAO,IAAIC,YAAAA,CACT,IAAA,CAAK,SAAA,CAAU,CAAE,QAAS,KAAA,CAAO,OAAA,CAAAF,CAAQ,CAAC,CAAA,CAC1C,CAAE,MAAA,CAAAC,CAAAA,CAAQ,QAAS,CAAE,cAAA,CAAgB,kBAAmB,CAAE,CAC5D,CACF,CAKA,SAASE,EAAWjB,CAAAA,CAA2B,CAE7C,OAAA,CADmBS,CAAAA,EAAQ,UAAA,EAAc,IACvB,IAAA,CAAKS,CAAAA,EACrBlB,IAAakB,CAAAA,EAASlB,CAAAA,CAAS,WAAW,CAAA,EAAGkB,CAAK,CAAA,CAAA,CAAG,CACvD,CACF,CAKA,SAASC,CAAAA,CAAiBnB,CAAAA,CAA2B,CAEnD,OAAIS,CAAAA,EAAQ,mBACH,CAACW,CAAAA,CAAcpB,CAAQ,CAAA,EAAK,CAACiB,CAAAA,CAAWjB,CAAQ,CAAA,CAAA,CAGjCS,CAAAA,EAAQ,iBAAmB,EAAC,EAC7B,KAAKS,CAAAA,EAC1BlB,CAAAA,GAAakB,CAAAA,EAASlB,CAAAA,CAAS,UAAA,CAAW,CAAA,EAAGkB,CAAK,CAAA,CAAA,CAAG,CACvD,CACF,CAKA,SAASE,EAAcpB,CAAAA,CAA2B,CAEhD,OAAA,CADqBS,CAAAA,EAAQ,YAAA,EAAgB,IACzB,IAAA,CAAKS,CAAAA,EACvBlB,IAAakB,CAAAA,EAASlB,CAAAA,CAAS,WAAW,CAAA,EAAGkB,CAAK,CAAA,CAAA,CAAG,CACvD,CACF,CAKA,SAASG,CAAAA,CAAmBC,CAAAA,CAAmC,CAC7D,IAAMC,CAAAA,CAAed,GAAQ,iBAAA,CAG7B,OAAI,CAACc,CAAAA,EAAgBA,CAAAA,CAAa,MAAA,GAAW,EACpC,IAAA,CAGFD,CAAAA,CAAYC,EAAa,QAAA,CAASD,CAAS,EAAI,KACxD,CAKA,eAAeE,CAAAA,CACbzD,CAAAA,CACA0D,EACuB,CACvB,GAAM,CAAE,MAAA,CAAAC,CAAO,EAAI3D,CAAAA,CAAI,OAAA,CAGvB,GAAIyC,CAAAA,EAAY,OAAA,CAAS,CACvB,IAAMmB,CAAAA,CAAmB,MAAMrB,EAAW,gBAAA,EAAiB,CAE3D,GAAIqB,CAAAA,CAAkB,CACpB,IAAI7C,CAAAA,CAEJ,OAAI2C,CAAAA,CACF3C,EAAWkC,YAAAA,CAAa,IAAA,GACfG,CAAAA,CAAiBpD,CAAAA,CAAI,QAAQ,QAAQ,CAAA,CAE9Ce,CAAAA,CAAWkC,YAAAA,CAAa,QAAA,CAAS,IAAI,IAAI,QAAA,CAAUU,CAAM,CAAC,CAAA,CAE1D5C,CAAAA,CAAWkC,aAAa,IAAA,EAAK,CAG/BlC,EAAS,OAAA,CAAQ,GAAA,CAAIyB,EAAQ,KAAA,CAAOoB,CAAAA,CAAkB,CACpD,GAAGxD,GAAAA,CACH,OAAQ,IACV,CAAC,CAAA,CAEMW,CACT,CACF,CAGA,OAAI2C,CAAAA,CACKZ,CAAAA,CAAU,wBAAoB,GAAG,CAAA,CAGtCM,EAAiBpD,CAAAA,CAAI,OAAA,CAAQ,QAAQ,CAAA,CAChCiD,YAAAA,CAAa,QAAA,CAAS,IAAI,GAAA,CAAI,QAAA,CAAUU,CAAM,CAAC,CAAA,CAGjDV,aAAa,IAAA,EACtB,CAKA,eAAeY,CAAAA,CACb7D,CAAAA,CACA8D,EACAC,CAAAA,CACAC,CAAAA,CACAN,EACuB,CACvB,GAAM,CAAE,QAAA,CAAAzB,CAAAA,CAAU,OAAA0B,CAAO,CAAA,CAAI3D,EAAI,OAAA,CAC3B,CAAE,QAAAiE,CAAAA,CAAS,SAAA,CAAAV,EAAW,QAAA,CAAAW,CAAS,CAAA,CAAIJ,CAAAA,CACnCK,CAAAA,CAAUZ,CAAAA,GAAca,EAAY,KAAA,CAG1C,GAAI,CAACH,CAAAA,CAAS,CAEZ,GAAIF,CAAAA,EAAeC,CAAAA,CAAc,CAC/B,IAAMK,CAAAA,CAAgB,MAAM9B,EAAW,YAAA,CAAayB,CAAY,EAEhE,GAAIK,CAAAA,CAAc,SAAWA,CAAAA,CAAc,QAAA,CAAU,CACnD,IAAMC,CAAAA,CAAe,MAAM/B,EAAW,YAAA,CAAa8B,CAAAA,CAAc,QAAQ,CAAA,CAEzE,GAAIC,EAAa,OAAA,CAAS,CAExB,IAAMC,CAAAA,CAAiB,IAAI,OAAA,CAAQvE,EAAI,OAAO,CAAA,CAE1CsE,EAAa,QAAA,EACfC,CAAAA,CAAe,IAAIC,CAAAA,CAAQ,SAAA,CAAW,IAAA,CAAK,SAAA,CAAUF,CAAAA,CAAa,QAAQ,CAAC,CAAA,CAE7EC,CAAAA,CAAe,IAAIC,CAAAA,CAAQ,eAAA,CAAiBH,EAAc,QAAQ,CAAA,CAGlE,IAAMI,CAAAA,CAASzC,EAAAA,CAAcC,CAAAA,CAAUC,CAAI,CAAA,CACvCuC,CAAAA,EACFF,EAAe,GAAA,CAAIC,CAAAA,CAAQ,OAAQC,CAAM,CAAA,CAG3C,IAAI1D,CAAAA,CAEJ,OAAImC,CAAAA,CAAWjB,CAAQ,CAAA,CACrBlB,CAAAA,CAAWkC,aAAa,QAAA,CAAS,IAAI,IAAI,GAAA,CAAKU,CAAM,CAAC,CAAA,CAErD5C,CAAAA,CAAWkC,aAAa,IAAA,CAAK,CAAE,QAAS,CAAE,OAAA,CAASsB,CAAe,CAAE,CAAC,CAAA,CAGvExD,CAAAA,CAAS,OAAA,CAAQ,GAAA,CAAIyB,EAAQ,IAAA,CAAM6B,CAAAA,CAAc,SAAU,CACzD,GAAGjE,IACH,MAAA,CAAQA,GAAAA,CAAc,MACxB,CAAC,CAAA,CACDuC,CAAAA,CAAiB3C,EAAKe,CAAAA,CAAUyB,CAAAA,CAAQ,KAAK,CAAA,CAEtCzB,CACT,CACF,CACF,CAGA,IAAMA,CAAAA,CAAW,MAAM0C,CAAAA,CAAczD,EAAK0D,CAAU,CAAA,CAKpD,OAFyB3C,CAAAA,CAAS,OAAA,CAAQ,IAAIyB,CAAAA,CAAQ,KAAK,GAAG,KAAA,CAO5DG,CAAAA,CAAiB3C,EAAKe,CAAAA,CAAUyB,CAAAA,CAAQ,IAAI,CAAA,CAH5CK,CAAAA,CAAqB7C,EAAKe,CAAQ,CAAA,CAM7BA,CACT,CAGA,IAAMwD,CAAAA,CAAiB,IAAI,OAAA,CAAQvE,CAAAA,CAAI,OAAO,CAAA,CAE1CkE,CAAAA,EACFK,EAAe,GAAA,CAAIC,CAAAA,CAAQ,SAAA,CAAW,IAAA,CAAK,SAAA,CAAUN,CAAQ,CAAC,CAAA,CAIhE,IAAMO,EAASzC,EAAAA,CAAcC,CAAAA,CAAUC,CAAI,CAAA,CAM3C,GALIuC,CAAAA,EACFF,CAAAA,CAAe,GAAA,CAAIC,CAAAA,CAAQ,OAAQC,CAAM,CAAA,CAIvC,CAACN,CAAAA,EAAW,CAACb,EAAmBC,CAAS,CAAA,CAAG,CAC9C,GAAIG,CAAAA,CAAY,CACd,IAAM3C,CAAAA,CAAW+B,EAAU,oCAAA,CAA8B,GAAG,EAC5D,OAAOD,CAAAA,CAAqB7C,CAAAA,CAAKe,CAAQ,CAC3C,CAEA,IAAMA,CAAAA,CAAWkC,YAAAA,CAAa,SAAS,IAAI,GAAA,CAAI,SAAUU,CAAM,CAAC,CAAA,CAChE,OAAOd,CAAAA,CAAqB7C,CAAAA,CAAKe,CAAQ,CAC3C,CAGA,GAAIoD,CAAAA,CACF,OAAIT,EACKT,YAAAA,CAAa,IAAA,CAAK,CAAE,OAAA,CAAS,CAAE,OAAA,CAASsB,CAAe,CAAE,CAAC,EAI/DnB,CAAAA,CAAiBnB,CAAQ,EACpBgB,YAAAA,CAAa,QAAA,CAAS,IAAI,GAAA,CAAI,QAAA,CAAUU,CAAM,CAAC,CAAA,CAGjDV,YAAAA,CAAa,KAAK,CAAE,OAAA,CAAS,CAAE,OAAA,CAASsB,CAAe,CAAE,CAAC,CAAA,CAInE,GAAIrB,EAAWjB,CAAQ,CAAA,CACrB,OAAOgB,YAAAA,CAAa,QAAA,CAAS,IAAI,GAAA,CAAI,GAAA,CAAKU,CAAM,CAAC,CAAA,CAInD,IAAM5C,EAAWkC,YAAAA,CAAa,IAAA,CAAK,CAAE,OAAA,CAAS,CAAE,QAASsB,CAAe,CAAE,CAAC,CAAA,CAC3E,OAAA5B,CAAAA,CAAiB3C,EAAKe,CAAAA,CAAUyB,CAAAA,CAAQ,KAAK,CAAA,CAEtCzB,CACT,CAEA,OAAO,CACL,qBAAA8B,CAAAA,CACA,SAAA,CAAAC,EACA,UAAA,CAAAI,CAAAA,CACA,iBAAAE,CAAAA,CACA,aAAA,CAAAC,EACA,kBAAA,CAAAC,CAAAA,CACA,aAAA,CAAAG,CAAAA,CACA,sBAAA,CAAAI,CACF,CACF,CCxRO,SAASa,EAAoBxE,CAAAA,CAA4B,CAK9D,eAAeyE,CAAAA,CAAcC,CAAAA,CAAoC,CAC/D,IAAMC,CAAAA,CAAcC,EAAAA,GAEpB,OAAO,CAAA,EADM,MAAMC,EAAAA,CAAY7E,CAAAA,CAAO,OAAQ0E,CAAAA,CAAWC,CAAW,CACtD,CAAA,CAAA,EAAIA,CAAW,CAAA,CAC/B,CAKA,SAASG,CAAAA,CAAgBhF,EAAwC,CAC/D,IAAMiF,EAASjF,CAAAA,CAAI,MAAA,CAAO,aAAY,CAGtC,GAAIE,EAAO,aAAA,CAAc,QAAA,CAAS+E,CAAM,CAAA,CACtC,OAAO,CAAE,KAAA,CAAO,IAAK,CAAA,CAGvB,IAAMC,CAAAA,CAAWhF,CAAAA,CAAO,SAGxB,GAAIgF,CAAAA,GAAa,kBAAoBA,CAAAA,GAAa,MAAA,CAAQ,CACxD,IAAMC,CAAAA,CAAcC,CAAAA,CAAsBpF,CAAG,CAAA,CAY7C,GAVIkF,IAAa,gBAAA,EAKbC,CAAAA,CAAY,OAKZA,CAAAA,CAAY,MAAA,GAAW,kBAIzB,OAAOA,CAEX,CAGA,OAAID,CAAAA,GAAa,eAAA,EAAmBA,IAAa,MAAA,CACxCG,CAAAA,CAAqBrF,CAAG,CAAA,CAG1B,CAAE,MAAO,IAAK,CACvB,CAMA,SAASoF,CAAAA,CAAsBpF,EAAwC,CACrE,IAAMsF,EAAetF,CAAAA,CAAI,OAAA,CAAQ,IAAI,gBAAgB,CAAA,CAGrD,GAAI,CAACsF,CAAAA,CACH,OAAO,CAAE,KAAA,CAAO,KAAA,CAAO,OAAQ,iBAAkB,CAAA,CAInD,GAAIA,CAAAA,GAAiB,aAAA,CACnB,OAAO,CAAE,KAAA,CAAO,IAAK,EAIvB,GAAIA,CAAAA,GAAiB,OAAQ,CAC3B,IAAML,EAASjF,CAAAA,CAAI,MAAA,CAAO,WAAA,EAAY,CACtC,OAAIE,CAAAA,CAAO,cAAc,QAAA,CAAS+E,CAAM,EAC/B,CAAE,KAAA,CAAO,IAAK,CAAA,CAGhB,CAAE,KAAA,CAAO,KAAA,CAAO,MAAA,CAAQ,iCAAkC,CACnE,CAGA,OAAIK,IAAiB,WAAA,CACfpF,CAAAA,CAAO,cACF,CAAE,KAAA,CAAO,IAAK,CAAA,CAGhB,CAAE,KAAA,CAAO,MAAO,MAAA,CAAQ,uBAAwB,EAIrDoF,CAAAA,GAAiB,YAAA,CACZ,CAAE,KAAA,CAAO,KAAA,CAAO,MAAA,CAAQ,oBAAqB,CAAA,CAI/C,CAAE,MAAO,KAAA,CAAO,MAAA,CAAQ,wBAAyB,CAC1D,CAKA,SAASD,CAAAA,CAAqBrF,CAAAA,CAAwC,CAEpE,IAAMuF,CAAAA,CAAcvF,CAAAA,CAAI,QAAQ,GAAA,CAAIE,CAAAA,CAAO,UAAU,CAAA,EAAG,KAAA,CAExD,GAAI,CAACqF,CAAAA,CACH,OAAO,CAAE,KAAA,CAAO,MAAO,MAAA,CAAQ,sBAAuB,EAIxD,IAAMC,CAAAA,CAAcxF,EAAI,OAAA,CAAQ,GAAA,CAAIE,CAAAA,CAAO,UAAU,CAAA,CAErD,OAAKsF,EAKAC,EAAAA,CAAkBF,CAAAA,CAAaC,CAAW,CAAA,CAKjCD,CAAAA,CAAY,MAAM,GAAG,CAAA,CACzB,MAAA,GAAW,CAAA,CACZ,CAAE,KAAA,CAAO,MAAO,MAAA,CAAQ,sBAAuB,EAGjD,CAAE,KAAA,CAAO,IAAK,CAAA,CATZ,CAAE,KAAA,CAAO,KAAA,CAAO,MAAA,CAAQ,gBAAiB,EALzC,CAAE,KAAA,CAAO,MAAO,MAAA,CAAQ,sBAAuB,CAe1D,CAKA,eAAeG,EACb3E,CAAAA,CACA6D,CAAAA,CACuB,CACvB,IAAMtD,CAAAA,CAAQ,MAAMqD,CAAAA,CAAcC,CAAS,EAE3C,OAAA7D,CAAAA,CAAS,OAAA,CAAQ,GAAA,CAAIb,CAAAA,CAAO,UAAA,CAAYoB,EAAO,CAC7C,QAAA,CAAU,MACV,MAAA,CAAQ,OAAA,CAAQ,IAAI,QAAA,GAAa,YAAA,CACjC,QAAA,CAAU,QAAA,CACV,IAAA,CAAM,GACR,CAAC,CAAA,CAEMP,CACT,CAEA,OAAO,CACL,gBAAAiE,CAAAA,CACA,aAAA,CAAAL,CAAAA,CACA,gBAAA,CAAAe,CACF,CACF,CAOA,SAASZ,EAAAA,EAA8B,CACrC,GAAI,OAAO,OAAW,GAAA,EAAe,MAAA,CAAO,gBAAiB,CAC3D,IAAMa,EAAQ,IAAI,UAAA,CAAW,EAAE,CAAA,CAC/B,OAAA,MAAA,CAAO,gBAAgBA,CAAK,CAAA,CACrB,KAAA,CAAM,IAAA,CAAKA,CAAAA,CAAOC,CAAAA,EAAKA,EAAE,QAAA,CAAS,EAAE,EAAE,QAAA,CAAS,CAAA,CAAG,GAAG,CAAC,CAAA,CAAE,IAAA,CAAK,EAAE,CACxE,CAEA,OAAO,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,EAAE,EAAE,SAAA,CAAU,CAAC,CAAA,CAAI,IAAA,CAAK,GAAA,EAAI,CAAE,SAAS,EAAE,CACzE,CAKA,eAAeb,EAAAA,CACbc,EACAjB,CAAAA,CACAC,CAAAA,CACiB,CACjB,IAAM9B,CAAAA,CAAU,CAAA,EAAG6B,EAAU,MAAM,CAAA,CAAA,EAAIA,CAAS,CAAA,CAAA,EAAIC,CAAAA,CAAY,MAAM,CAAA,CAAA,EAAIA,CAAW,CAAA,CAAA,CAErF,GAAI,OAAO,MAAA,CAAW,KAAe,MAAA,CAAO,MAAA,CAAQ,CAClD,IAAMiB,CAAAA,CAAU,IAAI,WAAA,CACdC,CAAAA,CAAUD,CAAAA,CAAQ,MAAA,CAAOD,CAAM,CAAA,CAC/BG,EAAcF,CAAAA,CAAQ,MAAA,CAAO/C,CAAO,CAAA,CAEpCkD,CAAAA,CAAM,MAAM,MAAA,CAAO,MAAA,CAAO,SAAA,CAC9B,KAAA,CACAF,CAAAA,CACA,CAAE,KAAM,MAAA,CAAQ,IAAA,CAAM,SAAU,CAAA,CAChC,KAAA,CACA,CAAC,MAAM,CACT,EAEMG,CAAAA,CAAY,MAAM,OAAO,MAAA,CAAO,IAAA,CAAK,OAAQD,CAAAA,CAAKD,CAAW,EACnE,OAAO,KAAA,CAAM,IAAA,CAAK,IAAI,UAAA,CAAWE,CAAS,EAAGN,CAAAA,EAC3CA,CAAAA,CAAE,SAAS,EAAE,CAAA,CAAE,SAAS,CAAA,CAAG,GAAG,CAChC,CAAA,CAAE,IAAA,CAAK,EAAE,CACX,CAGA,IAAIO,EAAO,CAAA,CACLC,CAAAA,CAAMP,EAAS9C,CAAAA,CACrB,IAAA,IAASsD,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAID,CAAAA,CAAI,OAAQC,CAAAA,EAAAA,CAAK,CACnC,IAAMC,CAAAA,CAAOF,CAAAA,CAAI,WAAWC,CAAC,CAAA,CAC7BF,GAASA,CAAAA,EAAQ,CAAA,EAAKA,EAAQG,CAAAA,CAC9BH,CAAAA,CAAOA,EAAOA,EAChB,CACA,OAAO,IAAA,CAAK,GAAA,CAAIA,CAAI,CAAA,CAAE,QAAA,CAAS,EAAE,CACnC,CAKA,SAASV,GAAkBc,CAAAA,CAAWX,CAAAA,CAAoB,CACxD,GAAIW,CAAAA,CAAE,MAAA,GAAWX,CAAAA,CAAE,MAAA,CACjB,OAAO,OAGT,IAAIY,CAAAA,CAAS,EACb,IAAA,IAASH,CAAAA,CAAI,EAAGA,CAAAA,CAAIE,CAAAA,CAAE,MAAA,CAAQF,CAAAA,EAAAA,CAC5BG,CAAAA,EAAUD,CAAAA,CAAE,WAAWF,CAAC,CAAA,CAAIT,EAAE,UAAA,CAAWS,CAAC,EAG5C,OAAOG,CAAAA,GAAW,CACpB,CC3NO,SAASC,CAAAA,CAAkBvG,CAAAA,CAAiC,CAEjE,IAAMwG,CAAAA,CAAQ,IAAI,GAAA,CAGZC,CAAAA,CAAkB,WAAA,CAAY,IAAM,CACxC,IAAMC,EAAM,IAAA,CAAK,GAAA,GACjB,IAAA,GAAW,CAACX,CAAAA,CAAKY,CAAK,CAAA,GAAKH,CAAAA,CACrBG,EAAM,OAAA,EAAWD,CAAAA,EACnBF,EAAM,MAAA,CAAOT,CAAG,EAGtB,CAAA,CAAG/F,CAAAA,CAAO,QAAQ,CAAA,CAGd,OAAO,OAAA,CAAY,KAAe,OAAA,CAAQ,EAAA,EAC5C,QAAQ,EAAA,CAAG,YAAA,CAAc,IAAM,aAAA,CAAcyG,CAAe,CAAC,CAAA,CAM/D,SAASG,EAAW7E,CAAAA,CAA2B,CAC7C,OAAO/B,CAAAA,CAAO,UAAA,CAAW,KAAK6G,CAAAA,EAExBA,CAAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,CACf9E,CAAAA,CAAS,WAAW8E,CAAAA,CAAQ,KAAA,CAAM,EAAG,EAAE,CAAC,EAE7CA,CAAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,CAChB9E,CAAAA,CAAS,UAAA,CAAW8E,EAAQ,KAAA,CAAM,CAAA,CAAG,EAAE,CAAC,CAAA,CAE1C9E,IAAa8E,CACrB,CACH,CAKA,SAASC,CAAAA,CAAMhH,CAAAA,CAAmC,CAChD,IAAMiC,CAAAA,CAAWjC,EAAI,OAAA,CAAQ,QAAA,CAG7B,GAAI8G,CAAAA,CAAW7E,CAAQ,EACrB,OAAO,CACL,QAAS,IAAA,CACT,SAAA,CAAW/B,EAAO,WAAA,CAClB,OAAA,CAAS,EACT,KAAA,CAAOA,CAAAA,CAAO,WAChB,CAAA,CAGF,IAAM+F,CAAAA,CAAM/F,EAAO,KAAA,CAAMF,CAAG,EACtB4G,CAAAA,CAAM,IAAA,CAAK,KAAI,CAEjBC,CAAAA,CAAQH,CAAAA,CAAM,GAAA,CAAIT,CAAG,CAAA,CAGzB,GAAI,CAACY,CAAAA,EAASA,EAAM,OAAA,EAAWD,CAAAA,CAC7B,OAAAC,CAAAA,CAAQ,CACN,KAAA,CAAO,CAAA,CACP,OAAA,CAASD,CAAAA,CAAM1G,EAAO,QACxB,CAAA,CACAwG,EAAM,GAAA,CAAIT,CAAAA,CAAKY,CAAK,CAAA,CAEb,CACL,QAAS,IAAA,CACT,SAAA,CAAW3G,EAAO,WAAA,CAAc,CAAA,CAChC,QAAS2G,CAAAA,CAAM,OAAA,CACf,MAAO3G,CAAAA,CAAO,WAChB,CAAA,CAIF2G,CAAAA,CAAM,KAAA,EAAA,CAEN,IAAMI,EAAY,IAAA,CAAK,GAAA,CAAI,EAAG/G,CAAAA,CAAO,WAAA,CAAc2G,EAAM,KAAK,CAAA,CAG9D,OAAO,CACL,OAAA,CAHcA,CAAAA,CAAM,OAAS3G,CAAAA,CAAO,WAAA,CAIpC,UAAA+G,CAAAA,CACA,OAAA,CAASJ,EAAM,OAAA,CACf,KAAA,CAAO3G,CAAAA,CAAO,WAChB,CACF,CAKA,SAASgH,CAAAA,CAAanG,CAAAA,CAAwByF,EAAuC,CACnF,OAAAzF,EAAS,OAAA,CAAQ,GAAA,CAAI,oBAAqByF,CAAAA,CAAO,KAAA,CAAM,UAAU,CAAA,CACjEzF,EAAS,OAAA,CAAQ,GAAA,CAAI,wBAAyByF,CAAAA,CAAO,SAAA,CAAU,QAAA,EAAU,CAAA,CACzEzF,CAAAA,CAAS,QAAQ,GAAA,CAAI,mBAAA,CAAqB,KAAK,IAAA,CAAKyF,CAAAA,CAAO,QAAU,GAAI,CAAA,CAAE,QAAA,EAAU,CAAA,CAC9EzF,CACT,CAKA,SAASoG,CAAAA,CAAsBnH,EAAkBwG,CAAAA,CAAuC,CAEtF,GAAItG,CAAAA,CAAO,aAAA,CAAe,CACxB,IAAMa,CAAAA,CAAWb,CAAAA,CAAO,cAAcF,CAAG,CAAA,CACzC,OAAOkH,CAAAA,CAAanG,CAAAA,CAAUyF,CAAM,CACtC,CAGA,IAAMzF,CAAAA,CAAWkC,YAAAA,CAAa,IAAA,CAC5B,CACE,OAAA,CAAS,KAAA,CACT,QAAS,4CAAA,CACT,UAAA,CAAY,KAAK,IAAA,CAAA,CAAMuD,CAAAA,CAAO,OAAA,CAAU,IAAA,CAAK,GAAA,EAAI,EAAK,GAAI,CAC5D,CAAA,CACA,CAAE,MAAA,CAAQ,GAAI,CAChB,CAAA,CAEA,OAAAzF,CAAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAA,CAAe,KAAK,IAAA,CAAA,CAAMyF,CAAAA,CAAO,QAAU,IAAA,CAAK,GAAA,IAAS,GAAI,CAAA,CAAE,QAAA,EAAU,CAAA,CACvFU,CAAAA,CAAanG,EAAUyF,CAAM,CACtC,CAKA,SAASY,CAAAA,CAAMnB,EAAmB,CAChCS,CAAAA,CAAM,OAAOT,CAAG,EAClB,CAKA,SAASoB,CAAAA,EAAc,CACrBX,CAAAA,CAAM,KAAA,GACR,CAKA,SAASY,CAAAA,EAAe,CACtB,OAAOZ,CAAAA,CAAM,IACf,CAEA,OAAO,CACL,KAAA,CAAAM,CAAAA,CACA,aAAAE,CAAAA,CACA,qBAAA,CAAAC,CAAAA,CACA,UAAA,CAAAL,CAAAA,CACA,KAAA,CAAAM,EACA,KAAA,CAAAC,CAAAA,CACA,KAAAC,CACF,CACF,CCrKO,SAASC,CAAAA,CAAkBrH,CAAAA,CAA6B,CAI7D,SAASsH,CAAAA,CAAUC,EAA+B,CAChD,OAAOvH,EAAO,OAAA,EAAWA,CAAAA,CAAO,OAAO,QAAA,CAASuH,CAAI,CACtD,CAKA,SAASC,EAAM1H,CAAAA,CAAiC,CAC9C,OACEA,CAAAA,CAAI,OAAA,CAAQ,IAAI,iBAAiB,CAAA,EAAG,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,GAAG,IAAA,EAAK,EACxDA,EAAI,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA,EAC3B,IAEJ,CAKA,eAAe2H,CAAAA,CACbF,CAAAA,CACAzH,EACA4H,CAAAA,CAKe,CACf,GAAI,CAACJ,CAAAA,CAAUC,CAAI,CAAA,CACjB,OAGF,IAAMI,CAAAA,CAAoB,CACxB,IAAA,CAAAJ,EACA,SAAA,CAAW,IAAI,KACf,EAAA,CAAIC,CAAAA,CAAM1H,CAAG,CAAA,CACb,MAAA,CAAQ4H,EAAQ,MAAA,CAChB,IAAA,CAAM5H,EAAI,OAAA,CAAQ,QAAA,CAClB,OAAQA,CAAAA,CAAI,MAAA,CACZ,QAAS4H,CAAAA,CAAQ,OAAA,CACjB,QAAA,CAAUA,CAAAA,CAAQ,QACpB,CAAA,CAGA,GAAI1H,CAAAA,CAAO,MAAA,CACT,GAAI,CACF,MAAMA,EAAO,MAAA,CAAO2H,CAAK,EAC3B,CAAA,MAASC,CAAAA,CAAO,CAEd,QAAQ,KAAA,CAAM,sCAAA,CAAwCA,CAAK,EAC7D,CAEJ,CAOA,SAASC,CAAAA,CAAY/H,CAAAA,CAAkBgI,CAAAA,CAAiBC,CAAAA,CAAoC,CAC1F,OAAON,CAAAA,CAAK,cAAA,CAAgB3H,EAAK,CAAE,OAAA,CAAS,KAAM,MAAA,CAAAgI,CAAAA,CAAQ,QAAA,CAAAC,CAAS,CAAC,CACtE,CAKA,SAASC,CAAAA,CAASlI,EAAkBiI,CAAAA,CAAoC,CACtE,OAAON,CAAAA,CAAK,WAAA,CAAa3H,CAAAA,CAAK,CAAE,OAAA,CAAS,KAAA,CAAO,SAAAiI,CAAS,CAAC,CAC5D,CAKA,SAASE,EAAYnI,CAAAA,CAAkBgI,CAAAA,CAAiBC,CAAAA,CAAoC,CAC1F,OAAON,CAAAA,CAAK,eAAgB3H,CAAAA,CAAK,CAAE,QAAS,IAAA,CAAM,MAAA,CAAAgI,EAAQ,QAAA,CAAAC,CAAS,CAAC,CACtE,CAKA,SAASG,EAAUpI,CAAAA,CAAkBiI,CAAAA,CAAoC,CACvE,OAAON,CAAAA,CAAK,aAAc3H,CAAAA,CAAK,CAAE,QAAS,IAAA,CAAM,QAAA,CAAAiI,CAAS,CAAC,CAC5D,CAKA,SAASI,CAAAA,CAAarI,EAAkBgI,CAAAA,CAAiBC,CAAAA,CAAoC,CAC3F,OAAON,CAAAA,CAAK,eAAA,CAAiB3H,EAAK,CAAE,OAAA,CAAS,MAAO,MAAA,CAAAgI,CAAAA,CAAQ,SAAAC,CAAS,CAAC,CACxE,CAKA,SAASK,CAAAA,CAAStI,EAAkBiI,CAAAA,CAAoC,CACtE,OAAON,CAAAA,CAAK,WAAA,CAAa3H,EAAK,CAAE,OAAA,CAAS,KAAA,CAAO,QAAA,CAAAiI,CAAS,CAAC,CAC5D,CAKA,SAASM,EAAkBvI,CAAAA,CAAkBiI,CAAAA,CAAoC,CAC/E,OAAON,CAAAA,CAAK,qBAAsB3H,CAAAA,CAAK,CAAE,QAAS,KAAA,CAAO,QAAA,CAAAiI,CAAS,CAAC,CACrE,CAKA,SAASH,CAAAA,CAAM9H,CAAAA,CAAkBwI,CAAAA,CAAYP,CAAAA,CAAoC,CAC/E,OAAON,CAAAA,CAAK,OAAA,CAAS3H,EAAK,CACxB,OAAA,CAAS,MACT,QAAA,CAAU,CACR,GAAGiI,CAAAA,CACH,KAAA,CAAOO,CAAAA,CAAI,QACX,KAAA,CAAOA,CAAAA,CAAI,KACb,CACF,CAAC,CACH,CAEA,OAAO,CACL,IAAA,CAAAb,CAAAA,CACA,SAAA,CAAAH,EAEA,WAAA,CAAAO,CAAAA,CACA,SAAAG,CAAAA,CACA,WAAA,CAAAC,EACA,SAAA,CAAAC,CAAAA,CACA,aAAAC,CAAAA,CACA,QAAA,CAAAC,EACA,iBAAA,CAAAC,CAAAA,CACA,MAAAT,CACF,CACF,CC7FO,SAASW,EAAAA,CAAgBC,CAAAA,CAA6B,CAE3D,IAAMxI,CAAAA,CAASD,EAAmByI,CAAU,CAAA,CAGtCnG,EAAatB,CAAAA,CAAsBf,CAAM,EAGzCyI,CAAAA,CAAWrG,CAAAA,CAAepC,CAAAA,CAAQqC,CAAU,CAAA,CAG5C/B,CAAAA,CAAOkE,EAAoBxE,CAAAA,CAAO,SAAA,CAAU,IAAI,CAAA,CAChD0I,CAAAA,CAAcnC,EAAkBvG,CAAAA,CAAO,SAAA,CAAU,SAAS,CAAA,CAC1DU,CAAAA,CAAQ2G,CAAAA,CAAkBrH,EAAO,SAAA,CAAU,KAAK,EAKtD,eAAe2I,CAAAA,CAAU7I,EAAyC,CAChE,GAAM,CAAE,QAAA,CAAAiC,CAAAA,CAAU,MAAA,CAAA0B,CAAO,CAAA,CAAI3D,CAAAA,CAAI,QAC3B0D,CAAAA,CAAazB,CAAAA,CAAS,WAAW,MAAM,CAAA,CAI7C,GAAI/B,CAAAA,CAAO,SAAA,CAAU,SAAA,CAAU,QAAS,CACtC,IAAM4I,EAAkBF,CAAAA,CAAY,KAAA,CAAM5I,CAAG,CAAA,CAE7C,GAAI,CAAC8I,CAAAA,CAAgB,OAAA,CACnB,OAAA,MAAMlI,EAAM,iBAAA,CAAkBZ,CAAAA,CAAK,CACjC,KAAA,CAAO8I,CAAAA,CAAgB,MACvB,OAAA,CAASA,CAAAA,CAAgB,OAC3B,CAAC,CAAA,CACMF,CAAAA,CAAY,sBAAsB5I,CAAAA,CAAK8I,CAAe,CAEjE,CAIA,GAAI5I,EAAO,SAAA,CAAU,IAAA,CAAK,QAAS,CACjC,IAAM6I,EAAavI,CAAAA,CAAK,eAAA,CAAgBR,CAAG,CAAA,CAE3C,GAAI,CAAC+I,CAAAA,CAAW,KAAA,CACd,OAAA,MAAMnI,CAAAA,CAAM,QAAA,CAASZ,CAAAA,CAAK,CAAE,MAAA,CAAQ+I,CAAAA,CAAW,MAAO,CAAC,CAAA,CAChD9F,aAAa,IAAA,CAClB,CAAE,OAAA,CAAS,KAAA,CAAO,OAAA,CAAS,wBAAyB,EACpD,CAAE,MAAA,CAAQ,GAAI,CAChB,CAEJ,CAIA,GAAI/C,CAAAA,CAAO,qBAAA,EAAyBwD,CAAAA,EAAAA,CACb1D,CAAAA,CAAI,OAAA,CAAQ,IAAI,QAAQ,CAAA,EAAK,IACjC,QAAA,CAAS,WAAW,EACnC,OAAOiD,YAAAA,CAAa,SAAS,IAAI,GAAA,CAAI,IAAKU,CAAM,CAAC,EAMrD,GAAIzD,CAAAA,CAAO,WAAY,CACrB,IAAM8I,CAAAA,CAAe,MAAM9I,CAAAA,CAAO,UAAA,CAAWF,CAAG,CAAA,CAChD,GAAIgJ,EACF,OAAOA,CAEX,CAIA,GAAA,CADsB9I,CAAAA,CAAO,aAAA,EAAiB,EAAC,EAC7B,IAAA,CAAK+I,GAAQhH,CAAAA,CAAS,UAAA,CAAWgH,CAAI,CAAC,CAAA,CACtD,OAAOC,CAAAA,CAAelJ,CAAAA,CAAKiD,YAAAA,CAAa,IAAA,EAAK,CAAG,CAAE,gBAAiB,KAAA,CAAO,OAAA,CAAS,MAAO,SAAA,CAAW,IAAA,CAAM,KAAM,IAAK,CAAC,EAYzH,GARqB,CACnB,kBACA,kBAAA,CACA,cAAA,CACA,oBACA,oBACF,CAAA,CAEiB,SAAShB,CAAQ,CAAA,CAChC,OAAOiH,CAAAA,CAAelJ,CAAAA,CAAKiD,YAAAA,CAAa,MAAK,CAAG,CAAE,gBAAiB,KAAA,CAAO,OAAA,CAAS,MAAO,SAAA,CAAW,IAAA,CAAM,IAAA,CAAM,IAAK,CAAC,CAAA,CAIzH,IAAMkG,CAAAA,CAAYnJ,CAAAA,CAAI,SAAS,GAAA,CAAIE,CAAAA,CAAO,QAAQ,IAAI,CAAA,EAAG,KAAA,CACnDuC,CAAAA,CAAazC,CAAAA,CAAI,OAAA,EAAS,IAAIE,CAAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG,KAAA,CACrD8D,EAAemF,CAAAA,EAAa1G,CAAAA,CAC5BsB,CAAAA,CAAc,CAAC,CAACoF,CAAAA,CAGtB,GAAI,CAACnF,CAAAA,CAAc,CACjB,MAAMpD,CAAAA,CAAM,SAASZ,CAAAA,CAAK,CAAE,MAAA,CAAQ,UAAW,CAAC,CAAA,CAChD,IAAMe,CAAAA,CAAW,MAAM4H,EAAS,aAAA,CAAc3I,CAAAA,CAAK0D,CAAU,CAAA,CAC7D,OAAOwF,CAAAA,CAAelJ,CAAAA,CAAKe,CAAAA,CAAU,CAAE,gBAAiB,KAAA,CAAO,OAAA,CAAS,MAAO,SAAA,CAAW,IAAA,CAAM,KAAM,IAAK,CAAC,CAC9G,CAGA,IAAM+C,CAAAA,CAAY,MAAMvB,CAAAA,CAAW,YAAA,CAAayB,CAAY,CAAA,CAGtDoF,CAAAA,CAAyB,CAC7B,eAAA,CAAiBtF,CAAAA,CAAU,SAAWA,CAAAA,CAAU,SAAA,GAAc,QAC9D,OAAA,CAASA,CAAAA,CAAU,SAAWA,CAAAA,CAAU,SAAA,GAAc,QACtD,SAAA,CAAWA,CAAAA,CAAU,SAAA,CACrB,IAAA,CAAMA,CAAAA,CAAU,QAClB,EAGA,GAAIA,CAAAA,CAAU,QACZ,GAAIA,CAAAA,CAAU,YAAc,OAAA,CAC1B,MAAMlD,CAAAA,CAAM,SAAA,CAAUZ,CAAG,CAAA,CAAA,KACpB,CACL,IAAMgI,CAAAA,CAASlE,EAAU,QAAA,EAAU,EAAA,EAAI,UAAS,CAChD,MAAMlD,CAAAA,CAAM,WAAA,CAAYZ,CAAAA,CAAKgI,CAAAA,CAAQ,CAAE,SAAA,CAAWlE,CAAAA,CAAU,SAAU,CAAC,EACzE,MAEA,MAAMlD,CAAAA,CAAM,SAASZ,CAAAA,CAAK,CAAE,OAAQ,eAAgB,CAAC,EAIvD,IAAMe,CAAAA,CAAW,MAAM4H,CAAAA,CAAS,sBAAA,CAC9B3I,CAAAA,CACA8D,CAAAA,CACAC,CAAAA,CACAC,CAAAA,CACAN,CACF,CAAA,CAGI2F,CAAAA,CAAgB,MAAMH,CAAAA,CAAelJ,CAAAA,CAAKe,EAAUqI,CAAU,CAAA,CAElE,GAAIlJ,CAAAA,CAAO,SAAA,CAAU,IAAA,CAAK,SAAWkJ,CAAAA,CAAW,eAAA,CAAiB,CAC/D,IAAMxE,CAAAA,CAAYd,EAAU,QAAA,EAAU,EAAA,EAAI,QAAA,EAAS,EAAKE,CAAAA,CAAa,KAAA,CAAM,EAAG,EAAE,CAAA,CAChFqF,EAAgB,MAAM7I,CAAAA,CAAK,iBAAiB6I,CAAAA,CAAezE,CAAS,EACtE,CAGA,GAAI1E,EAAO,SAAA,CAAU,SAAA,CAAU,QAAS,CACtC,IAAM4I,EAAkBF,CAAAA,CAAY,KAAA,CAAM5I,CAAG,CAAA,CAC7CqJ,CAAAA,CAAgBT,CAAAA,CAAY,aAAaS,CAAAA,CAAeP,CAAe,EACzE,CAEA,OAAOO,CACT,CAKA,eAAeH,CAAAA,CAAelJ,CAAAA,CAAkBe,CAAAA,CAAwBqI,CAAAA,CAA+C,CACrH,OAAIlJ,CAAAA,CAAO,UACFA,CAAAA,CAAO,SAAA,CAAUF,EAAKe,CAAAA,CAAUqI,CAAU,CAAA,CAE5CrI,CACT,CAGA,OAAA8H,EAAU,MAAA,CAAS3I,CAAAA,CACnB2I,EAAU,IAAA,CAAOrI,CAAAA,CACjBqI,EAAU,WAAA,CAAcD,CAAAA,CACxBC,CAAAA,CAAU,KAAA,CAAQjI,CAAAA,CAEXiI,CACT,CCvLA,SAASS,GAAeC,CAAAA,CAAkBC,CAAAA,CAA6B,CACrE,GAAI,CAACA,CAAAA,EAAYA,EAAS,MAAA,GAAW,CAAA,CAAG,OAAO,MAAA,CAG/C,IAAMC,EAAqBF,CAAAA,CACxB,OAAA,CAAQ,UAAA,CAAY,EAAE,CAAA,CACtB,OAAA,CAAQ,MAAO,EAAE,CAAA,CAEpB,OAAOC,CAAAA,CAAS,IAAA,CAAKzC,GAAW,CAC9B,GAAIA,CAAAA,GAAY0C,CAAAA,CAAoB,OAAO,KAAA,CAE3C,GAAI1C,CAAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,CAAG,CACzB,IAAM2C,CAAAA,CAAe3C,CAAAA,CAClB,QAAQ,OAAA,CAAS,cAAc,EAC/B,OAAA,CAAQ,KAAA,CAAO,OAAO,CAAA,CACtB,OAAA,CAAQ,gBAAiB,IAAI,CAAA,CAEhC,OADc,IAAI,MAAA,CAAO,CAAA,CAAA,EAAI2C,CAAY,CAAA,CAAA,CAAG,CAAA,CAC/B,KAAKD,CAAkB,CACtC,CAEA,OAAO,MACT,CAAC,CACH,CAwBO,SAASE,GAAmBzJ,GAAAA,CAA4B,CAC7D,GAAM,CACJ,UAAA,CAAAC,EACA,cAAA,CAAAyJ,CAAAA,CAAiB,YAAA,CACjB,eAAA,CAAAC,CAAAA,CAAkB,aAAA,CAClB,kBAAAC,CAAAA,CAAoB,KAAA,CACpB,gBAAAC,CAAAA,CAAkB,GAClB,cAAA,CAAAC,CAAAA,CAAiB,CAAC,cAAA,CAAgB,QAAA,CAAU,kBAAmB,kBAAkB,CAAA,CACjF,eAAAC,CAAAA,CAAiB,CAAC,OAAQ,YAAA,CAAc,QAAQ,CAAA,CAChD,gBAAA,CAAAC,CAAAA,CACA,iBAAA,CAAAC,CACF,CAAA,CAAIjK,GAAAA,CAGEkK,EAAUjK,CAAAA,CAAW,OAAA,CAAQ,MAAO,EAAE,CAAA,CAK5C,SAASkK,CAAAA,CAAerK,CAAAA,CAAkBuJ,CAAAA,CAA2B,CAQnE,OANuBvJ,CAAAA,CAAI,QAAQ,GAAA,CAAIwE,CAAAA,CAAQ,SAAS,CAAA,GACjC,MAAA,EAKnB8E,EAAAA,CAAeC,CAAAA,CAAUQ,CAAe,CAAA,CACnC,KAIFD,CACT,CAKA,eAAeQ,CAAAA,CAAQtK,CAAAA,CAAyC,CAC9D,GAAI,CAEF,IAAMuK,CAAAA,CAAM,IAAI,IAAIvK,CAAAA,CAAI,GAAG,EACrBuJ,CAAAA,CAAWgB,CAAAA,CAAI,SAAS,OAAA,CAAQ,WAAA,CAAa,EAAE,CAAA,CAG/CC,CAAAA,CAAa,IAAI,IAAI,CAAA,EAAGJ,CAAO,IAAIb,CAAQ,CAAA,CAAE,EACnDiB,CAAAA,CAAW,MAAA,CAASD,CAAAA,CAAI,MAAA,CAGxB,IAAME,CAAAA,CAAU,IAAI,OAAA,CAmBpB,GAhBAT,EAAe,OAAA,CAAQU,CAAAA,EAAc,CACnC,IAAMC,CAAAA,CAAQ3K,CAAAA,CAAI,OAAA,CAAQ,GAAA,CAAI0K,CAAU,EACpCC,CAAAA,EACFF,CAAAA,CAAQ,IAAIC,CAAAA,CAAYC,CAAK,EAEjC,CAAC,CAAA,CAGD3K,CAAAA,CAAI,OAAA,CAAQ,OAAA,CAAQ,CAAC2K,EAAO1E,CAAAA,GAAQ,CAClC,IAAM2E,CAAAA,CAAW3E,CAAAA,CAAI,aAAY,CAC7B,CAACgE,CAAAA,CAAe,QAAA,CAASW,CAAQ,CAAA,EAAK,CAACH,CAAAA,CAAQ,GAAA,CAAIxE,CAAG,CAAA,EACxDwE,CAAAA,CAAQ,IAAIxE,CAAAA,CAAK0E,CAAK,EAE1B,CAAC,CAAA,CAGG,CAACN,EAAerK,CAAAA,CAAKuJ,CAAQ,EAAG,CAClC,IAAMJ,EAAYnJ,CAAAA,CAAI,OAAA,CAAQ,GAAA,CAAI4J,CAAc,CAAA,EAAG,KAAA,CAC7CnH,EAAazC,CAAAA,CAAI,OAAA,CAAQ,IAAI6J,CAAe,CAAA,EAAG,MAC/CvI,CAAAA,CAAQ6H,CAAAA,EAAa1G,EAEvBnB,CAAAA,EACFmJ,CAAAA,CAAQ,IAAIjG,CAAAA,CAAQ,aAAA,CAAe,UAAUlD,CAAK,CAAA,CAAE,EAExD,CAGAmJ,CAAAA,CAAQ,MAAA,CAAOjG,CAAAA,CAAQ,SAAS,CAAA,CAGhC,IAAMqG,CAAAA,CAAeX,CAAAA,CACjB,MAAMA,CAAAA,CAAiBlK,CAAAA,CAAKyK,CAAO,CAAA,CACnCA,CAAAA,CAGAK,CAAAA,CAAwB,IAAA,CAC5B,GAAI9K,CAAAA,CAAI,SAAW,KAAA,EAASA,CAAAA,CAAI,SAAW,MAAA,CAAQ,CACjD,IAAM+K,CAAAA,CAAc/K,CAAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,EAAK,GAEnD+K,CAAAA,CAAY,QAAA,CAAS,kBAAkB,CAAA,CACzCD,CAAAA,CAAO,MAAM9K,CAAAA,CAAI,IAAA,GACR+K,CAAAA,CAAY,QAAA,CAAS,qBAAqB,CAAA,CACnDD,CAAAA,CAAO,MAAM9K,CAAAA,CAAI,QAAA,GAEjB8K,CAAAA,CAAO,MAAM9K,CAAAA,CAAI,IAAA,GAErB,CAGA,IAAMgL,CAAAA,CAAkB,MAAM,MAAMR,CAAAA,CAAW,QAAA,GAAY,CACzD,MAAA,CAAQxK,CAAAA,CAAI,MAAA,CACZ,OAAA,CAAS6K,CAAAA,CACT,KAAAC,CACF,CAAC,EAGKC,CAAAA,CAAcC,CAAAA,CAAgB,QAAQ,GAAA,CAAI,cAAc,CAAA,EAAK,EAAA,CAC/DC,CAAAA,CAEAF,CAAAA,CAAY,SAAS,kBAAkB,CAAA,CACzCE,EAAe,MAAMD,CAAAA,CAAgB,MAAK,CAE1CC,CAAAA,CAAe,MAAMD,CAAAA,CAAgB,WAAA,GAIvC,IAAME,CAAAA,CAAkB,IAAI,OAAA,CAC5BF,CAAAA,CAAgB,QAAQ,OAAA,CAAQ,CAACL,CAAAA,CAAO1E,CAAAA,GAAQ,CAC9C,IAAM2E,EAAW3E,CAAAA,CAAI,WAAA,GAEhB,CAAC,mBAAA,CAAqB,aAAc,YAAY,CAAA,CAAE,QAAA,CAAS2E,CAAQ,CAAA,EACtEM,CAAAA,CAAgB,IAAIjF,CAAAA,CAAK0E,CAAK,EAElC,CAAC,CAAA,CAGD,IAAI5J,CAAAA,CAAW,IAAIkC,YAAAA,CAAagI,CAAAA,CAAc,CAC5C,MAAA,CAAQD,EAAgB,MAAA,CACxB,UAAA,CAAYA,EAAgB,UAAA,CAC5B,OAAA,CAASE,CACX,CAAC,CAAA,CAGD,OAAIf,CAAAA,GACFpJ,CAAAA,CAAW,MAAMoJ,EAAkBpJ,CAAQ,CAAA,CAAA,CAGtCA,CACT,CAAA,MAAS+G,CAAAA,CAAO,CACd,OAAA,OAAA,CAAQ,KAAA,CAAM,eAAA,CAAiBA,CAAK,CAAA,CAE7B7E,YAAAA,CAAa,KAClB,CACE,OAAA,CAAS,MACT,OAAA,CAAS,2CAAA,CACT,MAAO6E,CAAAA,YAAiB,KAAA,CAAQA,CAAAA,CAAM,OAAA,CAAU,eAClD,CAAA,CACA,CAAE,MAAA,CAAQ,GAAI,CAChB,CACF,CACF,CAGA,OAAAwC,CAAAA,CAAQ,MAAA,CAASpK,GAAAA,CAEVoK,CACT","file":"index.js","sourcesContent":["/**\r\n * Config Resolver\r\n * Resolves and validates configuration with defaults\r\n */\r\n\r\nimport type { NextRequest } from 'next/server';\r\nimport type {\r\n AuthProxyConfig,\r\n InternalProxyConfig,\r\n ApiClientConfig,\r\n ResolvedCookieOptions,\r\n EndpointConfig,\r\n ResolvedCsrfConfig,\r\n ResolvedRateLimitConfig,\r\n ResolvedAuditConfig,\r\n AuditEventType,\r\n} from './types';\r\n\r\nimport {\r\n DEFAULT_COOKIE_OPTIONS,\r\n DEFAULT_ENDPOINTS,\r\n DEFAULT_CSRF_CONFIG,\r\n DEFAULT_RATE_LIMIT_CONFIG,\r\n DEFAULT_AUDIT_CONFIG,\r\n} from './constants';\r\n\r\n/**\r\n * Generates a random secret for CSRF HMAC signing\r\n */\r\nfunction generateCsrfSecret(): string {\r\n // Use crypto if available (Node.js)\r\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\r\n return crypto.randomUUID() + crypto.randomUUID();\r\n }\r\n // Fallback: timestamp + random\r\n return `${Date.now()}-${Math.random().toString(36).substring(2)}`;\r\n}\r\n\r\n/**\r\n * Default rate limit key function (IP-based)\r\n */\r\nfunction defaultRateLimitKeyFn(req: NextRequest): string {\r\n const forwarded = req.headers.get('x-forwarded-for');\r\n const ip = forwarded?.split(',')[0]?.trim() || \r\n req.headers.get('x-real-ip') || \r\n 'unknown';\r\n return `rl:${ip}`;\r\n}\r\n\r\n/**\r\n * Resolves proxy configuration with defaults\r\n */\r\nexport function resolveProxyConfig(config: AuthProxyConfig): InternalProxyConfig {\r\n // Validate required fields\r\n if (!config.apiBaseUrl) {\r\n throw new Error('next-api-layer: apiBaseUrl is required');\r\n }\r\n \r\n if (!config.cookies?.user || !config.cookies?.guest) {\r\n throw new Error('next-api-layer: cookies.user and cookies.guest are required');\r\n }\r\n\r\n // Ensure apiBaseUrl ends with /\r\n const apiBaseUrl = config.apiBaseUrl.endsWith('/')\r\n ? config.apiBaseUrl\r\n : `${config.apiBaseUrl}/`;\r\n\r\n // Resolve cookie options\r\n const cookieOptions: ResolvedCookieOptions = {\r\n ...DEFAULT_COOKIE_OPTIONS,\r\n ...config.cookies.options,\r\n };\r\n\r\n // Resolve endpoints\r\n const endpoints: Required<EndpointConfig> = {\r\n ...DEFAULT_ENDPOINTS,\r\n ...config.endpoints,\r\n };\r\n\r\n // Resolve CSRF config\r\n const csrf: ResolvedCsrfConfig = {\r\n enabled: config.csrf?.enabled ?? false,\r\n strategy: config.csrf?.strategy ?? DEFAULT_CSRF_CONFIG.strategy,\r\n secret: config.csrf?.secret ?? generateCsrfSecret(),\r\n cookieName: config.csrf?.cookieName ?? DEFAULT_CSRF_CONFIG.cookieName,\r\n headerName: config.csrf?.headerName ?? DEFAULT_CSRF_CONFIG.headerName,\r\n ignoreMethods: config.csrf?.ignoreMethods ?? DEFAULT_CSRF_CONFIG.ignoreMethods,\r\n trustSameSite: config.csrf?.trustSameSite ?? DEFAULT_CSRF_CONFIG.trustSameSite,\r\n };\r\n\r\n // Resolve rate limit config\r\n const rateLimit: ResolvedRateLimitConfig = {\r\n enabled: config.rateLimit?.enabled ?? false,\r\n windowMs: config.rateLimit?.windowMs ?? DEFAULT_RATE_LIMIT_CONFIG.windowMs,\r\n maxRequests: config.rateLimit?.maxRequests ?? DEFAULT_RATE_LIMIT_CONFIG.maxRequests,\r\n keyFn: config.rateLimit?.keyFn ?? defaultRateLimitKeyFn,\r\n skipRoutes: config.rateLimit?.skipRoutes ?? DEFAULT_RATE_LIMIT_CONFIG.skipRoutes,\r\n onRateLimited: config.rateLimit?.onRateLimited,\r\n };\r\n\r\n // Resolve audit config\r\n const audit: ResolvedAuditConfig = {\r\n enabled: config.audit?.enabled ?? false,\r\n events: config.audit?.events ?? [...DEFAULT_AUDIT_CONFIG.events] as AuditEventType[],\r\n logger: config.audit?.logger,\r\n };\r\n\r\n return {\r\n ...config,\r\n apiBaseUrl,\r\n _resolved: {\r\n cookieOptions,\r\n endpoints,\r\n csrf,\r\n rateLimit,\r\n audit,\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Resolves API client configuration with defaults\r\n */\r\nexport function resolveApiClientConfig(config: ApiClientConfig = {}) {\r\n return {\r\n sanitization: {\r\n enabled: config.sanitization?.enabled ?? true,\r\n allowedTags: config.sanitization?.allowedTags,\r\n skipFields: config.sanitization?.skipFields ?? [],\r\n skipEndpoints: config.sanitization?.skipEndpoints ?? [],\r\n },\r\n i18n: {\r\n enabled: config.i18n?.enabled ?? false,\r\n paramName: config.i18n?.paramName ?? 'lang',\r\n locales: config.i18n?.locales ?? [],\r\n defaultLocale: config.i18n?.defaultLocale ?? 'en',\r\n },\r\n auth: {\r\n skipByDefault: config.auth?.skipByDefault ?? false,\r\n publicEndpoints: config.auth?.publicEndpoints ?? [],\r\n },\r\n methodSpoofing: config.methodSpoofing ?? false,\r\n errorMessages: {\r\n noToken: config.errorMessages?.noToken ?? 'Token bulunamadı.',\r\n connectionError: config.errorMessages?.connectionError ?? 'Bağlantı hatası oluştu.',\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type guard to check if a value is defined\r\n */\r\nexport function isDefined<T>(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null;\r\n}\r\n","/**\r\n * Token Validation\r\n * Handles token validation and refresh with backend API\r\n */\r\n\r\nimport type { \r\n TokenInfo, \r\n RefreshResult, \r\n AuthMeResponse, \r\n GuestTokenResponse,\r\n InternalProxyConfig,\r\n ResponseMappers,\r\n} from '../shared/types';\r\n\r\n/**\r\n * Default response parsers (standard format)\r\n */\r\nconst defaultMappers: Required<ResponseMappers> = {\r\n // Default: { success: true, data: { type, exp, ...user } }\r\n parseAuthMe: (response: unknown): TokenInfo | null => {\r\n const res = response as AuthMeResponse | null;\r\n if (!res?.success || !res?.data) {\r\n return null;\r\n }\r\n return {\r\n isValid: true,\r\n tokenType: res.data.type || 'user',\r\n exp: res.data.exp || null,\r\n userData: res.data,\r\n };\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseRefreshToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.success && res?.data?.accessToken ? res.data.accessToken : null;\r\n },\r\n \r\n // Default: { success: true, data: { accessToken } }\r\n parseGuestToken: (response: unknown): string | null => {\r\n const res = response as GuestTokenResponse | null;\r\n return res?.data?.accessToken || null;\r\n },\r\n};\r\n\r\n/**\r\n * Creates token validation functions\r\n */\r\nexport function createTokenValidation(\r\n config: InternalProxyConfig\r\n) {\r\n const { apiBaseUrl, _resolved, responseMappers } = config;\r\n const { endpoints } = _resolved;\r\n \r\n // Merge custom mappers with defaults\r\n const mappers: Required<ResponseMappers> = {\r\n parseAuthMe: responseMappers?.parseAuthMe || defaultMappers.parseAuthMe,\r\n parseRefreshToken: responseMappers?.parseRefreshToken || defaultMappers.parseRefreshToken,\r\n parseGuestToken: responseMappers?.parseGuestToken || defaultMappers.parseGuestToken,\r\n };\r\n\r\n /**\r\n * Validates a token against the backend\r\n */\r\n async function validateToken(token: string): Promise<TokenInfo> {\r\n const invalidResult: TokenInfo = { isValid: false, tokenType: null, exp: null, userData: null };\r\n \r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.validate}`, {\r\n headers: {\r\n 'Authorization': `Bearer ${token}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return invalidResult;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const parsed = mappers.parseAuthMe(rawResponse);\r\n \r\n if (!parsed || !parsed.isValid) {\r\n return invalidResult;\r\n }\r\n\r\n return parsed;\r\n } catch {\r\n return invalidResult;\r\n }\r\n }\r\n\r\n /**\r\n * Gets token info (validates with backend)\r\n */\r\n async function getTokenInfo(token: string): Promise<TokenInfo> {\r\n return validateToken(token);\r\n }\r\n\r\n /**\r\n * Refreshes a token\r\n */\r\n async function refreshToken(oldToken: string): Promise<RefreshResult> {\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.refresh}`, {\r\n method: 'POST',\r\n headers: {\r\n 'Authorization': `Bearer ${oldToken}`,\r\n 'Content-Type': 'application/json',\r\n },\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n const newToken = mappers.parseRefreshToken(rawResponse);\r\n\r\n if (!newToken) {\r\n return { success: false, newToken: null };\r\n }\r\n\r\n return { success: true, newToken };\r\n } catch {\r\n return { success: false, newToken: null };\r\n }\r\n }\r\n\r\n /**\r\n * Creates a guest token\r\n */\r\n async function createGuestToken(): Promise<string | null> {\r\n const guestConfig = config.guestToken;\r\n \r\n if (!guestConfig?.enabled || !guestConfig.credentials) {\r\n return null;\r\n }\r\n\r\n try {\r\n const res = await fetch(`${apiBaseUrl}${endpoints.guest}`, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify({\r\n username: guestConfig.credentials.username,\r\n password: guestConfig.credentials.password,\r\n }),\r\n cache: 'no-store',\r\n });\r\n\r\n if (!res.ok) {\r\n return null;\r\n }\r\n\r\n const rawResponse: unknown = await res.json().catch(() => null);\r\n \r\n // Use custom or default mapper\r\n return mappers.parseGuestToken(rawResponse);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n return {\r\n validateToken,\r\n getTokenInfo,\r\n refreshToken,\r\n createGuestToken,\r\n };\r\n}\r\n\r\nexport type TokenValidation = ReturnType<typeof createTokenValidation>;\r\n","/**\r\n * Proxy Handlers\r\n * Request handling logic for different scenarios\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { InternalProxyConfig, TokenInfo } from '../shared/types';\r\nimport { HEADERS, TOKEN_TYPES } from '../shared/constants';\r\nimport type { TokenValidation } from './tokenValidation';\r\n\r\n/**\r\n * Extracts locale from pathname based on i18n config\r\n * e.g., /en/dashboard → 'en', /dashboard → defaultLocale or null\r\n */\r\nfunction extractLocale(pathname: string, i18n?: InternalProxyConfig['i18n']): string | null {\r\n if (!i18n?.enabled) return null;\r\n \r\n const locales = i18n.locales ?? [];\r\n const defaultLocale = i18n.defaultLocale;\r\n \r\n // Extract first path segment\r\n const segments = pathname.split('/').filter(Boolean);\r\n const firstSegment = segments[0];\r\n \r\n // Check if it's a valid locale\r\n if (firstSegment && locales.includes(firstSegment)) {\r\n return firstSegment;\r\n }\r\n \r\n // Return default locale if provided\r\n return defaultLocale ?? null;\r\n}\r\n\r\n/**\r\n * Creates proxy handlers\r\n */\r\nexport function createHandlers(\r\n config: InternalProxyConfig,\r\n validation: TokenValidation\r\n) {\r\n const { cookies, guestToken, access, i18n, _resolved } = config;\r\n const { cookieOptions } = _resolved;\r\n\r\n /**\r\n * Safely deletes a cookie only if it exists in the request.\r\n * Prevents empty-value cookies from being created when deleting non-existent cookies.\r\n */\r\n function safeDeleteCookie(req: NextRequest, response: NextResponse, cookieName: string): void {\r\n if (req.cookies.get(cookieName)?.value) {\r\n response.cookies.delete(cookieName);\r\n }\r\n }\r\n\r\n /**\r\n * Deletes all auth cookies from response (only if they exist)\r\n */\r\n function deleteAllAuthCookies(req: NextRequest, response: NextResponse): NextResponse {\r\n safeDeleteCookie(req, response, cookies.guest);\r\n safeDeleteCookie(req, response, cookies.user);\r\n return response;\r\n }\r\n\r\n /**\r\n * Creates a JSON error response\r\n */\r\n function jsonError(message: string, status = 500): NextResponse {\r\n return new NextResponse(\r\n JSON.stringify({ success: false, message }),\r\n { status, headers: { 'Content-Type': 'application/json' } }\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is an auth page\r\n */\r\n function isAuthPage(pathname: string): boolean {\r\n const authRoutes = access?.authRoutes ?? [];\r\n return authRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is a protected route\r\n */\r\n function isProtectedRoute(pathname: string): boolean {\r\n // If protectedByDefault is true, everything is protected except public/auth routes\r\n if (access?.protectedByDefault) {\r\n return !isPublicRoute(pathname) && !isAuthPage(pathname);\r\n }\r\n \r\n const protectedRoutes = access?.protectedRoutes ?? [];\r\n return protectedRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if pathname is explicitly public\r\n */\r\n function isPublicRoute(pathname: string): boolean {\r\n const publicRoutes = access?.publicRoutes ?? [];\r\n return publicRoutes.some(route => \r\n pathname === route || pathname.startsWith(`${route}/`)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if token type is allowed\r\n */\r\n function isTokenTypeAllowed(tokenType: string | null): boolean {\r\n const allowedTypes = access?.allowedTokenTypes;\r\n \r\n // If no restriction, all types allowed\r\n if (!allowedTypes || allowedTypes.length === 0) {\r\n return true;\r\n }\r\n \r\n return tokenType ? allowedTypes.includes(tokenType) : false;\r\n }\r\n\r\n /**\r\n * Handles request when no token is present\r\n */\r\n async function handleNoToken(\r\n req: NextRequest,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { origin } = req.nextUrl;\r\n \r\n // Try to create guest token\r\n if (guestToken?.enabled) {\r\n const guestAccessToken = await validation.createGuestToken();\r\n \r\n if (guestAccessToken) {\r\n let response: NextResponse;\r\n \r\n if (isApiRoute) {\r\n response = NextResponse.next();\r\n } else if (isProtectedRoute(req.nextUrl.pathname)) {\r\n // Redirect to login if protected route\r\n response = NextResponse.redirect(new URL('/login', origin));\r\n } else {\r\n response = NextResponse.next();\r\n }\r\n \r\n response.cookies.set(cookies.guest, guestAccessToken, {\r\n ...cookieOptions,\r\n maxAge: 3600, // 1 hour default for guest tokens\r\n });\r\n \r\n return response;\r\n }\r\n }\r\n \r\n // No guest token - just continue or redirect\r\n if (isApiRoute) {\r\n return jsonError('Token bulunamadı', 401);\r\n }\r\n \r\n if (isProtectedRoute(req.nextUrl.pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n \r\n return NextResponse.next();\r\n }\r\n\r\n /**\r\n * Handles token validation result\r\n */\r\n async function handleValidationResult(\r\n req: NextRequest,\r\n tokenInfo: TokenInfo,\r\n isUserToken: boolean,\r\n currentToken: string,\r\n isApiRoute: boolean\r\n ): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const { isValid, tokenType, userData } = tokenInfo;\r\n const isGuest = tokenType === TOKEN_TYPES.GUEST;\r\n\r\n // ===== TOKEN INVALID =====\r\n if (!isValid) {\r\n // Try refresh if user token\r\n if (isUserToken && currentToken) {\r\n const refreshResult = await validation.refreshToken(currentToken);\r\n \r\n if (refreshResult.success && refreshResult.newToken) {\r\n const newTokenInfo = await validation.getTokenInfo(refreshResult.newToken);\r\n \r\n if (newTokenInfo.isValid) {\r\n // Successful refresh\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (newTokenInfo.userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(newTokenInfo.userData));\r\n }\r\n requestHeaders.set(HEADERS.REFRESHED_TOKEN, refreshResult.newToken);\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n let response: NextResponse;\r\n \r\n if (isAuthPage(pathname)) {\r\n response = NextResponse.redirect(new URL('/', origin));\r\n } else {\r\n response = NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n response.cookies.set(cookies.user, refreshResult.newToken, {\r\n ...cookieOptions,\r\n maxAge: cookieOptions.maxAge,\r\n });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n\r\n return response;\r\n }\r\n }\r\n }\r\n\r\n // Refresh failed or no user token - handle as no token\r\n const response = await handleNoToken(req, isApiRoute);\r\n \r\n // Check if handleNoToken created a new guest token\r\n const hasNewGuestToken = response.cookies.get(cookies.guest)?.value;\r\n \r\n if (!hasNewGuestToken) {\r\n // No new guest token created - delete all auth cookies\r\n deleteAllAuthCookies(req, response);\r\n } else {\r\n // New guest token created - only delete the invalid user cookie\r\n safeDeleteCookie(req, response, cookies.user);\r\n }\r\n \r\n return response;\r\n }\r\n\r\n // ===== TOKEN VALID =====\r\n const requestHeaders = new Headers(req.headers);\r\n \r\n if (userData) {\r\n requestHeaders.set(HEADERS.AUTH_USER, JSON.stringify(userData));\r\n }\r\n \r\n // Set locale header if i18n is enabled\r\n const locale = extractLocale(pathname, i18n);\r\n if (locale) {\r\n requestHeaders.set(HEADERS.LOCALE, locale);\r\n }\r\n\r\n // Check if token type is allowed\r\n if (!isGuest && !isTokenTypeAllowed(tokenType)) {\r\n if (isApiRoute) {\r\n const response = jsonError('Bu işlem için yetkiniz yok', 403);\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n \r\n const response = NextResponse.redirect(new URL('/login', origin));\r\n return deleteAllAuthCookies(req, response);\r\n }\r\n\r\n // Guest token handling\r\n if (isGuest) {\r\n if (isApiRoute) {\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // Protected routes require login\r\n if (isProtectedRoute(pathname)) {\r\n return NextResponse.redirect(new URL('/login', origin));\r\n }\r\n\r\n return NextResponse.next({ request: { headers: requestHeaders } });\r\n }\r\n\r\n // User token - block auth pages\r\n if (isAuthPage(pathname)) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n\r\n // Normal access\r\n const response = NextResponse.next({ request: { headers: requestHeaders } });\r\n safeDeleteCookie(req, response, cookies.guest);\r\n \r\n return response;\r\n }\r\n\r\n return {\r\n deleteAllAuthCookies,\r\n jsonError,\r\n isAuthPage,\r\n isProtectedRoute,\r\n isPublicRoute,\r\n isTokenTypeAllowed,\r\n handleNoToken,\r\n handleValidationResult,\r\n };\r\n}\r\n\r\nexport type Handlers = ReturnType<typeof createHandlers>;\r\n","/**\r\n * CSRF Protection\r\n * \r\n * Implements OWASP recommended CSRF protection:\r\n * - Fetch Metadata (Sec-Fetch-Site header) for modern browsers (98%+ coverage)\r\n * - Signed HMAC Double-Submit Cookie as fallback\r\n * \r\n * @see https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedCsrfConfig } from '../shared/types';\r\n\r\nexport interface CsrfValidationResult {\r\n valid: boolean;\r\n reason?: string;\r\n}\r\n\r\n/**\r\n * Creates CSRF validator functions\r\n */\r\nexport function createCsrfValidator(config: ResolvedCsrfConfig) {\r\n /**\r\n * Generate HMAC-signed CSRF token\r\n * Format: hmac.randomValue\r\n */\r\n async function generateToken(sessionId: string): Promise<string> {\r\n const randomValue = generateRandomValue();\r\n const hmac = await computeHmac(config.secret, sessionId, randomValue);\r\n return `${hmac}.${randomValue}`;\r\n }\r\n\r\n /**\r\n * Validate CSRF request using configured strategy\r\n */\r\n function validateRequest(req: NextRequest): CsrfValidationResult {\r\n const method = req.method.toUpperCase();\r\n \r\n // Skip safe methods\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n\r\n const strategy = config.strategy;\r\n \r\n // Fetch Metadata validation (primary, modern browsers)\r\n if (strategy === 'fetch-metadata' || strategy === 'both') {\r\n const fetchResult = validateFetchMetadata(req);\r\n \r\n if (strategy === 'fetch-metadata') {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata passes, we're good\r\n if (fetchResult.valid) {\r\n return fetchResult;\r\n }\r\n \r\n // 'both' strategy: if Fetch Metadata fails or not available, try double-submit\r\n if (fetchResult.reason === 'missing-headers') {\r\n // Fall through to double-submit\r\n } else {\r\n // Explicit cross-site rejection from Fetch Metadata\r\n return fetchResult;\r\n }\r\n }\r\n\r\n // Double-Submit Cookie validation (fallback)\r\n if (strategy === 'double-submit' || strategy === 'both') {\r\n return validateDoubleSubmit(req);\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Validate using Fetch Metadata headers (Sec-Fetch-Site)\r\n * @see https://web.dev/fetch-metadata/\r\n */\r\n function validateFetchMetadata(req: NextRequest): CsrfValidationResult {\r\n const secFetchSite = req.headers.get('sec-fetch-site');\r\n \r\n // No Fetch Metadata headers (older browser or stripped by proxy)\r\n if (!secFetchSite) {\r\n return { valid: false, reason: 'missing-headers' };\r\n }\r\n\r\n // Same-origin requests are always trusted\r\n if (secFetchSite === 'same-origin') {\r\n return { valid: true };\r\n }\r\n\r\n // None = direct navigation (bookmark, typed URL) - allow for GET\r\n if (secFetchSite === 'none') {\r\n const method = req.method.toUpperCase();\r\n if (config.ignoreMethods.includes(method)) {\r\n return { valid: true };\r\n }\r\n // Non-safe method from direct navigation is suspicious\r\n return { valid: false, reason: 'direct-navigation-unsafe-method' };\r\n }\r\n\r\n // Same-site: trust based on config\r\n if (secFetchSite === 'same-site') {\r\n if (config.trustSameSite) {\r\n return { valid: true };\r\n }\r\n // Conservative: don't trust same-site by default (subdomain takeover risk)\r\n return { valid: false, reason: 'same-site-not-trusted' };\r\n }\r\n\r\n // Cross-site: reject state-changing requests\r\n if (secFetchSite === 'cross-site') {\r\n return { valid: false, reason: 'cross-site-request' };\r\n }\r\n\r\n // Unknown value - be conservative\r\n return { valid: false, reason: 'unknown-sec-fetch-site' };\r\n }\r\n\r\n /**\r\n * Validate using Double-Submit Cookie pattern with HMAC\r\n */\r\n function validateDoubleSubmit(req: NextRequest): CsrfValidationResult {\r\n // Get token from cookie\r\n const cookieToken = req.cookies.get(config.cookieName)?.value;\r\n \r\n if (!cookieToken) {\r\n return { valid: false, reason: 'missing-cookie-token' };\r\n }\r\n\r\n // Get token from header (or form field)\r\n const headerToken = req.headers.get(config.headerName);\r\n \r\n if (!headerToken) {\r\n return { valid: false, reason: 'missing-header-token' };\r\n }\r\n\r\n // Tokens must match (constant-time comparison to prevent timing attacks)\r\n if (!constantTimeEqual(cookieToken, headerToken)) {\r\n return { valid: false, reason: 'token-mismatch' };\r\n }\r\n\r\n // Validate HMAC structure\r\n const parts = cookieToken.split('.');\r\n if (parts.length !== 2) {\r\n return { valid: false, reason: 'invalid-token-format' };\r\n }\r\n\r\n return { valid: true };\r\n }\r\n\r\n /**\r\n * Create response with CSRF cookie set\r\n */\r\n async function attachCsrfCookie(\r\n response: NextResponse, \r\n sessionId: string\r\n ): Promise<NextResponse> {\r\n const token = await generateToken(sessionId);\r\n \r\n response.cookies.set(config.cookieName, token, {\r\n httpOnly: false, // Must be readable by JS\r\n secure: process.env.NODE_ENV === 'production',\r\n sameSite: 'strict',\r\n path: '/',\r\n });\r\n\r\n return response;\r\n }\r\n\r\n return {\r\n validateRequest,\r\n generateToken,\r\n attachCsrfCookie,\r\n };\r\n}\r\n\r\n// ==================== Helper Functions ====================\r\n\r\n/**\r\n * Generate cryptographically random value\r\n */\r\nfunction generateRandomValue(): string {\r\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\r\n const array = new Uint8Array(32);\r\n crypto.getRandomValues(array);\r\n return Array.from(array, b => b.toString(16).padStart(2, '0')).join('');\r\n }\r\n // Fallback (less secure, for edge cases)\r\n return Math.random().toString(36).substring(2) + Date.now().toString(36);\r\n}\r\n\r\n/**\r\n * Compute HMAC-SHA256\r\n */\r\nasync function computeHmac(\r\n secret: string, \r\n sessionId: string, \r\n randomValue: string\r\n): Promise<string> {\r\n const message = `${sessionId.length}!${sessionId}!${randomValue.length}!${randomValue}`;\r\n \r\n if (typeof crypto !== 'undefined' && crypto.subtle) {\r\n const encoder = new TextEncoder();\r\n const keyData = encoder.encode(secret);\r\n const messageData = encoder.encode(message);\r\n \r\n const key = await crypto.subtle.importKey(\r\n 'raw',\r\n keyData,\r\n { name: 'HMAC', hash: 'SHA-256' },\r\n false,\r\n ['sign']\r\n );\r\n \r\n const signature = await crypto.subtle.sign('HMAC', key, messageData);\r\n return Array.from(new Uint8Array(signature), b => \r\n b.toString(16).padStart(2, '0')\r\n ).join('');\r\n }\r\n \r\n // Fallback (less secure)\r\n let hash = 0;\r\n const str = secret + message;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash;\r\n }\r\n return Math.abs(hash).toString(16);\r\n}\r\n\r\n/**\r\n * Constant-time string comparison (prevents timing attacks)\r\n */\r\nfunction constantTimeEqual(a: string, b: string): boolean {\r\n if (a.length !== b.length) {\r\n return false;\r\n }\r\n \r\n let result = 0;\r\n for (let i = 0; i < a.length; i++) {\r\n result |= a.charCodeAt(i) ^ b.charCodeAt(i);\r\n }\r\n \r\n return result === 0;\r\n}\r\n\r\nexport type CsrfValidator = ReturnType<typeof createCsrfValidator>;\r\n","/**\r\n * Rate Limiting\r\n * \r\n * Implements token bucket algorithm for rate limiting.\r\n * In-memory store by default, designed for single-instance deployments.\r\n * \r\n * For horizontal scaling (multiple instances), use a custom store\r\n * with Redis or similar distributed cache.\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { ResolvedRateLimitConfig } from '../shared/types';\r\n\r\ninterface RateLimitEntry {\r\n count: number;\r\n resetAt: number;\r\n}\r\n\r\nexport interface RateLimitResult {\r\n allowed: boolean;\r\n remaining: number;\r\n resetAt: number;\r\n limit: number;\r\n}\r\n\r\n/**\r\n * Creates a rate limiter with in-memory store\r\n */\r\nexport function createRateLimiter(config: ResolvedRateLimitConfig) {\r\n // In-memory store\r\n const store = new Map<string, RateLimitEntry>();\r\n \r\n // Cleanup expired entries periodically\r\n const cleanupInterval = setInterval(() => {\r\n const now = Date.now();\r\n for (const [key, entry] of store) {\r\n if (entry.resetAt <= now) {\r\n store.delete(key);\r\n }\r\n }\r\n }, config.windowMs);\r\n\r\n // Prevent memory leak in long-running processes\r\n if (typeof process !== 'undefined' && process.on) {\r\n process.on('beforeExit', () => clearInterval(cleanupInterval));\r\n }\r\n\r\n /**\r\n * Check if route should skip rate limiting\r\n */\r\n function shouldSkip(pathname: string): boolean {\r\n return config.skipRoutes.some(pattern => {\r\n // Simple glob matching\r\n if (pattern.endsWith('*')) {\r\n return pathname.startsWith(pattern.slice(0, -1));\r\n }\r\n if (pattern.endsWith('**')) {\r\n return pathname.startsWith(pattern.slice(0, -2));\r\n }\r\n return pathname === pattern;\r\n });\r\n }\r\n\r\n /**\r\n * Check rate limit for request\r\n */\r\n function check(req: NextRequest): RateLimitResult {\r\n const pathname = req.nextUrl.pathname;\r\n \r\n // Skip if route matches skip patterns\r\n if (shouldSkip(pathname)) {\r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests,\r\n resetAt: 0,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n const key = config.keyFn(req);\r\n const now = Date.now();\r\n \r\n let entry = store.get(key);\r\n \r\n // New window or expired\r\n if (!entry || entry.resetAt <= now) {\r\n entry = {\r\n count: 1,\r\n resetAt: now + config.windowMs,\r\n };\r\n store.set(key, entry);\r\n \r\n return {\r\n allowed: true,\r\n remaining: config.maxRequests - 1,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n // Existing window\r\n entry.count++;\r\n \r\n const remaining = Math.max(0, config.maxRequests - entry.count);\r\n const allowed = entry.count <= config.maxRequests;\r\n \r\n return {\r\n allowed,\r\n remaining,\r\n resetAt: entry.resetAt,\r\n limit: config.maxRequests,\r\n };\r\n }\r\n\r\n /**\r\n * Apply rate limit headers to response\r\n */\r\n function applyHeaders(response: NextResponse, result: RateLimitResult): NextResponse {\r\n response.headers.set('X-RateLimit-Limit', result.limit.toString());\r\n response.headers.set('X-RateLimit-Remaining', result.remaining.toString());\r\n response.headers.set('X-RateLimit-Reset', Math.ceil(result.resetAt / 1000).toString());\r\n return response;\r\n }\r\n\r\n /**\r\n * Create rate limited response (429 Too Many Requests)\r\n */\r\n function createLimitedResponse(req: NextRequest, result: RateLimitResult): NextResponse {\r\n // User-provided handler\r\n if (config.onRateLimited) {\r\n const response = config.onRateLimited(req);\r\n return applyHeaders(response, result);\r\n }\r\n\r\n // Default response\r\n const response = NextResponse.json(\r\n {\r\n success: false,\r\n message: 'Too many requests. Please try again later.',\r\n retryAfter: Math.ceil((result.resetAt - Date.now()) / 1000),\r\n },\r\n { status: 429 }\r\n );\r\n\r\n response.headers.set('Retry-After', Math.ceil((result.resetAt - Date.now()) / 1000).toString());\r\n return applyHeaders(response, result);\r\n }\r\n\r\n /**\r\n * Reset rate limit for a key (useful for testing)\r\n */\r\n function reset(key: string): void {\r\n store.delete(key);\r\n }\r\n\r\n /**\r\n * Clear all rate limit entries\r\n */\r\n function clear(): void {\r\n store.clear();\r\n }\r\n\r\n /**\r\n * Get current store size (for monitoring)\r\n */\r\n function size(): number {\r\n return store.size;\r\n }\r\n\r\n return {\r\n check,\r\n applyHeaders,\r\n createLimitedResponse,\r\n shouldSkip,\r\n reset,\r\n clear,\r\n size,\r\n };\r\n}\r\n\r\nexport type RateLimiter = ReturnType<typeof createRateLimiter>;\r\n","/**\r\n * Audit Logging\r\n * \r\n * Event-based security audit logging system.\r\n * Emits events for authentication, access control, and security violations.\r\n */\r\n\r\nimport { NextRequest } from 'next/server';\r\nimport type { ResolvedAuditConfig, AuditEvent, AuditEventType } from '../shared/types';\r\n\r\n/**\r\n * Creates an audit logger instance\r\n */\r\nexport function createAuditLogger(config: ResolvedAuditConfig) {\r\n /**\r\n * Check if event type is enabled\r\n */\r\n function isEnabled(type: AuditEventType): boolean {\r\n return config.enabled && config.events.includes(type);\r\n }\r\n\r\n /**\r\n * Extract IP address from request\r\n */\r\n function getIp(req: NextRequest): string | null {\r\n return (\r\n req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||\r\n req.headers.get('x-real-ip') ||\r\n null\r\n );\r\n }\r\n\r\n /**\r\n * Emit an audit event\r\n */\r\n async function emit(\r\n type: AuditEventType,\r\n req: NextRequest,\r\n options: {\r\n success: boolean;\r\n userId?: string;\r\n metadata?: Record<string, unknown>;\r\n }\r\n ): Promise<void> {\r\n if (!isEnabled(type)) {\r\n return;\r\n }\r\n\r\n const event: AuditEvent = {\r\n type,\r\n timestamp: new Date(),\r\n ip: getIp(req),\r\n userId: options.userId,\r\n path: req.nextUrl.pathname,\r\n method: req.method,\r\n success: options.success,\r\n metadata: options.metadata,\r\n };\r\n\r\n // Call user's logger\r\n if (config.logger) {\r\n try {\r\n await config.logger(event);\r\n } catch (error) {\r\n // Silently fail - don't break the request flow for logging errors\r\n console.error('[next-api-layer] Audit logger error:', error);\r\n }\r\n }\r\n }\r\n\r\n // ==================== Convenience Methods ====================\r\n\r\n /**\r\n * Log successful authentication\r\n */\r\n function authSuccess(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:success', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log failed authentication\r\n */\r\n function authFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log token refresh\r\n */\r\n function authRefresh(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('auth:refresh', req, { success: true, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log guest token creation\r\n */\r\n function authGuest(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('auth:guest', req, { success: true, metadata });\r\n }\r\n\r\n /**\r\n * Log access denied\r\n */\r\n function accessDenied(req: NextRequest, userId?: string, metadata?: Record<string, unknown>) {\r\n return emit('access:denied', req, { success: false, userId, metadata });\r\n }\r\n\r\n /**\r\n * Log CSRF validation failure\r\n */\r\n function csrfFail(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('csrf:fail', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log rate limit exceeded\r\n */\r\n function rateLimitExceeded(req: NextRequest, metadata?: Record<string, unknown>) {\r\n return emit('rateLimit:exceeded', req, { success: false, metadata });\r\n }\r\n\r\n /**\r\n * Log general error\r\n */\r\n function error(req: NextRequest, err: Error, metadata?: Record<string, unknown>) {\r\n return emit('error', req, { \r\n success: false, \r\n metadata: { \r\n ...metadata, \r\n error: err.message,\r\n stack: err.stack,\r\n } \r\n });\r\n }\r\n\r\n return {\r\n emit,\r\n isEnabled,\r\n // Convenience methods\r\n authSuccess,\r\n authFail,\r\n authRefresh,\r\n authGuest,\r\n accessDenied,\r\n csrfFail,\r\n rateLimitExceeded,\r\n error,\r\n };\r\n}\r\n\r\nexport type AuditLogger = ReturnType<typeof createAuditLogger>;\r\n","/**\r\n * createAuthProxy\r\n * Factory function to create Next.js middleware for external JWT authentication\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport type { AuthProxyConfig, AuthResult } from '../shared/types';\r\nimport { resolveProxyConfig } from '../shared/config';\r\nimport { createTokenValidation } from './tokenValidation';\r\nimport { createHandlers } from './handlers';\r\nimport { createCsrfValidator } from './csrf';\r\nimport { createRateLimiter } from './rateLimit';\r\nimport { createAuditLogger } from './audit';\r\n\r\n/**\r\n * Creates an authentication proxy middleware for Next.js\r\n * \r\n * @example\r\n * ```ts\r\n * // middleware.ts\r\n * import { createAuthProxy } from 'next-api-layer';\r\n * \r\n * const authProxy = createAuthProxy({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * cookies: {\r\n * user: 'userAuthToken',\r\n * guest: 'guestAuthToken',\r\n * },\r\n * guestToken: {\r\n * enabled: true,\r\n * credentials: {\r\n * username: process.env.GUEST_USERNAME!,\r\n * password: process.env.GUEST_PASSWORD!,\r\n * },\r\n * },\r\n * access: {\r\n * protectedRoutes: ['/dashboard', '/profile'],\r\n * authRoutes: ['/login', '/register'],\r\n * },\r\n * // Security features\r\n * csrf: { enabled: true },\r\n * rateLimit: { enabled: true, maxRequests: 100 },\r\n * audit: { \r\n * enabled: true, \r\n * logger: (event) => console.log('[AUDIT]', event) \r\n * },\r\n * });\r\n * \r\n * export default authProxy;\r\n * \r\n * export const config = {\r\n * matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],\r\n * };\r\n * ```\r\n */\r\nexport function createAuthProxy(userConfig: AuthProxyConfig) {\r\n // Resolve config with defaults\r\n const config = resolveProxyConfig(userConfig);\r\n \r\n // Create validation functions\r\n const validation = createTokenValidation(config);\r\n \r\n // Create handlers\r\n const handlers = createHandlers(config, validation);\r\n\r\n // Create security modules\r\n const csrf = createCsrfValidator(config._resolved.csrf);\r\n const rateLimiter = createRateLimiter(config._resolved.rateLimit);\r\n const audit = createAuditLogger(config._resolved.audit);\r\n\r\n /**\r\n * The middleware function\r\n */\r\n async function authProxy(req: NextRequest): Promise<NextResponse> {\r\n const { pathname, origin } = req.nextUrl;\r\n const isApiRoute = pathname.startsWith('/api');\r\n\r\n // ============ Rate Limiting ============\r\n // Check early to protect against DoS\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n \r\n if (!rateLimitResult.allowed) {\r\n await audit.rateLimitExceeded(req, { \r\n limit: rateLimitResult.limit,\r\n resetAt: rateLimitResult.resetAt,\r\n });\r\n return rateLimiter.createLimitedResponse(req, rateLimitResult);\r\n }\r\n }\r\n\r\n // ============ CSRF Protection ============\r\n // Check before any state-changing operations\r\n if (config._resolved.csrf.enabled) {\r\n const csrfResult = csrf.validateRequest(req);\r\n \r\n if (!csrfResult.valid) {\r\n await audit.csrfFail(req, { reason: csrfResult.reason });\r\n return NextResponse.json(\r\n { success: false, message: 'CSRF validation failed' },\r\n { status: 403 }\r\n );\r\n }\r\n }\r\n\r\n // ============ Block Browser API Access ============\r\n // Prevents direct browser access to API routes (when Accept: text/html)\r\n if (config.blockBrowserApiAccess && isApiRoute) {\r\n const acceptHeader = req.headers.get('accept') || '';\r\n if (acceptHeader.includes('text/html')) {\r\n return NextResponse.redirect(new URL('/', origin));\r\n }\r\n }\r\n\r\n // ============ beforeAuth Hook ============\r\n // Allows user to handle request before auth validation\r\n if (config.beforeAuth) {\r\n const beforeResult = await config.beforeAuth(req);\r\n if (beforeResult) {\r\n return beforeResult; // User handled the request\r\n }\r\n }\r\n\r\n // Skip excluded paths\r\n const excludedPaths = config.excludedPaths ?? [];\r\n if (excludedPaths.some(path => pathname.startsWith(path))) {\r\n return applyAfterAuth(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Skip auth API endpoints (they handle their own auth)\r\n const authApiPaths = [\r\n '/api/auth/login',\r\n '/api/auth/logout',\r\n '/api/auth/me',\r\n '/api/auth/refresh',\r\n '/api/auth/register',\r\n ];\r\n \r\n if (authApiPaths.includes(pathname)) {\r\n return applyAfterAuth(req, NextResponse.next(), { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Get tokens from cookies\r\n const userToken = req.cookies?.get(config.cookies.user)?.value;\r\n const guestToken = req.cookies?.get(config.cookies.guest)?.value;\r\n const currentToken = userToken || guestToken;\r\n const isUserToken = !!userToken;\r\n\r\n // No token - handle appropriately\r\n if (!currentToken) {\r\n await audit.authFail(req, { reason: 'no-token' });\r\n const response = await handlers.handleNoToken(req, isApiRoute);\r\n return applyAfterAuth(req, response, { isAuthenticated: false, isGuest: false, tokenType: null, user: null });\r\n }\r\n\r\n // Validate token\r\n const tokenInfo = await validation.getTokenInfo(currentToken);\r\n \r\n // Build auth result for afterAuth hook\r\n const authResult: AuthResult = {\r\n isAuthenticated: tokenInfo.isValid && tokenInfo.tokenType !== 'guest',\r\n isGuest: tokenInfo.isValid && tokenInfo.tokenType === 'guest',\r\n tokenType: tokenInfo.tokenType,\r\n user: tokenInfo.userData,\r\n };\r\n\r\n // Audit logging based on validation result \r\n if (tokenInfo.isValid) {\r\n if (tokenInfo.tokenType === 'guest') {\r\n await audit.authGuest(req);\r\n } else {\r\n const userId = tokenInfo.userData?.id?.toString();\r\n await audit.authSuccess(req, userId, { tokenType: tokenInfo.tokenType });\r\n }\r\n } else {\r\n await audit.authFail(req, { reason: 'invalid-token' });\r\n }\r\n\r\n // Handle validation result\r\n const response = await handlers.handleValidationResult(\r\n req,\r\n tokenInfo,\r\n isUserToken,\r\n currentToken,\r\n isApiRoute\r\n );\r\n \r\n // Apply CSRF cookie if enabled (for authenticated requests)\r\n let finalResponse = await applyAfterAuth(req, response, authResult);\r\n \r\n if (config._resolved.csrf.enabled && authResult.isAuthenticated) {\r\n const sessionId = tokenInfo.userData?.id?.toString() || currentToken.slice(0, 32);\r\n finalResponse = await csrf.attachCsrfCookie(finalResponse, sessionId);\r\n }\r\n\r\n // Apply rate limit headers\r\n if (config._resolved.rateLimit.enabled) {\r\n const rateLimitResult = rateLimiter.check(req);\r\n finalResponse = rateLimiter.applyHeaders(finalResponse, rateLimitResult);\r\n }\r\n \r\n return finalResponse;\r\n }\r\n \r\n /**\r\n * Helper to apply afterAuth hook\r\n */\r\n async function applyAfterAuth(req: NextRequest, response: NextResponse, authResult: AuthResult): Promise<NextResponse> {\r\n if (config.afterAuth) {\r\n return config.afterAuth(req, response, authResult);\r\n }\r\n return response;\r\n }\r\n\r\n // Attach instances for debugging/testing\r\n authProxy.config = config;\r\n authProxy.csrf = csrf;\r\n authProxy.rateLimiter = rateLimiter;\r\n authProxy.audit = audit;\r\n\r\n return authProxy;\r\n}\r\n\r\nexport type AuthProxy = ReturnType<typeof createAuthProxy>;\r\n","/**\r\n * createProxyHandler\r\n * Factory function to create Next.js API route handlers that proxy requests to backend\r\n */\r\n\r\nimport { NextRequest, NextResponse } from 'next/server';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\nexport interface ProxyHandlerConfig {\r\n /** Base URL of the backend API */\r\n apiBaseUrl: string;\r\n /** Cookie name for user auth token */\r\n userCookieName?: string;\r\n /** Cookie name for guest auth token */\r\n guestCookieName?: string;\r\n /** \r\n * Default behavior for auth - if true, all requests skip auth by default\r\n * Individual requests can override with X-Skip-Auth header\r\n */\r\n skipAuthByDefault?: boolean;\r\n /**\r\n * Public endpoints that should never include auth token (glob patterns)\r\n * e.g., ['news/*', 'public/**', 'categories']\r\n */\r\n publicEndpoints?: string[];\r\n /** Headers to forward from client request */\r\n forwardHeaders?: string[];\r\n /** Headers to exclude from forwarding */\r\n excludeHeaders?: string[];\r\n /** Custom request transformer */\r\n transformRequest?: (req: NextRequest, headers: Headers) => Headers | Promise<Headers>;\r\n /** Custom response transformer */\r\n transformResponse?: (response: Response) => Response | Promise<Response>;\r\n}\r\n\r\n/**\r\n * Check if endpoint matches any patterns (glob support)\r\n */\r\nfunction matchesPattern(endpoint: string, patterns: string[]): boolean {\r\n if (!patterns || patterns.length === 0) return false;\r\n \r\n // Normalize endpoint\r\n const normalizedEndpoint = endpoint\r\n .replace(/^\\/api\\//, '')\r\n .replace(/^\\//, '');\r\n \r\n return patterns.some(pattern => {\r\n if (pattern === normalizedEndpoint) return true;\r\n \r\n if (pattern.includes('*')) {\r\n const regexPattern = pattern\r\n .replace(/\\*\\*/g, '<<<DOUBLE>>>')\r\n .replace(/\\*/g, '[^/]+')\r\n .replace(/<<<DOUBLE>>>/g, '.+');\r\n const regex = new RegExp(`^${regexPattern}$`);\r\n return regex.test(normalizedEndpoint);\r\n }\r\n \r\n return false;\r\n });\r\n}\r\n\r\n/**\r\n * Creates a proxy handler for Next.js API routes\r\n * \r\n * @example\r\n * ```ts\r\n * // app/api/[...path]/route.ts\r\n * import { createProxyHandler } from 'next-api-layer';\r\n * \r\n * const handler = createProxyHandler({\r\n * apiBaseUrl: process.env.API_BASE_URL!,\r\n * userCookieName: 'auth_token',\r\n * guestCookieName: 'guest_token',\r\n * publicEndpoints: ['news/*', 'categories', 'public/**'],\r\n * });\r\n * \r\n * export const GET = handler;\r\n * export const POST = handler;\r\n * export const PUT = handler;\r\n * export const PATCH = handler;\r\n * export const DELETE = handler;\r\n * ```\r\n */\r\nexport function createProxyHandler(config: ProxyHandlerConfig) {\r\n const {\r\n apiBaseUrl,\r\n userCookieName = 'auth_token',\r\n guestCookieName = 'guest_token',\r\n skipAuthByDefault = false,\r\n publicEndpoints = [],\r\n forwardHeaders = ['content-type', 'accept', 'accept-language', 'x-requested-with'],\r\n excludeHeaders = ['host', 'connection', 'cookie'],\r\n transformRequest,\r\n transformResponse,\r\n } = config;\r\n\r\n // Normalize base URL (remove trailing slash)\r\n const baseUrl = apiBaseUrl.replace(/\\/$/, '');\r\n\r\n /**\r\n * Determine if auth should be skipped for this request\r\n */\r\n function shouldSkipAuth(req: NextRequest, endpoint: string): boolean {\r\n // Check X-Skip-Auth header from client\r\n const skipAuthHeader = req.headers.get(HEADERS.SKIP_AUTH);\r\n if (skipAuthHeader === 'true') {\r\n return true;\r\n }\r\n \r\n // Check if endpoint matches public patterns\r\n if (matchesPattern(endpoint, publicEndpoints)) {\r\n return true;\r\n }\r\n \r\n // Default behavior\r\n return skipAuthByDefault;\r\n }\r\n\r\n /**\r\n * The proxy handler function\r\n */\r\n async function handler(req: NextRequest): Promise<NextResponse> {\r\n try {\r\n // Extract endpoint from URL path (remove /api/ prefix)\r\n const url = new URL(req.url);\r\n const endpoint = url.pathname.replace(/^\\/api\\/?/, '');\r\n \r\n // Build backend URL\r\n const backendUrl = new URL(`${baseUrl}/${endpoint}`);\r\n backendUrl.search = url.search; // Forward query params\r\n\r\n // Build headers for backend request\r\n const headers = new Headers();\r\n \r\n // Forward allowed headers from original request\r\n forwardHeaders.forEach(headerName => {\r\n const value = req.headers.get(headerName);\r\n if (value) {\r\n headers.set(headerName, value);\r\n }\r\n });\r\n\r\n // Forward all headers except excluded ones\r\n req.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n if (!excludeHeaders.includes(lowerKey) && !headers.has(key)) {\r\n headers.set(key, value);\r\n }\r\n });\r\n\r\n // Add Authorization header if auth is not skipped\r\n if (!shouldSkipAuth(req, endpoint)) {\r\n const userToken = req.cookies.get(userCookieName)?.value;\r\n const guestToken = req.cookies.get(guestCookieName)?.value;\r\n const token = userToken || guestToken;\r\n \r\n if (token) {\r\n headers.set(HEADERS.AUTHORIZATION, `Bearer ${token}`);\r\n }\r\n }\r\n\r\n // Remove skip-auth header (internal use only)\r\n headers.delete(HEADERS.SKIP_AUTH);\r\n\r\n // Allow custom request transformation\r\n const finalHeaders = transformRequest \r\n ? await transformRequest(req, headers) \r\n : headers;\r\n\r\n // Get request body if present\r\n let body: BodyInit | null = null;\r\n if (req.method !== 'GET' && req.method !== 'HEAD') {\r\n const contentType = req.headers.get('content-type') || '';\r\n \r\n if (contentType.includes('application/json')) {\r\n body = await req.text();\r\n } else if (contentType.includes('multipart/form-data')) {\r\n body = await req.formData();\r\n } else {\r\n body = await req.text();\r\n }\r\n }\r\n\r\n // Make request to backend\r\n const backendResponse = await fetch(backendUrl.toString(), {\r\n method: req.method,\r\n headers: finalHeaders,\r\n body,\r\n });\r\n\r\n // Get response body\r\n const contentType = backendResponse.headers.get('content-type') || '';\r\n let responseBody: ArrayBuffer | string;\r\n \r\n if (contentType.includes('application/json')) {\r\n responseBody = await backendResponse.text();\r\n } else {\r\n responseBody = await backendResponse.arrayBuffer();\r\n }\r\n\r\n // Build response headers (forward relevant ones)\r\n const responseHeaders = new Headers();\r\n backendResponse.headers.forEach((value, key) => {\r\n const lowerKey = key.toLowerCase();\r\n // Skip hop-by-hop headers\r\n if (!['transfer-encoding', 'connection', 'keep-alive'].includes(lowerKey)) {\r\n responseHeaders.set(key, value);\r\n }\r\n });\r\n\r\n // Create response\r\n let response = new NextResponse(responseBody, {\r\n status: backendResponse.status,\r\n statusText: backendResponse.statusText,\r\n headers: responseHeaders,\r\n });\r\n\r\n // Allow custom response transformation\r\n if (transformResponse) {\r\n response = await transformResponse(response) as NextResponse;\r\n }\r\n\r\n return response;\r\n } catch (error) {\r\n console.error('[Proxy Error]', error);\r\n \r\n return NextResponse.json(\r\n { \r\n success: false, \r\n message: 'Proxy error: Unable to connect to backend',\r\n error: error instanceof Error ? error.message : 'Unknown error',\r\n },\r\n { status: 502 }\r\n );\r\n }\r\n }\r\n\r\n // Attach config for debugging\r\n handler.config = config;\r\n\r\n return handler;\r\n}\r\n\r\nexport type ProxyHandler = ReturnType<typeof createProxyHandler>;\r\n"]}
|
package/dist/server.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var chunk6ENVQMWQ_cjs=require('./chunk-6ENVQMWQ.cjs'),headers=require('next/headers');function A(t){if(!t||typeof t!="object")return false;let e=t;return e.token_type==="guest"||e.type==="guest"}function w(t){if(!t||typeof t!="object")return null;let e=t;return e.success&&e.data?e.data:e.user?e.user:"id"in e||"email"in e||"type"in e?e:null}async function G(t={}){let{userCookie:e="userAuthToken",guestCookie:n="guestAuthToken",apiBaseUrl:p=process.env.API_BASE_URL||process.env.NEXT_PUBLIC_API_URL,validateEndpoint:T="auth/me",skipHeader:U=false,isGuestFn:c=A,parseResponse:d=w}=t;if(!U)try{let u=(await headers.headers()).get(chunk6ENVQMWQ_cjs.e.AUTH_USER);if(u){let r=JSON.parse(u),o=c(r),k=await headers.cookies(),v=k.get(e)?.value||k.get(n)?.value||null;return {user:r,isAuthenticated:!o,isGuest:o,token:v}}}catch{}let f=await headers.cookies(),g=f.get(e)?.value,h=f.get(n)?.value,s=g||h;if(!s)return {user:null,isAuthenticated:false,isGuest:false,token:null};try{let l=await fetch(`${p}/${T}`,{method:"GET",headers:{[chunk6ENVQMWQ_cjs.e.AUTHORIZATION]:`Bearer ${s}`,[chunk6ENVQMWQ_cjs.e.CONTENT_TYPE]:"application/json"},cache:"no-store"});if(!l.ok)return {user:null,isAuthenticated:!1,isGuest:!1,token:s};let u=await l.json(),r=d(u);if(r){let o=c(r);return {user:r,isAuthenticated:!o,isGuest:o,token:s}}return {user:null,isAuthenticated:!1,isGuest:!1,token:s}}catch{return {user:null,isAuthenticated:false,isGuest:false,token:s}}}async function R(t="userAuthToken"){return !!(await headers.cookies()).get(t)?.value}async function m(t="userAuthToken",e="guestAuthToken"){let n=await headers.cookies();return n.get(t)?.value||n.get(e)?.value||null}exports.getServerToken=m;exports.getServerUser=G;exports.isAuthenticatedServer=R;//# sourceMappingURL=server.cjs.map
|
|
2
|
+
//# sourceMappingURL=server.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/getServerUser.ts"],"names":["defaultIsGuest","user","u","defaultParseResponse","response","res","getServerUser","options","userCookie","guestCookie","apiBaseUrl","validateEndpoint","skipHeader","isGuestFn","parseResponse","userHeader","headers","HEADERS","userData","isGuest","cookieStore","cookies","token","userToken","guestToken","data","isAuthenticatedServer","getServerToken"],"mappings":"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","file":"server.cjs","sourcesContent":["/**\r\n * Server-side utilities\r\n * For use in Server Components and API routes\r\n */\r\n\r\nimport { cookies, headers } from 'next/headers';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n// ==================== Types ====================\r\n\r\nexport interface GetServerUserOptions<TUser = unknown> {\r\n /** Name of the user token cookie */\r\n userCookie?: string;\r\n /** Name of the guest token cookie */\r\n guestCookie?: string;\r\n /** Base URL for API requests (only used if header not found) */\r\n apiBaseUrl?: string;\r\n /** Endpoint to validate token (only used if header not found) */\r\n validateEndpoint?: string;\r\n /** Skip header check and always fetch from backend */\r\n skipHeader?: boolean;\r\n /**\r\n * Function to check if user is a guest\r\n * @default checks token_type === 'guest' or type === 'guest'\r\n */\r\n isGuestFn?: (user: TUser) => boolean;\r\n /**\r\n * Function to parse API response and extract user data\r\n * @default extracts from response.data or response.user\r\n */\r\n parseResponse?: (response: unknown) => TUser | null;\r\n}\r\n\r\nexport interface ServerUserResult<TUser = unknown> {\r\n user: TUser | null;\r\n isAuthenticated: boolean;\r\n isGuest: boolean;\r\n token: string | null;\r\n}\r\n\r\n// ==================== Default Functions ====================\r\n\r\n/** Default guest check */\r\nfunction defaultIsGuest<TUser>(user: TUser): boolean {\r\n if (!user || typeof user !== 'object') return false;\r\n const u = user as Record<string, unknown>;\r\n return u.token_type === 'guest' || u.type === 'guest';\r\n}\r\n\r\n/** Default response parser */\r\nfunction defaultParseResponse<TUser>(response: unknown): TUser | null {\r\n if (!response || typeof response !== 'object') return null;\r\n const res = response as Record<string, unknown>;\r\n \r\n // { success: true, data: user }\r\n if (res.success && res.data) return res.data as TUser;\r\n // { user: {...} }\r\n if (res.user) return res.user as TUser;\r\n // Direct user object\r\n if ('id' in res || 'email' in res || 'type' in res) return res as TUser;\r\n \r\n return null;\r\n}\r\n\r\n// ==================== Main Function ====================\r\n\r\n/**\r\n * Get user data in Server Components\r\n * \r\n * First checks x-auth-user header (set by proxy) - NO backend call needed!\r\n * Falls back to backend validation only if header not found.\r\n * \r\n * @typeParam TUser - User data type\r\n * \r\n * @example\r\n * ```tsx\r\n * // app/layout.tsx - with SSR\r\n * import { getServerUser } from 'next-api-layer/server';\r\n * import { AuthProvider } from 'next-api-layer/client';\r\n * \r\n * interface MyUser {\r\n * type: 'guest' | 'admin';\r\n * user?: { name: string; };\r\n * }\r\n * \r\n * export default async function RootLayout({ children }) {\r\n * const { user } = await getServerUser<MyUser>({\r\n * userCookie: 'myUserToken',\r\n * isGuestFn: (u) => u.type === 'guest',\r\n * });\r\n * \r\n * return (\r\n * <AuthProvider<MyUser> initialUser={user}>\r\n * {children}\r\n * </AuthProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport async function getServerUser<TUser = unknown>(\r\n options: GetServerUserOptions<TUser> = {}\r\n): Promise<ServerUserResult<TUser>> {\r\n const {\r\n userCookie = 'userAuthToken',\r\n guestCookie = 'guestAuthToken',\r\n apiBaseUrl = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_URL,\r\n validateEndpoint = 'auth/me',\r\n skipHeader = false,\r\n isGuestFn = defaultIsGuest,\r\n parseResponse = defaultParseResponse,\r\n } = options;\r\n\r\n // FAST PATH: Check x-auth-user header first (set by proxy)\r\n // This avoids extra backend calls - proxy already validated the token!\r\n if (!skipHeader) {\r\n try {\r\n const headersList = await headers();\r\n const userHeader = headersList.get(HEADERS.AUTH_USER);\r\n \r\n if (userHeader) {\r\n const userData = JSON.parse(userHeader) as TUser;\r\n const isGuest = isGuestFn(userData);\r\n \r\n // Get token from cookies for completeness\r\n const cookieStore = await cookies();\r\n const token = cookieStore.get(userCookie)?.value || cookieStore.get(guestCookie)?.value || null;\r\n \r\n return {\r\n user: userData,\r\n isAuthenticated: !isGuest,\r\n isGuest,\r\n token,\r\n };\r\n }\r\n } catch {\r\n // Header parse failed, continue to fallback\r\n }\r\n }\r\n\r\n // FALLBACK: Validate with backend (only if header not available)\r\n const cookieStore = await cookies();\r\n const userToken = cookieStore.get(userCookie)?.value;\r\n const guestToken = cookieStore.get(guestCookie)?.value;\r\n const token = userToken || guestToken;\r\n\r\n // No token - not authenticated\r\n if (!token) {\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token: null,\r\n };\r\n }\r\n\r\n // Validate token with backend\r\n try {\r\n const response = await fetch(`${apiBaseUrl}/${validateEndpoint}`, {\r\n method: 'GET',\r\n headers: {\r\n [HEADERS.AUTHORIZATION]: `Bearer ${token}`,\r\n [HEADERS.CONTENT_TYPE]: 'application/json',\r\n },\r\n cache: 'no-store', // Don't cache auth requests\r\n });\r\n\r\n if (!response.ok) {\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n }\r\n\r\n const data = await response.json();\r\n const user = parseResponse(data);\r\n\r\n if (user) {\r\n const isGuest = isGuestFn(user);\r\n\r\n return {\r\n user,\r\n isAuthenticated: !isGuest,\r\n isGuest,\r\n token,\r\n };\r\n }\r\n\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n } catch {\r\n // Network error - return unauthenticated\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Check if user is authenticated in Server Components\r\n * Lighter weight than getServerUser - doesn't validate with backend\r\n */\r\nexport async function isAuthenticatedServer(\r\n userCookie = 'userAuthToken'\r\n): Promise<boolean> {\r\n const cookieStore = await cookies();\r\n return !!cookieStore.get(userCookie)?.value;\r\n}\r\n\r\n/**\r\n * Get the auth token in Server Components\r\n */\r\nexport async function getServerToken(\r\n userCookie = 'userAuthToken',\r\n guestCookie = 'guestAuthToken'\r\n): Promise<string | null> {\r\n const cookieStore = await cookies();\r\n return cookieStore.get(userCookie)?.value || cookieStore.get(guestCookie)?.value || null;\r\n}\r\n"]}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side utilities
|
|
3
|
+
* For use in Server Components and API routes
|
|
4
|
+
*/
|
|
5
|
+
interface GetServerUserOptions<TUser = unknown> {
|
|
6
|
+
/** Name of the user token cookie */
|
|
7
|
+
userCookie?: string;
|
|
8
|
+
/** Name of the guest token cookie */
|
|
9
|
+
guestCookie?: string;
|
|
10
|
+
/** Base URL for API requests (only used if header not found) */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** Endpoint to validate token (only used if header not found) */
|
|
13
|
+
validateEndpoint?: string;
|
|
14
|
+
/** Skip header check and always fetch from backend */
|
|
15
|
+
skipHeader?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Function to check if user is a guest
|
|
18
|
+
* @default checks token_type === 'guest' or type === 'guest'
|
|
19
|
+
*/
|
|
20
|
+
isGuestFn?: (user: TUser) => boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Function to parse API response and extract user data
|
|
23
|
+
* @default extracts from response.data or response.user
|
|
24
|
+
*/
|
|
25
|
+
parseResponse?: (response: unknown) => TUser | null;
|
|
26
|
+
}
|
|
27
|
+
interface ServerUserResult<TUser = unknown> {
|
|
28
|
+
user: TUser | null;
|
|
29
|
+
isAuthenticated: boolean;
|
|
30
|
+
isGuest: boolean;
|
|
31
|
+
token: string | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get user data in Server Components
|
|
35
|
+
*
|
|
36
|
+
* First checks x-auth-user header (set by proxy) - NO backend call needed!
|
|
37
|
+
* Falls back to backend validation only if header not found.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam TUser - User data type
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* // app/layout.tsx - with SSR
|
|
44
|
+
* import { getServerUser } from 'next-api-layer/server';
|
|
45
|
+
* import { AuthProvider } from 'next-api-layer/client';
|
|
46
|
+
*
|
|
47
|
+
* interface MyUser {
|
|
48
|
+
* type: 'guest' | 'admin';
|
|
49
|
+
* user?: { name: string; };
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* export default async function RootLayout({ children }) {
|
|
53
|
+
* const { user } = await getServerUser<MyUser>({
|
|
54
|
+
* userCookie: 'myUserToken',
|
|
55
|
+
* isGuestFn: (u) => u.type === 'guest',
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <AuthProvider<MyUser> initialUser={user}>
|
|
60
|
+
* {children}
|
|
61
|
+
* </AuthProvider>
|
|
62
|
+
* );
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare function getServerUser<TUser = unknown>(options?: GetServerUserOptions<TUser>): Promise<ServerUserResult<TUser>>;
|
|
67
|
+
/**
|
|
68
|
+
* Check if user is authenticated in Server Components
|
|
69
|
+
* Lighter weight than getServerUser - doesn't validate with backend
|
|
70
|
+
*/
|
|
71
|
+
declare function isAuthenticatedServer(userCookie?: string): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Get the auth token in Server Components
|
|
74
|
+
*/
|
|
75
|
+
declare function getServerToken(userCookie?: string, guestCookie?: string): Promise<string | null>;
|
|
76
|
+
|
|
77
|
+
export { type GetServerUserOptions, type ServerUserResult, getServerToken, getServerUser, isAuthenticatedServer };
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side utilities
|
|
3
|
+
* For use in Server Components and API routes
|
|
4
|
+
*/
|
|
5
|
+
interface GetServerUserOptions<TUser = unknown> {
|
|
6
|
+
/** Name of the user token cookie */
|
|
7
|
+
userCookie?: string;
|
|
8
|
+
/** Name of the guest token cookie */
|
|
9
|
+
guestCookie?: string;
|
|
10
|
+
/** Base URL for API requests (only used if header not found) */
|
|
11
|
+
apiBaseUrl?: string;
|
|
12
|
+
/** Endpoint to validate token (only used if header not found) */
|
|
13
|
+
validateEndpoint?: string;
|
|
14
|
+
/** Skip header check and always fetch from backend */
|
|
15
|
+
skipHeader?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Function to check if user is a guest
|
|
18
|
+
* @default checks token_type === 'guest' or type === 'guest'
|
|
19
|
+
*/
|
|
20
|
+
isGuestFn?: (user: TUser) => boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Function to parse API response and extract user data
|
|
23
|
+
* @default extracts from response.data or response.user
|
|
24
|
+
*/
|
|
25
|
+
parseResponse?: (response: unknown) => TUser | null;
|
|
26
|
+
}
|
|
27
|
+
interface ServerUserResult<TUser = unknown> {
|
|
28
|
+
user: TUser | null;
|
|
29
|
+
isAuthenticated: boolean;
|
|
30
|
+
isGuest: boolean;
|
|
31
|
+
token: string | null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get user data in Server Components
|
|
35
|
+
*
|
|
36
|
+
* First checks x-auth-user header (set by proxy) - NO backend call needed!
|
|
37
|
+
* Falls back to backend validation only if header not found.
|
|
38
|
+
*
|
|
39
|
+
* @typeParam TUser - User data type
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* // app/layout.tsx - with SSR
|
|
44
|
+
* import { getServerUser } from 'next-api-layer/server';
|
|
45
|
+
* import { AuthProvider } from 'next-api-layer/client';
|
|
46
|
+
*
|
|
47
|
+
* interface MyUser {
|
|
48
|
+
* type: 'guest' | 'admin';
|
|
49
|
+
* user?: { name: string; };
|
|
50
|
+
* }
|
|
51
|
+
*
|
|
52
|
+
* export default async function RootLayout({ children }) {
|
|
53
|
+
* const { user } = await getServerUser<MyUser>({
|
|
54
|
+
* userCookie: 'myUserToken',
|
|
55
|
+
* isGuestFn: (u) => u.type === 'guest',
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* return (
|
|
59
|
+
* <AuthProvider<MyUser> initialUser={user}>
|
|
60
|
+
* {children}
|
|
61
|
+
* </AuthProvider>
|
|
62
|
+
* );
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
declare function getServerUser<TUser = unknown>(options?: GetServerUserOptions<TUser>): Promise<ServerUserResult<TUser>>;
|
|
67
|
+
/**
|
|
68
|
+
* Check if user is authenticated in Server Components
|
|
69
|
+
* Lighter weight than getServerUser - doesn't validate with backend
|
|
70
|
+
*/
|
|
71
|
+
declare function isAuthenticatedServer(userCookie?: string): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Get the auth token in Server Components
|
|
74
|
+
*/
|
|
75
|
+
declare function getServerToken(userCookie?: string, guestCookie?: string): Promise<string | null>;
|
|
76
|
+
|
|
77
|
+
export { type GetServerUserOptions, type ServerUserResult, getServerToken, getServerUser, isAuthenticatedServer };
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {e}from'./chunk-XBAO7FJN.js';import {headers,cookies}from'next/headers';function A(t){if(!t||typeof t!="object")return false;let e=t;return e.token_type==="guest"||e.type==="guest"}function w(t){if(!t||typeof t!="object")return null;let e=t;return e.success&&e.data?e.data:e.user?e.user:"id"in e||"email"in e||"type"in e?e:null}async function G(t={}){let{userCookie:e$1="userAuthToken",guestCookie:n="guestAuthToken",apiBaseUrl:p=process.env.API_BASE_URL||process.env.NEXT_PUBLIC_API_URL,validateEndpoint:T="auth/me",skipHeader:U=false,isGuestFn:c=A,parseResponse:d=w}=t;if(!U)try{let u=(await headers()).get(e.AUTH_USER);if(u){let r=JSON.parse(u),o=c(r),k=await cookies(),v=k.get(e$1)?.value||k.get(n)?.value||null;return {user:r,isAuthenticated:!o,isGuest:o,token:v}}}catch{}let f=await cookies(),g=f.get(e$1)?.value,h=f.get(n)?.value,s=g||h;if(!s)return {user:null,isAuthenticated:false,isGuest:false,token:null};try{let l=await fetch(`${p}/${T}`,{method:"GET",headers:{[e.AUTHORIZATION]:`Bearer ${s}`,[e.CONTENT_TYPE]:"application/json"},cache:"no-store"});if(!l.ok)return {user:null,isAuthenticated:!1,isGuest:!1,token:s};let u=await l.json(),r=d(u);if(r){let o=c(r);return {user:r,isAuthenticated:!o,isGuest:o,token:s}}return {user:null,isAuthenticated:!1,isGuest:!1,token:s}}catch{return {user:null,isAuthenticated:false,isGuest:false,token:s}}}async function R(t="userAuthToken"){return !!(await cookies()).get(t)?.value}async function m(t="userAuthToken",e="guestAuthToken"){let n=await cookies();return n.get(t)?.value||n.get(e)?.value||null}export{m as getServerToken,G as getServerUser,R as isAuthenticatedServer};//# sourceMappingURL=server.js.map
|
|
2
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/getServerUser.ts"],"names":["defaultIsGuest","user","u","defaultParseResponse","response","res","getServerUser","options","userCookie","guestCookie","apiBaseUrl","validateEndpoint","skipHeader","isGuestFn","parseResponse","userHeader","headers","HEADERS","userData","isGuest","cookieStore","cookies","token","userToken","guestToken","data","isAuthenticatedServer","getServerToken"],"mappings":"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","file":"server.js","sourcesContent":["/**\r\n * Server-side utilities\r\n * For use in Server Components and API routes\r\n */\r\n\r\nimport { cookies, headers } from 'next/headers';\r\nimport { HEADERS } from '../shared/constants';\r\n\r\n// ==================== Types ====================\r\n\r\nexport interface GetServerUserOptions<TUser = unknown> {\r\n /** Name of the user token cookie */\r\n userCookie?: string;\r\n /** Name of the guest token cookie */\r\n guestCookie?: string;\r\n /** Base URL for API requests (only used if header not found) */\r\n apiBaseUrl?: string;\r\n /** Endpoint to validate token (only used if header not found) */\r\n validateEndpoint?: string;\r\n /** Skip header check and always fetch from backend */\r\n skipHeader?: boolean;\r\n /**\r\n * Function to check if user is a guest\r\n * @default checks token_type === 'guest' or type === 'guest'\r\n */\r\n isGuestFn?: (user: TUser) => boolean;\r\n /**\r\n * Function to parse API response and extract user data\r\n * @default extracts from response.data or response.user\r\n */\r\n parseResponse?: (response: unknown) => TUser | null;\r\n}\r\n\r\nexport interface ServerUserResult<TUser = unknown> {\r\n user: TUser | null;\r\n isAuthenticated: boolean;\r\n isGuest: boolean;\r\n token: string | null;\r\n}\r\n\r\n// ==================== Default Functions ====================\r\n\r\n/** Default guest check */\r\nfunction defaultIsGuest<TUser>(user: TUser): boolean {\r\n if (!user || typeof user !== 'object') return false;\r\n const u = user as Record<string, unknown>;\r\n return u.token_type === 'guest' || u.type === 'guest';\r\n}\r\n\r\n/** Default response parser */\r\nfunction defaultParseResponse<TUser>(response: unknown): TUser | null {\r\n if (!response || typeof response !== 'object') return null;\r\n const res = response as Record<string, unknown>;\r\n \r\n // { success: true, data: user }\r\n if (res.success && res.data) return res.data as TUser;\r\n // { user: {...} }\r\n if (res.user) return res.user as TUser;\r\n // Direct user object\r\n if ('id' in res || 'email' in res || 'type' in res) return res as TUser;\r\n \r\n return null;\r\n}\r\n\r\n// ==================== Main Function ====================\r\n\r\n/**\r\n * Get user data in Server Components\r\n * \r\n * First checks x-auth-user header (set by proxy) - NO backend call needed!\r\n * Falls back to backend validation only if header not found.\r\n * \r\n * @typeParam TUser - User data type\r\n * \r\n * @example\r\n * ```tsx\r\n * // app/layout.tsx - with SSR\r\n * import { getServerUser } from 'next-api-layer/server';\r\n * import { AuthProvider } from 'next-api-layer/client';\r\n * \r\n * interface MyUser {\r\n * type: 'guest' | 'admin';\r\n * user?: { name: string; };\r\n * }\r\n * \r\n * export default async function RootLayout({ children }) {\r\n * const { user } = await getServerUser<MyUser>({\r\n * userCookie: 'myUserToken',\r\n * isGuestFn: (u) => u.type === 'guest',\r\n * });\r\n * \r\n * return (\r\n * <AuthProvider<MyUser> initialUser={user}>\r\n * {children}\r\n * </AuthProvider>\r\n * );\r\n * }\r\n * ```\r\n */\r\nexport async function getServerUser<TUser = unknown>(\r\n options: GetServerUserOptions<TUser> = {}\r\n): Promise<ServerUserResult<TUser>> {\r\n const {\r\n userCookie = 'userAuthToken',\r\n guestCookie = 'guestAuthToken',\r\n apiBaseUrl = process.env.API_BASE_URL || process.env.NEXT_PUBLIC_API_URL,\r\n validateEndpoint = 'auth/me',\r\n skipHeader = false,\r\n isGuestFn = defaultIsGuest,\r\n parseResponse = defaultParseResponse,\r\n } = options;\r\n\r\n // FAST PATH: Check x-auth-user header first (set by proxy)\r\n // This avoids extra backend calls - proxy already validated the token!\r\n if (!skipHeader) {\r\n try {\r\n const headersList = await headers();\r\n const userHeader = headersList.get(HEADERS.AUTH_USER);\r\n \r\n if (userHeader) {\r\n const userData = JSON.parse(userHeader) as TUser;\r\n const isGuest = isGuestFn(userData);\r\n \r\n // Get token from cookies for completeness\r\n const cookieStore = await cookies();\r\n const token = cookieStore.get(userCookie)?.value || cookieStore.get(guestCookie)?.value || null;\r\n \r\n return {\r\n user: userData,\r\n isAuthenticated: !isGuest,\r\n isGuest,\r\n token,\r\n };\r\n }\r\n } catch {\r\n // Header parse failed, continue to fallback\r\n }\r\n }\r\n\r\n // FALLBACK: Validate with backend (only if header not available)\r\n const cookieStore = await cookies();\r\n const userToken = cookieStore.get(userCookie)?.value;\r\n const guestToken = cookieStore.get(guestCookie)?.value;\r\n const token = userToken || guestToken;\r\n\r\n // No token - not authenticated\r\n if (!token) {\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token: null,\r\n };\r\n }\r\n\r\n // Validate token with backend\r\n try {\r\n const response = await fetch(`${apiBaseUrl}/${validateEndpoint}`, {\r\n method: 'GET',\r\n headers: {\r\n [HEADERS.AUTHORIZATION]: `Bearer ${token}`,\r\n [HEADERS.CONTENT_TYPE]: 'application/json',\r\n },\r\n cache: 'no-store', // Don't cache auth requests\r\n });\r\n\r\n if (!response.ok) {\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n }\r\n\r\n const data = await response.json();\r\n const user = parseResponse(data);\r\n\r\n if (user) {\r\n const isGuest = isGuestFn(user);\r\n\r\n return {\r\n user,\r\n isAuthenticated: !isGuest,\r\n isGuest,\r\n token,\r\n };\r\n }\r\n\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n } catch {\r\n // Network error - return unauthenticated\r\n return {\r\n user: null,\r\n isAuthenticated: false,\r\n isGuest: false,\r\n token,\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * Check if user is authenticated in Server Components\r\n * Lighter weight than getServerUser - doesn't validate with backend\r\n */\r\nexport async function isAuthenticatedServer(\r\n userCookie = 'userAuthToken'\r\n): Promise<boolean> {\r\n const cookieStore = await cookies();\r\n return !!cookieStore.get(userCookie)?.value;\r\n}\r\n\r\n/**\r\n * Get the auth token in Server Components\r\n */\r\nexport async function getServerToken(\r\n userCookie = 'userAuthToken',\r\n guestCookie = 'guestAuthToken'\r\n): Promise<string | null> {\r\n const cookieStore = await cookies();\r\n return cookieStore.get(userCookie)?.value || cookieStore.get(guestCookie)?.value || null;\r\n}\r\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-api-layer",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Production-grade API layer for Next.js with external JWT backend support",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nextjs",
|
|
7
|
+
"auth",
|
|
8
|
+
"jwt",
|
|
9
|
+
"external-backend",
|
|
10
|
+
"laravel",
|
|
11
|
+
"django",
|
|
12
|
+
"api-client",
|
|
13
|
+
"middleware",
|
|
14
|
+
"proxy"
|
|
15
|
+
],
|
|
16
|
+
"author": "dijinar",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"homepage": "https://nextapilayer.com",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/dijinar/next-api-layer"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/dijinar/next-api-layer/issues"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"bin": {
|
|
29
|
+
"next-api-layer": "./dist/cli/init.js"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.cjs",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"import": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"default": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"require": {
|
|
41
|
+
"types": "./dist/index.d.cts",
|
|
42
|
+
"default": "./dist/index.cjs"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"./api": {
|
|
46
|
+
"import": {
|
|
47
|
+
"types": "./dist/api.d.ts",
|
|
48
|
+
"default": "./dist/api.js"
|
|
49
|
+
},
|
|
50
|
+
"require": {
|
|
51
|
+
"types": "./dist/api.d.cts",
|
|
52
|
+
"default": "./dist/api.cjs"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"./client": {
|
|
56
|
+
"import": {
|
|
57
|
+
"types": "./dist/client.d.ts",
|
|
58
|
+
"default": "./dist/client.js"
|
|
59
|
+
},
|
|
60
|
+
"require": {
|
|
61
|
+
"types": "./dist/client.d.cts",
|
|
62
|
+
"default": "./dist/client.cjs"
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"./server": {
|
|
66
|
+
"import": {
|
|
67
|
+
"types": "./dist/server.d.ts",
|
|
68
|
+
"default": "./dist/server.js"
|
|
69
|
+
},
|
|
70
|
+
"require": {
|
|
71
|
+
"types": "./dist/server.d.cts",
|
|
72
|
+
"default": "./dist/server.cjs"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"files": [
|
|
77
|
+
"dist",
|
|
78
|
+
"README.md",
|
|
79
|
+
"LICENSE"
|
|
80
|
+
],
|
|
81
|
+
"scripts": {
|
|
82
|
+
"build": "tsup && tsup src/cli/init.ts --format esm --outDir dist/cli --no-splitting",
|
|
83
|
+
"dev": "tsup --watch",
|
|
84
|
+
"test": "vitest run",
|
|
85
|
+
"test:watch": "vitest",
|
|
86
|
+
"test:coverage": "vitest run --coverage",
|
|
87
|
+
"typecheck": "tsc --noEmit",
|
|
88
|
+
"lint": "eslint src/",
|
|
89
|
+
"prepublishOnly": "npm run build"
|
|
90
|
+
},
|
|
91
|
+
"peerDependencies": {
|
|
92
|
+
"next": ">=14.0.0",
|
|
93
|
+
"next-intl": ">=3.0.0",
|
|
94
|
+
"react": ">=18.0.0",
|
|
95
|
+
"react-dom": ">=18.0.0",
|
|
96
|
+
"swr": ">=2.0.0"
|
|
97
|
+
},
|
|
98
|
+
"peerDependenciesMeta": {
|
|
99
|
+
"next-intl": {
|
|
100
|
+
"optional": true
|
|
101
|
+
},
|
|
102
|
+
"swr": {
|
|
103
|
+
"optional": true
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"devDependencies": {
|
|
107
|
+
"@biomejs/biome": "^2.4.9",
|
|
108
|
+
"@types/node": "^20.0.0",
|
|
109
|
+
"@types/prompts": "^2.4.9",
|
|
110
|
+
"@types/react": "^18.0.0",
|
|
111
|
+
"@vitest/coverage-v8": "^4.1.1",
|
|
112
|
+
"esbuild-visualizer": "^0.7.0",
|
|
113
|
+
"swr": "^2.4.1",
|
|
114
|
+
"tsup": "^8.0.0",
|
|
115
|
+
"typescript": "^5.0.0",
|
|
116
|
+
"vitest": "^4.1.1"
|
|
117
|
+
},
|
|
118
|
+
"engines": {
|
|
119
|
+
"node": ">=18.0.0"
|
|
120
|
+
},
|
|
121
|
+
"dependencies": {
|
|
122
|
+
"prompts": "^2.4.2"
|
|
123
|
+
}
|
|
124
|
+
}
|