@mrxsys/mrx-core 2.3.3-canary-20250514-a17e993
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/CHANGELOG.md +45 -0
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/chunk-0pft81dh.js +2 -0
- package/dist/chunk-6sa6shm6.js +2 -0
- package/dist/chunk-7map0v4a.js +2 -0
- package/dist/chunk-dppgkz92.js +2 -0
- package/dist/chunk-kn1300vm.js +2 -0
- package/dist/chunk-nntk6mwx.js +2 -0
- package/dist/chunk-rr6e20n5.js +2 -0
- package/dist/chunk-s3xqwymw.js +2 -0
- package/dist/chunk-t411e41y.js +2 -0
- package/dist/chunk-wggj590m.js +2 -0
- package/dist/chunk-xcz2fxzt.js +2 -0
- package/dist/chunk-yeyzc9n2.js +2 -0
- package/dist/database/enums/databaseKeyError.d.ts +56 -0
- package/dist/database/enums/index.d.ts +2 -0
- package/dist/database/enums/index.js +2 -0
- package/dist/database/enums/mssqlErrorCode.d.ts +25 -0
- package/dist/database/events/index.d.ts +2 -0
- package/dist/database/events/index.js +2 -0
- package/dist/database/events/mssqlEvent.d.ts +3 -0
- package/dist/database/events/tableEvent.d.ts +6 -0
- package/dist/database/index.d.ts +5 -0
- package/dist/database/index.js +2 -0
- package/dist/database/mssql.d.ts +163 -0
- package/dist/database/table.d.ts +58 -0
- package/dist/database/types/index.d.ts +2 -0
- package/dist/database/types/index.js +1 -0
- package/dist/database/types/mssqlDatabaseOption.d.ts +92 -0
- package/dist/database/types/mssqlEventLog.d.ts +29 -0
- package/dist/elysia/advancedSearch.d.ts +460 -0
- package/dist/elysia/crud.d.ts +609 -0
- package/dist/elysia/dynamicDatabaseSelector.d.ts +352 -0
- package/dist/elysia/enums/elysiaKeyError.d.ts +16 -0
- package/dist/elysia/enums/httpStatusCode.d.ts +241 -0
- package/dist/elysia/enums/index.d.ts +2 -0
- package/dist/elysia/enums/index.js +2 -0
- package/dist/elysia/error.d.ts +50 -0
- package/dist/elysia/index.d.ts +7 -0
- package/dist/elysia/index.js +2 -0
- package/dist/elysia/jwt.d.ts +46 -0
- package/dist/elysia/microservice.d.ts +102 -0
- package/dist/elysia/ratelimit.d.ts +76 -0
- package/dist/elysia/schemas/index.d.ts +2 -0
- package/dist/elysia/schemas/index.js +2 -0
- package/dist/elysia/schemas/info.d.ts +9 -0
- package/dist/elysia/schemas/ping.d.ts +3 -0
- package/dist/elysia/types/crudOptions.d.ts +126 -0
- package/dist/elysia/types/crudRoutes.d.ts +2 -0
- package/dist/elysia/types/dynamicDatabaseSelectorPluginOptions.d.ts +30 -0
- package/dist/elysia/types/index.d.ts +5 -0
- package/dist/elysia/types/index.js +1 -0
- package/dist/elysia/types/jwtOptions.d.ts +92 -0
- package/dist/elysia/types/rateLimitOptions.d.ts +44 -0
- package/dist/error/coreError.d.ts +109 -0
- package/dist/error/index.d.ts +1 -0
- package/dist/error/index.js +2 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +2 -0
- package/dist/mailer/enums/index.d.ts +1 -0
- package/dist/mailer/enums/index.js +2 -0
- package/dist/mailer/enums/mailerKeyError.d.ts +12 -0
- package/dist/mailer/index.d.ts +1 -0
- package/dist/mailer/index.js +2 -0
- package/dist/mailer/smtp.d.ts +62 -0
- package/dist/mailer/types/index.d.ts +3 -0
- package/dist/mailer/types/index.js +1 -0
- package/dist/mailer/types/smtpCredentials.d.ts +10 -0
- package/dist/mailer/types/smtpOptions.d.ts +26 -0
- package/dist/mailer/types/smtpPoolOptions.d.ts +7 -0
- package/dist/repository/index.d.ts +1 -0
- package/dist/repository/index.js +2 -0
- package/dist/repository/repository.d.ts +381 -0
- package/dist/repository/types/advancedSearch.d.ts +47 -0
- package/dist/repository/types/index.d.ts +8 -0
- package/dist/repository/types/index.js +1 -0
- package/dist/repository/types/orderBy.d.ts +21 -0
- package/dist/repository/types/queryOptions.d.ts +33 -0
- package/dist/repository/types/queryOptionsExtendPagination.d.ts +21 -0
- package/dist/repository/types/queryOptionsExtendStream.d.ts +16 -0
- package/dist/repository/types/selectedFields.d.ts +16 -0
- package/dist/repository/types/transaction.d.ts +10 -0
- package/dist/repository/types/whereClause.d.ts +15 -0
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.js +2 -0
- package/dist/store/redis.d.ts +6 -0
- package/dist/utils/enums/index.d.ts +1 -0
- package/dist/utils/enums/index.js +2 -0
- package/dist/utils/enums/utilKeyError.d.ts +8 -0
- package/dist/utils/env.d.ts +10 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils/isIsoDateString.d.ts +1 -0
- package/dist/utils/stream.d.ts +11 -0
- package/dist/utils/types/index.d.ts +1 -0
- package/dist/utils/types/index.js +1 -0
- package/dist/utils/types/streamWithAsyncIterable.d.ts +26 -0
- package/package.json +119 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{A as L,B as c,C as o,D as v,E as n,F as g,G as P,q as y,r as q,s as m,t as x,v as C,w as D,y as j}from"../chunk-kn1300vm.js";import{V as bG}from"../chunk-wggj590m.js";import"../chunk-t411e41y.js";import"../chunk-7map0v4a.js";import{_ as A}from"../chunk-6sa6shm6.js";import"../chunk-dppgkz92.js";import"../chunk-yeyzc9n2.js";import{da as B,ea as d}from"../chunk-nntk6mwx.js";import{fa as pG,ga as dG}from"../chunk-xcz2fxzt.js";import{ha as vG}from"../chunk-rr6e20n5.js";import{filterByKeyExclusion as YY}from"@basalt-lab/basalt-helper/data";var f={};vG(f,{TypeGuardUnknownTypeError:()=>i,IsVoid:()=>SG,IsUnsafe:()=>OG,IsUnknown:()=>fG,IsUnionLiteral:()=>GY,IsUnion:()=>h,IsUndefined:()=>xG,IsUint8Array:()=>NG,IsTuple:()=>wG,IsTransform:()=>KG,IsThis:()=>EG,IsTemplateLiteral:()=>RG,IsSymbol:()=>kG,IsString:()=>PG,IsSchema:()=>J,IsRegExp:()=>qG,IsRef:()=>CG,IsRecursive:()=>eG,IsRecord:()=>TG,IsReadonly:()=>uG,IsProperties:()=>N,IsPromise:()=>BG,IsOptional:()=>tG,IsObject:()=>LG,IsNumber:()=>AG,IsNull:()=>DG,IsNot:()=>MG,IsNever:()=>jG,IsMappedResult:()=>JG,IsMappedKey:()=>QG,IsLiteralValue:()=>WG,IsLiteralString:()=>FG,IsLiteralNumber:()=>UG,IsLiteralBoolean:()=>aG,IsLiteral:()=>E,IsKindOf:()=>$,IsKind:()=>yG,IsIterator:()=>_G,IsIntersect:()=>HG,IsInteger:()=>XG,IsImport:()=>lG,IsFunction:()=>$G,IsDate:()=>VG,IsConstructor:()=>zG,IsComputed:()=>ZG,IsBoolean:()=>YG,IsBigInt:()=>GG,IsAsyncIterator:()=>e,IsArray:()=>a,IsArgument:()=>l,IsAny:()=>t});class i extends c{}var iG=["Argument","Any","Array","AsyncIterator","BigInt","Boolean","Computed","Constructor","Date","Enum","Function","Integer","Intersect","Iterator","Literal","MappedKey","MappedResult","Not","Null","Number","Object","Promise","Record","Ref","RegExp","String","Symbol","TemplateLiteral","This","Tuple","Undefined","Union","Uint8Array","Unknown","Void"];function r(G){try{return new RegExp(G),!0}catch{return!1}}function I(G){if(!j(G))return!1;for(let Y=0;Y<G.length;Y++){let Z=G.charCodeAt(Y);if(Z>=7&&Z<=13||Z===27||Z===127)return!1}return!0}function s(G){return b(G)||J(G)}function R(G){return L(G)||m(G)}function F(G){return L(G)||C(G)}function b(G){return L(G)||x(G)}function _(G){return L(G)||j(G)}function rG(G){return L(G)||j(G)&&I(G)&&r(G)}function sG(G){return L(G)||j(G)&&I(G)}function u(G){return L(G)||J(G)}function uG(G){return D(G)&&G[v]==="Readonly"}function tG(G){return D(G)&&G[n]==="Optional"}function t(G){return $(G,"Any")&&_(G.$id)}function l(G){return $(G,"Argument")&&C(G.index)}function a(G){return $(G,"Array")&&G.type==="array"&&_(G.$id)&&J(G.items)&&F(G.minItems)&&F(G.maxItems)&&b(G.uniqueItems)&&u(G.contains)&&F(G.minContains)&&F(G.maxContains)}function e(G){return $(G,"AsyncIterator")&&G.type==="AsyncIterator"&&_(G.$id)&&J(G.items)}function GG(G){return $(G,"BigInt")&&G.type==="bigint"&&_(G.$id)&&R(G.exclusiveMaximum)&&R(G.exclusiveMinimum)&&R(G.maximum)&&R(G.minimum)&&R(G.multipleOf)}function YG(G){return $(G,"Boolean")&&G.type==="boolean"&&_(G.$id)}function ZG(G){return $(G,"Computed")&&j(G.target)&&q(G.parameters)&&G.parameters.every((Y)=>J(Y))}function zG(G){return $(G,"Constructor")&&G.type==="Constructor"&&_(G.$id)&&q(G.parameters)&&G.parameters.every((Y)=>J(Y))&&J(G.returns)}function VG(G){return $(G,"Date")&&G.type==="Date"&&_(G.$id)&&F(G.exclusiveMaximumTimestamp)&&F(G.exclusiveMinimumTimestamp)&&F(G.maximumTimestamp)&&F(G.minimumTimestamp)&&F(G.multipleOfTimestamp)}function $G(G){return $(G,"Function")&&G.type==="Function"&&_(G.$id)&&q(G.parameters)&&G.parameters.every((Y)=>J(Y))&&J(G.returns)}function lG(G){return $(G,"Import")&&y(G,"$defs")&&D(G.$defs)&&N(G.$defs)&&y(G,"$ref")&&j(G.$ref)&&G.$ref in G.$defs}function XG(G){return $(G,"Integer")&&G.type==="integer"&&_(G.$id)&&F(G.exclusiveMaximum)&&F(G.exclusiveMinimum)&&F(G.maximum)&&F(G.minimum)&&F(G.multipleOf)}function N(G){return D(G)&&Object.entries(G).every(([Y,Z])=>I(Y)&&J(Z))}function HG(G){return $(G,"Intersect")&&(j(G.type)&&G.type!=="object"?!1:!0)&&q(G.allOf)&&G.allOf.every((Y)=>J(Y)&&!KG(Y))&&_(G.type)&&(b(G.unevaluatedProperties)||u(G.unevaluatedProperties))&&_(G.$id)}function _G(G){return $(G,"Iterator")&&G.type==="Iterator"&&_(G.$id)&&J(G.items)}function $(G,Y){return D(G)&&P in G&&G[P]===Y}function FG(G){return E(G)&&j(G.const)}function UG(G){return E(G)&&C(G.const)}function aG(G){return E(G)&&x(G.const)}function E(G){return $(G,"Literal")&&_(G.$id)&&WG(G.const)}function WG(G){return x(G)||C(G)||j(G)}function QG(G){return $(G,"MappedKey")&&q(G.keys)&&G.keys.every((Y)=>C(Y)||j(Y))}function JG(G){return $(G,"MappedResult")&&N(G.properties)}function jG(G){return $(G,"Never")&&D(G.not)&&Object.getOwnPropertyNames(G.not).length===0}function MG(G){return $(G,"Not")&&J(G.not)}function DG(G){return $(G,"Null")&&G.type==="null"&&_(G.$id)}function AG(G){return $(G,"Number")&&G.type==="number"&&_(G.$id)&&F(G.exclusiveMaximum)&&F(G.exclusiveMinimum)&&F(G.maximum)&&F(G.minimum)&&F(G.multipleOf)}function LG(G){return $(G,"Object")&&G.type==="object"&&_(G.$id)&&N(G.properties)&&s(G.additionalProperties)&&F(G.minProperties)&&F(G.maxProperties)}function BG(G){return $(G,"Promise")&&G.type==="Promise"&&_(G.$id)&&J(G.item)}function TG(G){return $(G,"Record")&&G.type==="object"&&_(G.$id)&&s(G.additionalProperties)&&D(G.patternProperties)&&((Y)=>{let Z=Object.getOwnPropertyNames(Y.patternProperties);return Z.length===1&&r(Z[0])&&D(Y.patternProperties)&&J(Y.patternProperties[Z[0]])})(G)}function eG(G){return D(G)&&g in G&&G[g]==="Recursive"}function CG(G){return $(G,"Ref")&&_(G.$id)&&j(G.$ref)}function qG(G){return $(G,"RegExp")&&_(G.$id)&&j(G.source)&&j(G.flags)&&F(G.maxLength)&&F(G.minLength)}function PG(G){return $(G,"String")&&G.type==="string"&&_(G.$id)&&F(G.minLength)&&F(G.maxLength)&&rG(G.pattern)&&sG(G.format)}function kG(G){return $(G,"Symbol")&&G.type==="symbol"&&_(G.$id)}function RG(G){return $(G,"TemplateLiteral")&&G.type==="string"&&j(G.pattern)&&G.pattern[0]==="^"&&G.pattern[G.pattern.length-1]==="$"}function EG(G){return $(G,"This")&&_(G.$id)&&j(G.$ref)}function KG(G){return D(G)&&o in G}function wG(G){return $(G,"Tuple")&&G.type==="array"&&_(G.$id)&&C(G.minItems)&&C(G.maxItems)&&G.minItems===G.maxItems&&(L(G.items)&&L(G.additionalItems)&&G.minItems===0||q(G.items)&&G.items.every((Y)=>J(Y)))}function xG(G){return $(G,"Undefined")&&G.type==="undefined"&&_(G.$id)}function GY(G){return h(G)&&G.anyOf.every((Y)=>FG(Y)||UG(Y))}function h(G){return $(G,"Union")&&_(G.$id)&&D(G)&&q(G.anyOf)&&G.anyOf.every((Y)=>J(Y))}function NG(G){return $(G,"Uint8Array")&&G.type==="Uint8Array"&&_(G.$id)&&F(G.minByteLength)&&F(G.maxByteLength)}function fG(G){return $(G,"Unknown")&&_(G.$id)}function OG(G){return $(G,"Unsafe")}function SG(G){return $(G,"Void")&&G.type==="void"&&_(G.$id)}function yG(G){return D(G)&&P in G&&j(G[P])&&!iG.includes(G[P])}function J(G){return D(G)&&(t(G)||l(G)||a(G)||YG(G)||GG(G)||e(G)||ZG(G)||zG(G)||VG(G)||$G(G)||XG(G)||HG(G)||_G(G)||E(G)||QG(G)||JG(G)||jG(G)||MG(G)||DG(G)||AG(G)||LG(G)||BG(G)||TG(G)||CG(G)||qG(G)||PG(G)||kG(G)||RG(G)||EG(G)||wG(G)||xG(G)||h(G)||NG(G)||fG(G)||OG(G)||SG(G)||yG(G))}import{Elysia as ZY,t as X}from"elysia";var zY=(G)=>X.Object({$eq:G,$neq:G,...!f.IsBoolean(G)?{$lt:G,$lte:G,$gt:G,$gte:G,$in:X.Array(G),$nin:X.Array(G),$between:X.Tuple([G,G]),$nbetween:X.Tuple([G,G]),$like:X.String(),$nlike:X.String()}:{},$isNull:X.Boolean()}),VY=(G)=>{let{properties:Y}=G,Z={};for(let[z,V]of Object.entries(Y)){let H=zY(V);Z[z]=X.Array(X.Union([X.Partial(H),V]))}return Z},$Y=(G)=>X.Array(X.Union([X.Partial(X.Object({selectedFields:X.Union([X.Array(X.KeyOf(G)),X.KeyOf(G)]),value:X.String()})),X.String()])),XY=(G)=>X.Array(X.KeyOf(G)),K=(G)=>X.Composite([X.Object({$q:X.Optional($Y(G)),$selectedFields:X.Optional(XY(G))}),X.Partial(X.Object(VY(G)))]),HY=(G)=>X.Composite([K(G),X.Partial(X.Object({$limit:X.Number(),$offset:X.Number()}))]),gG=(G,Y,Z=[])=>new ZY({name:`advancedSearchPlugin-${G}`,seed:Y}).model({[`advancedSearch${G}Query`]:K(Y),[`advancedSearch${G}QueryWithPagination`]:HY(Y)}).macro({hasAdvancedSearch:{resolve:({query:z})=>{let V=z,H={advancedSearch:[],selectedFields:V.$selectedFields||["*"],pagination:{limit:V.$limit||100,offset:V.$offset||0}};if(z.$q){let M=Array.isArray(z.$q)?z.$q:[z.$q];H.advancedSearch.push(...M.map((W)=>({$q:W})))}V=YY(V,["$selectedFields","$q","$limit","$offset",...Z],!0);for(let[M,W]of Object.entries(V))(Array.isArray(W)?W:[W]).forEach((k,T)=>{if(!H.advancedSearch[T])H.advancedSearch[T]={};H.advancedSearch[T][M]=k});return H}}}).as("scoped");import{SingletonManager as FY}from"@basalt-lab/basalt-helper/util";import{Elysia as p,t as Q}from"elysia";import{SingletonManager as O}from"@basalt-lab/basalt-helper/util";import{Elysia as _Y,t as IG}from"elysia";var hG=(G)=>new _Y({name:"dynamicDatabaseSelectorPlugin"}).model({databaseUsingHeader:IG.Object({[G.headerKey||"database-using"]:IG.String()})}).macro({hasDynamicDatabaseSelector:{async resolve({headers:Y}){let Z=Y[G.headerKey||"database-using"];if(!Z)throw new A({key:B.dynamicDatabaseKeyNotFound,message:"Dynamic Database key not found in the request headers.",httpStatusCode:400});if(!O.has(`database:${Z}`))O.register(`database:${Z}`,bG,{...G.baseDatabaseConfig,databaseName:Z}),await O.get(`database:${Z}`).connect();return{dynamicDB:O.get(`database:${Z}`)}}}}).as("scoped");var UY=(G)=>{let{properties:Y}=G,Z={};for(let z in Y)Z[z]=Q.Union([Y[z],Q.Undefined(),Q.Null(),Q.Literal("")]);return Q.Object({message:Q.String(),content:Q.Array(Q.Partial(Q.Object(Z)))})},WY=(G,Y)=>{let{properties:Z}=G,z={};for(let V in Z)z[V]=Y?.includes(V)?Z[V]:Q.Optional(Z[V]);return Q.Object(z)},QY=(G,Y)=>{let{baseSchema:Z,tableName:z,insertPropertiesSchemaRequired:V}=Y,H=new p().model({[`crud${z}Response200`]:UY(Z)}),M={insert:()=>H.model({[`crud${z}InsertBody`]:WY(Z,V)}),update:()=>H.model({[`crud${z}UpdateBody`]:Q.Partial(Z)}),count:()=>H.model({[`crud${z}CountResponse200`]:Q.Object({message:Q.String(),content:Q.Number()})})};if(G.forEach((U)=>{if(U in M)M[U]?.()}),["findOne","deleteOne","updateOne"].some((U)=>G.includes(U)))H.model({[`crud${z}IdParam`]:Q.Object({id:Q.Union([Q.String(),Q.Number()])})});return H},JY=(G=[],Y=[])=>{let Z=["insert","find","findOne","update","updateOne","delete","deleteOne","count"],z=G.length>0?G:Z;return Y.length>0?z.filter((V)=>!Y.includes(V)):z},jY=(G)=>{let Y=new p;if(typeof G==="string")Y.resolve(()=>({dynamicDB:FY.get(`database:${G}`)}));else Y.use(hG({baseDatabaseConfig:G.baseDatabaseConfig,headerKey:G.headerKey||"database-using"}));return Y.as("scoped")},MY={insertHandler:async(G,Y)=>{let z=G.dynamicDB.getRepository(Y),{body:V}=G,H=await z.insert(V);return{message:`Inserted record for ${Y}`,content:H}},findHandler:async(G,Y)=>{let V=await G.dynamicDB.getRepository(Y).find({advancedSearch:G.advancedSearch,selectedFields:G.selectedFields,limit:G.pagination.limit,offset:G.pagination.offset});return{message:`Found ${V.length} records for ${Y}`,content:V}},findOneHandler:async(G,Y)=>{let Z=G.dynamicDB,z=Z.getRepository(Y),H=Z.getTable(Y).primaryKey,{id:M}=G.params,W=await z.findOne({advancedSearch:{[H[0]]:M}});return{message:`Found record for ${Y}`,content:W}},countHandler:async(G,Y)=>{let V=await G.dynamicDB.getRepository(Y).count({advancedSearch:G.advancedSearch});return{message:`${V} records found for ${Y}`,content:V}},updateHandler:async(G,Y)=>{let z=G.dynamicDB.getRepository(Y);if(!G.advancedSearch||G.advancedSearch.length===0||!G.advancedSearch[0])throw new A({key:B.needAdvancedSearch,message:"You need to provide advanced search to update records. It's dangerous to update all records.",httpStatusCode:400});let V=await z.update(G.body,{advancedSearch:G.advancedSearch,selectedFields:G.selectedField});return{message:`Updated ${V.length} records for ${Y}`,content:V}},updateOneHandler:async(G,Y)=>{let Z=G.dynamicDB,z=Z.getRepository(Y),H=Z.getTable(Y).primaryKey,{id:M}=G.params,W=await z.update(G.body,{advancedSearch:{[H[0]]:M}});return{message:`Updated record for ${Y}`,content:W}},deleteHandler:async(G,Y)=>{let z=G.dynamicDB.getRepository(Y);if(!G.advancedSearch||G.advancedSearch.length===0||!G.advancedSearch[0])throw new A({key:B.needAdvancedSearch,message:"You need to provide advanced search to delete records. It's dangerous to delete all records.",httpStatusCode:400});let V=await z.delete({advancedSearch:G.advancedSearch,selectedFields:G.selectedFields});return{message:`Deleted ${V.length} records for ${Y}`,content:V}},deleteOneHandler:async(G,Y)=>{let Z=G.dynamicDB,z=Z.getRepository(Y),H=Z.getTable(Y).primaryKey,{id:M}=G.params,W=await z.delete({advancedSearch:{[H[0]]:M}});return{message:`Deleted record for ${Y}`,content:W}}},DY=(G,Y,Z,z)=>(V)=>{let H={insert:"post",find:"get",findOne:"get",count:"get",update:"patch",updateOne:"patch",delete:"delete",deleteOne:"delete"},M={insert:"/",find:"/",findOne:"/:id",count:"/count",update:"/",updateOne:"/:id",delete:"/",deleteOne:"/:id"};if(G.includes("find")||G.includes("count")||G.includes("update")||G.includes("delete"))V.use(gG(Y,Z));for(let U of G){let k=H[U],T=M[U];if(k&&T){let w=MY[`${U}Handler`],cG={...U==="findOne"||U==="deleteOne"||U==="updateOne"?{params:`crud${Y}IdParam`}:{},...U==="count"||U==="update"||U==="delete"?{query:K(Z)}:U==="find"?{query:K(Z)}:{},...U==="insert"||U==="update"||U==="updateOne"?{body:`crud${Y}${U==="insert"?"Insert":"Update"}Body`}:{},response:`crud${Y}${U==="count"?"Count":""}Response200`,hasAdvancedSearch:!0,hasDynamicDatabaseSelector:z};V[k](T,(oG)=>w(oG,Y),cG)}}return V},rY=(G)=>{let Y=JY(G.includedRoutes,G.excludedRoutes),Z=new p({name:`crudPlugin[${G.tableName}]`,tags:[G.tableName]}).use(QY(Y,G)).use(G.permissionConfig.permissionsPlugin);return Z.use(jY(G.database)).use(DY(Y,G.tableName,G.baseSchema,typeof G.database!=="string")),Z};import{BasaltError as AY}from"@basalt-lab/basalt-helper/error";import{BasaltError as LY}from"@basalt-lab/basalt-logger/error";import{Elysia as BY}from"elysia";var eY=new BY({name:"errorPlugin"}).error({CoreError:A,BasaltHelperError:AY,BasaltLoggerError:LY}).onError(({code:G,error:Y,set:Z})=>{switch(G){case"CoreError":case"BasaltHelperError":case"BasaltLoggerError":return Z.status=Y.httpStatusCode,{key:Y.key,message:Y.message,cause:Y.cause};case"VALIDATION":return Z.status=400,{key:"core.error.validation",message:"Validation error",cause:{on:Y.type,found:Y.value,errors:Y.all}};case"NOT_FOUND":return Z.status=404,{key:"core.error.not_found",message:"Not found"};case"INTERNAL_SERVER_ERROR":case"UNKNOWN":default:return Z.status=500,{key:"core.error.internal_server_error",message:"Internal server error"}}}).as("global");import{Elysia as TY}from"elysia";import{SignJWT as CY,jwtVerify as qY}from"jose";var $Z=(G)=>{if(!G.secret)throw new A({key:B.jwtSecretNotFound,message:"Secret key is required for JWT signing and verification."});let Y=new TextEncoder().encode(G.secret),Z=G.name??"jwt";return new TY({name:"jwtPlugin",seed:G}).decorate(Z,{sign(z,V=G.exp??"15m"){let H={iss:"core",aud:"core client",jti:Bun.randomUUIDv7(),...G.payload,...z};try{return new CY(H).setProtectedHeader({alg:"HS256",b64:!0}).setIssuer(H.iss??"core").setAudience(H.aud??"client").setExpirationTime(V).sign(Y)}catch{throw new A({key:B.jwtSignError,message:"Error signing JWT."})}},async verify(z){if(!z)return!1;try{return(await qY(z,Y)).payload}catch{return!1}}}).as("scoped")};import{Elysia as PY}from"elysia";import{existsSync as kY}from"fs";import{platform as RY}from"os";var mG=(G)=>{let Y=RY()==="win32",Z=Y?"\\":"/";if(Y&&/^[A-Z]:\\$/i.test(G)||!Y&&G==="/")return"";let z=G+Z+"package.json";if(kY(z))return z;let V=G.split(Z);if(V.length<=1)return"";let H=V.slice(0,-1).join(Z);if(H==="")return"";return mG(H)},S=await import(mG(Bun.main)),QZ=new PY({name:"microservicePlugin",prefix:"/microservice",detail:{tags:["Microservice"],security:[]}}).model({infoResponse200:pG,pingResponse200:dG}).get("/ping",()=>({message:"pong"}),{detail:{summary:"Ping",description:"Ping the microservice to check if it is alive"},response:"pingResponse200"}).get("/info",()=>({message:"Microservice Information",content:{name:S.default.name,version:S.default.version,description:S.default.description,author:S.default.author}}),{detail:{summary:"Info",description:"Get information about the microservice"},response:"infoResponse200"}).as("scoped");import{Elysia as EY}from"elysia";var LZ=({redis:G,limit:Y,window:Z,message:z})=>new EY({name:"rateLimitPlugin",seed:{redis:G,limit:Y,window:Z,message:z}}).onRequest(async({set:V,request:H})=>{let W=`ratelimit:${H.headers.get("x-forwarded-for")||H.headers.get("x-real-ip")||"127.0.0.1"}`,U=await G.client.get(W);if((U?parseInt(U):0)===0)await G.client.setex(W,Z,"1");else await G.client.incr(W);let T=await G.client.get(W),w=T?parseInt(T):0;if(w>Y)throw V.status=d.tooManyRequests,new A({key:B.rateLimitExceeded,message:z||"Rate limit exceeded",httpStatusCode:d.tooManyRequests,cause:{limit:Y,window:Z,remaining:0,reset:await G.client.ttl(W)}});V.headers={"X-RateLimit-Limit":Y.toString(),"X-RateLimit-Remaining":Math.max(0,Y-w).toString(),"X-RateLimit-Reset":(await G.client.ttl(W)).toString()}}).as("scoped");export{LZ as rateLimitPlugin,QZ as microservicePlugin,$Z as jwtPlugin,eY as errorPlugin,hG as dynamicDatabaseSelectorPlugin,rY as crudPlugin,UY as createResponse200Schema,WY as createInsertBodySchema,HY as createBaseSearchSchemaWithPagination,K as createBaseSearchSchema,gG as advancedSearchPlugin};export{K as f,HY as g,gG as h,hG as i,UY as j,WY as k,rY as l,eY as m,$Z as n,QZ as o,LZ as p};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import { type JWTPayload } from 'jose';
|
|
3
|
+
import type { JWTOptions } from './types/jwtOptions';
|
|
4
|
+
/**
|
|
5
|
+
* The `jwtPlugin` provides JSON Web Token (JWT) authentication capabilities for Elysia applications.
|
|
6
|
+
*
|
|
7
|
+
* The plugin adds two primary methods to your Elysia context:
|
|
8
|
+
* - `sign()`: Generate and sign new JWTs
|
|
9
|
+
* - `verify()`: Validate and decode existing JWTs
|
|
10
|
+
*
|
|
11
|
+
* @template Name - The name to use for JWT functionality in the context (default: 'jwt')
|
|
12
|
+
* @param options - Configuration options for the JWT plugin
|
|
13
|
+
*
|
|
14
|
+
* @returns An Elysia plugin that adds JWT functionality to the application context
|
|
15
|
+
*/
|
|
16
|
+
export declare const jwtPlugin: <const Name extends string = "jwt">(options: JWTOptions<Name>) => Elysia<"", {
|
|
17
|
+
decorator: { [name in Name]: {
|
|
18
|
+
sign(additionalPayload?: JWTPayload, exp?: number | string | Date): Promise<string>;
|
|
19
|
+
verify(jwt?: string): Promise<JWTPayload | false>;
|
|
20
|
+
}; } extends infer T ? { [K in keyof T]: { [name in Name]: {
|
|
21
|
+
sign(additionalPayload?: JWTPayload, exp?: number | string | Date): Promise<string>;
|
|
22
|
+
verify(jwt?: string): Promise<JWTPayload | false>;
|
|
23
|
+
}; }[K]; } : never;
|
|
24
|
+
store: {};
|
|
25
|
+
derive: {};
|
|
26
|
+
resolve: {};
|
|
27
|
+
}, {
|
|
28
|
+
typebox: {};
|
|
29
|
+
error: {};
|
|
30
|
+
}, {
|
|
31
|
+
schema: {};
|
|
32
|
+
standaloneSchema: {};
|
|
33
|
+
macro: {};
|
|
34
|
+
macroFn: {};
|
|
35
|
+
parser: {};
|
|
36
|
+
}, {}, {
|
|
37
|
+
derive: {};
|
|
38
|
+
resolve: {};
|
|
39
|
+
schema: import("elysia").MergeSchema<{}, {}, "">;
|
|
40
|
+
standaloneSchema: import("elysia/types").PrettifySchema<{}>;
|
|
41
|
+
}, {
|
|
42
|
+
derive: {};
|
|
43
|
+
resolve: {};
|
|
44
|
+
schema: {};
|
|
45
|
+
standaloneSchema: {};
|
|
46
|
+
}>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
/**
|
|
3
|
+
* The `microservicePlugin` provides endpoints for microservice information and health checks.
|
|
4
|
+
*
|
|
5
|
+
* It includes the following endpoints:
|
|
6
|
+
* - `/ping`: Checks if the microservice is alive.
|
|
7
|
+
* - `/info`: Provides information about the microservice.
|
|
8
|
+
*/
|
|
9
|
+
export declare const microservicePlugin: Elysia<"/microservice", {
|
|
10
|
+
decorator: {};
|
|
11
|
+
store: {};
|
|
12
|
+
derive: {};
|
|
13
|
+
resolve: {};
|
|
14
|
+
}, {
|
|
15
|
+
typebox: {
|
|
16
|
+
readonly infoResponse200: import("@sinclair/typebox").TObject<{
|
|
17
|
+
message: import("@sinclair/typebox").TString;
|
|
18
|
+
content: import("@sinclair/typebox").TObject<{
|
|
19
|
+
author: import("@sinclair/typebox").TString;
|
|
20
|
+
name: import("@sinclair/typebox").TString;
|
|
21
|
+
version: import("@sinclair/typebox").TString;
|
|
22
|
+
description: import("@sinclair/typebox").TString;
|
|
23
|
+
}>;
|
|
24
|
+
}>;
|
|
25
|
+
readonly pingResponse200: import("@sinclair/typebox").TObject<{
|
|
26
|
+
message: import("@sinclair/typebox").TString;
|
|
27
|
+
}>;
|
|
28
|
+
};
|
|
29
|
+
error: {};
|
|
30
|
+
}, {
|
|
31
|
+
schema: {};
|
|
32
|
+
standaloneSchema: {};
|
|
33
|
+
macro: {};
|
|
34
|
+
macroFn: {};
|
|
35
|
+
parser: {};
|
|
36
|
+
}, {
|
|
37
|
+
microservice: {
|
|
38
|
+
ping: {
|
|
39
|
+
get: {
|
|
40
|
+
body: unknown;
|
|
41
|
+
params: {};
|
|
42
|
+
query: unknown;
|
|
43
|
+
headers: unknown;
|
|
44
|
+
response: {
|
|
45
|
+
200: {
|
|
46
|
+
message: string;
|
|
47
|
+
};
|
|
48
|
+
422: {
|
|
49
|
+
type: "validation";
|
|
50
|
+
on: string;
|
|
51
|
+
summary?: string;
|
|
52
|
+
message?: string;
|
|
53
|
+
found?: unknown;
|
|
54
|
+
property?: string;
|
|
55
|
+
expected?: string;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
} & {
|
|
62
|
+
microservice: {
|
|
63
|
+
info: {
|
|
64
|
+
get: {
|
|
65
|
+
body: unknown;
|
|
66
|
+
params: {};
|
|
67
|
+
query: unknown;
|
|
68
|
+
headers: unknown;
|
|
69
|
+
response: {
|
|
70
|
+
200: {
|
|
71
|
+
message: string;
|
|
72
|
+
content: {
|
|
73
|
+
description: string;
|
|
74
|
+
version: string;
|
|
75
|
+
name: string;
|
|
76
|
+
author: string;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
422: {
|
|
80
|
+
type: "validation";
|
|
81
|
+
on: string;
|
|
82
|
+
summary?: string;
|
|
83
|
+
message?: string;
|
|
84
|
+
found?: unknown;
|
|
85
|
+
property?: string;
|
|
86
|
+
expected?: string;
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
}, {
|
|
93
|
+
derive: {};
|
|
94
|
+
resolve: {};
|
|
95
|
+
schema: import("elysia").MergeSchema<{}, {}, "">;
|
|
96
|
+
standaloneSchema: import("elysia/types").PrettifySchema<{}>;
|
|
97
|
+
}, {
|
|
98
|
+
derive: {};
|
|
99
|
+
resolve: {};
|
|
100
|
+
schema: {};
|
|
101
|
+
standaloneSchema: {};
|
|
102
|
+
}>;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import type { RateLimitOptions } from './types/rateLimitOptions';
|
|
3
|
+
/**
|
|
4
|
+
* The `rateLimitPlugin` provides rate limiting capabilities for Elysia applications,
|
|
5
|
+
* protecting APIs from excessive use and potential abuse. It tracks request rates by client IP
|
|
6
|
+
* and enforces configurable limits based on a sliding time window.
|
|
7
|
+
*
|
|
8
|
+
* The plugin uses Redis for tracking request counts, making it suitable for distributed
|
|
9
|
+
* environments where multiple server instances may be handling requests from the same client.
|
|
10
|
+
* When a client exceeds the configured request limit, the plugin returns a 429 Too Many Requests
|
|
11
|
+
* response with appropriate headers indicating the limit and reset time.
|
|
12
|
+
*
|
|
13
|
+
* ### Rate Limit Headers:
|
|
14
|
+
* The plugin adds the following headers to all responses:
|
|
15
|
+
* - `X-RateLimit-Limit`: The maximum number of requests allowed in the window
|
|
16
|
+
* - `X-RateLimit-Remaining`: The number of requests remaining in the current window
|
|
17
|
+
* - `X-RateLimit-Reset`: The time in seconds until the rate limit resets
|
|
18
|
+
*
|
|
19
|
+
* @param options - The configuration options for the rate limit plugin
|
|
20
|
+
*
|
|
21
|
+
* @returns An {@link Elysia} plugin that adds rate limiting functionality
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* import { Elysia } from 'elysia';
|
|
26
|
+
* import { Redis } from '#/core/store/redis';
|
|
27
|
+
* import { rateLimitPlugin } from '#/core/elysia/plugin/ratelimit';
|
|
28
|
+
*
|
|
29
|
+
* // Create Redis instance
|
|
30
|
+
* const redis = new Redis({
|
|
31
|
+
* host: 'localhost',
|
|
32
|
+
* port: 6379
|
|
33
|
+
* });
|
|
34
|
+
* await redis.connect();
|
|
35
|
+
*
|
|
36
|
+
* // Create and configure the application with rate limiting
|
|
37
|
+
* const app = new Elysia()
|
|
38
|
+
* .use(rateLimitPlugin({
|
|
39
|
+
* redis,
|
|
40
|
+
* limit: 100, // 100 requests
|
|
41
|
+
* window: 60, // per minute
|
|
42
|
+
* message: 'Too many requests, please try again later.'
|
|
43
|
+
* }))
|
|
44
|
+
* .get('/public-api', () => {
|
|
45
|
+
* return { success: true, message: 'This endpoint is rate limited' };
|
|
46
|
+
* });
|
|
47
|
+
*
|
|
48
|
+
* // Start the server
|
|
49
|
+
* app.listen(3000);
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare const rateLimitPlugin: ({ redis, limit, window, message }: RateLimitOptions) => Elysia<"", {
|
|
53
|
+
decorator: {};
|
|
54
|
+
store: {};
|
|
55
|
+
derive: {};
|
|
56
|
+
resolve: {};
|
|
57
|
+
}, {
|
|
58
|
+
typebox: {};
|
|
59
|
+
error: {};
|
|
60
|
+
}, {
|
|
61
|
+
schema: {};
|
|
62
|
+
standaloneSchema: {};
|
|
63
|
+
macro: {};
|
|
64
|
+
macroFn: {};
|
|
65
|
+
parser: {};
|
|
66
|
+
}, {}, {
|
|
67
|
+
derive: {};
|
|
68
|
+
resolve: {};
|
|
69
|
+
schema: import("elysia").MergeSchema<{}, {}, "">;
|
|
70
|
+
standaloneSchema: import("elysia/types").PrettifySchema<{}>;
|
|
71
|
+
}, {
|
|
72
|
+
derive: {};
|
|
73
|
+
resolve: {};
|
|
74
|
+
schema: {};
|
|
75
|
+
standaloneSchema: {};
|
|
76
|
+
}>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const infoResponse200Schema: import("@sinclair/typebox").TObject<{
|
|
2
|
+
message: import("@sinclair/typebox").TString;
|
|
3
|
+
content: import("@sinclair/typebox").TObject<{
|
|
4
|
+
author: import("@sinclair/typebox").TString;
|
|
5
|
+
name: import("@sinclair/typebox").TString;
|
|
6
|
+
version: import("@sinclair/typebox").TString;
|
|
7
|
+
description: import("@sinclair/typebox").TString;
|
|
8
|
+
}>;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Static, TObject } from '@sinclair/typebox';
|
|
2
|
+
import type { Context, DefinitionBase, Elysia, EphemeralType, MetadataBase, RouteBase, RouteSchema, SingletonBase } from 'elysia';
|
|
3
|
+
import type { CRUDRoutes } from './crudRoutes';
|
|
4
|
+
import type { DynamicDatabaseSelectorPluginOptions } from './dynamicDatabaseSelectorPluginOptions';
|
|
5
|
+
/**
|
|
6
|
+
* Options for the CRUD plugin
|
|
7
|
+
*
|
|
8
|
+
* @template TInferedObject - The type of the object to be used in the CRUD operations extending {@link TObject}
|
|
9
|
+
* @template KEnumPermission - The type of the enum for permissions extending {@link String}
|
|
10
|
+
*/
|
|
11
|
+
export interface CrudOptions<TInferedObject extends TObject, KEnumPermission extends string> {
|
|
12
|
+
/**
|
|
13
|
+
* Optional API path prefix for all generated routes.
|
|
14
|
+
*
|
|
15
|
+
* When specified, all routes will be prefixed with this path.
|
|
16
|
+
* For example, if prefix is '/api/users', the find route will be '/api/users/count'.
|
|
17
|
+
*
|
|
18
|
+
* @defaultValue ''
|
|
19
|
+
*/
|
|
20
|
+
prefix?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The name of the table this CRUD interface will manage.
|
|
23
|
+
*
|
|
24
|
+
* This should match the database table name and will be used to identify
|
|
25
|
+
* the repository and models in the generated API.
|
|
26
|
+
*/
|
|
27
|
+
tableName: string;
|
|
28
|
+
/**
|
|
29
|
+
* Database configuration for the CRUD operations.
|
|
30
|
+
*
|
|
31
|
+
* This can either be a string (database name) for static connections or a
|
|
32
|
+
* DynamicDatabaseSelectorPluginOptions object for dynamic database selection.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // Static database connection
|
|
37
|
+
* database: 'my_database'
|
|
38
|
+
*
|
|
39
|
+
* // Dynamic database selection
|
|
40
|
+
* database: {
|
|
41
|
+
* baseDatabaseConfig: {
|
|
42
|
+
* host: 'localhost',
|
|
43
|
+
* port: 1433,
|
|
44
|
+
* user: 'sa',
|
|
45
|
+
* password: 'Password123'
|
|
46
|
+
* },
|
|
47
|
+
* headerKey: 'x-tenant-db'
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*
|
|
51
|
+
* @see {@link DynamicDatabaseSelectorPluginOptions}
|
|
52
|
+
*
|
|
53
|
+
*/
|
|
54
|
+
database: string | DynamicDatabaseSelectorPluginOptions;
|
|
55
|
+
/**
|
|
56
|
+
* The schema to be used for the CRUD operations {@link TInferedObject}
|
|
57
|
+
* [TODO] - improve this description when working on the AND-188 ticket
|
|
58
|
+
*/
|
|
59
|
+
baseSchema: TInferedObject;
|
|
60
|
+
/**
|
|
61
|
+
* Array of property names from the schema that should be required in insert operations.
|
|
62
|
+
*
|
|
63
|
+
* When specified, these properties will be required in the body of insert requests,
|
|
64
|
+
* while other properties will remain optional.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* // Makes name and email required for insertion, while other fields remain optional
|
|
69
|
+
* insertPropertiesSchemaRequired: ['name', 'email']
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
insertPropertiesSchemaRequired?: (keyof Static<TInferedObject>)[];
|
|
73
|
+
/**
|
|
74
|
+
* Array of route types to include in the generated API.
|
|
75
|
+
*
|
|
76
|
+
* @see {@link CRUDRoutes}
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* // Only generate read-only routes
|
|
81
|
+
* includedRoutes: ['find', 'findOne', 'count']
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
includedRoutes?: CRUDRoutes[];
|
|
85
|
+
/**
|
|
86
|
+
* Array of route types to exclude from the generated API.
|
|
87
|
+
*
|
|
88
|
+
* @see {@link CRUDRoutes}
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Generate all routes except delete operations
|
|
93
|
+
* excludedRoutes?: ['delete', 'deleteOne']
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
excludedRoutes?: CRUDRoutes[];
|
|
97
|
+
/** This allows configuring permissions for each CRUD route. */
|
|
98
|
+
permissionConfig: {
|
|
99
|
+
/**
|
|
100
|
+
* A bit tricky: this should be an instance of {@link Elysia} that must have two macros
|
|
101
|
+
* called `needsOnePermission` and `needsMultiplePermissions`, each taking an array of permissions {@link KEnumPermission}
|
|
102
|
+
* as a parameter and throwing a {@link CoreError} if the user does not have the required permission.
|
|
103
|
+
*/
|
|
104
|
+
permissionsPlugin: Elysia<'', SingletonBase, DefinitionBase, MetadataBase & {
|
|
105
|
+
schema: RouteSchema;
|
|
106
|
+
macro: Partial<{
|
|
107
|
+
readonly needsOnePermission: KEnumPermission[];
|
|
108
|
+
readonly needsMultiplePermissions: KEnumPermission[];
|
|
109
|
+
}>;
|
|
110
|
+
macroFn: {
|
|
111
|
+
readonly needsOnePermission: (permissions: KEnumPermission[]) => {
|
|
112
|
+
beforeHandle: (ctx: Context) => Promise<void>;
|
|
113
|
+
};
|
|
114
|
+
readonly needsMultiplePermissions: (permissions: KEnumPermission[]) => {
|
|
115
|
+
beforeHandle: (ctx: Context) => Promise<void>;
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
}, RouteBase, EphemeralType, EphemeralType>;
|
|
119
|
+
/**
|
|
120
|
+
* The permissions to be used for the CRUD operations
|
|
121
|
+
* @see {@link CRUDRoutes}
|
|
122
|
+
* @see {@link KEnumPermission}
|
|
123
|
+
*/
|
|
124
|
+
operationsPermissions: Partial<Record<CRUDRoutes, KEnumPermission[]>>;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MSSQLDatabaseOptions } from '../../database/types';
|
|
2
|
+
/**
|
|
3
|
+
* Options to configure the dynamic database selector plugin.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const options: DynamicDatabaseSelectorPluginOptions = {
|
|
8
|
+
* baseDatabaseConfig: {
|
|
9
|
+
* host: 'localhost',
|
|
10
|
+
* port: 1433,
|
|
11
|
+
* user: 'sa',
|
|
12
|
+
* password: 'Password123',
|
|
13
|
+
* encrypt: true
|
|
14
|
+
* },
|
|
15
|
+
* headerKey: 'x-database-name'
|
|
16
|
+
* };
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export interface DynamicDatabaseSelectorPluginOptions {
|
|
20
|
+
/**
|
|
21
|
+
* Options for the database connection
|
|
22
|
+
* @see {@link MSSQLDatabaseOptions}
|
|
23
|
+
*/
|
|
24
|
+
baseDatabaseConfig: Omit<MSSQLDatabaseOptions, 'databaseName'>;
|
|
25
|
+
/**
|
|
26
|
+
* The name of the key to be used in the header to select the database
|
|
27
|
+
* @example 'x-database-name'
|
|
28
|
+
*/
|
|
29
|
+
headerKey?: string;
|
|
30
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { JWSHeaderParameters, JWTPayload } from 'jose';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the JWT plugin.
|
|
4
|
+
*
|
|
5
|
+
* @template TPluginName - The name to be used for accessing the JWT functionality in the Elysia context
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* const options: JWTOption = {
|
|
10
|
+
* name: 'auth',
|
|
11
|
+
* secret: process.env.JWT_SECRET || 'your-secret-key',
|
|
12
|
+
* exp: '1d',
|
|
13
|
+
* header: {
|
|
14
|
+
* alg: 'HS256',
|
|
15
|
+
* typ: 'JWT'
|
|
16
|
+
* },
|
|
17
|
+
* payload: {
|
|
18
|
+
* iss: 'my-api',
|
|
19
|
+
* aud: 'my-client'
|
|
20
|
+
* }
|
|
21
|
+
* };
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface JWTOptions<TPluginName extends string | undefined = 'jwt'> {
|
|
25
|
+
/**
|
|
26
|
+
* JWT name to add in context with decorate.
|
|
27
|
+
*
|
|
28
|
+
* This allows you to customize how you access the JWT functionality in your
|
|
29
|
+
* route handlers. For example, if set to 'auth', you would use `auth.sign()`
|
|
30
|
+
* instead of the default `jwt.sign()`.
|
|
31
|
+
*
|
|
32
|
+
* @defaultValue 'jwt'
|
|
33
|
+
*/
|
|
34
|
+
name?: TPluginName;
|
|
35
|
+
/**
|
|
36
|
+
* Secret key used to sign and verify JWTs.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* // Using a string secret
|
|
41
|
+
* secret: 'your-very-secret-key'
|
|
42
|
+
*
|
|
43
|
+
* // Using an environment variable (recommended for production)
|
|
44
|
+
* secret: process.env.JWT_SECRET
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
secret: string;
|
|
48
|
+
/**
|
|
49
|
+
* JWT expiration setting. Applies as the default expiration for tokens.
|
|
50
|
+
*
|
|
51
|
+
* Controls how long tokens are valid before they expire. This setting provides
|
|
52
|
+
* a good balance between security (limiting the window of opportunity for token misuse)
|
|
53
|
+
* and user experience (not requiring frequent re-authentication).
|
|
54
|
+
*
|
|
55
|
+
* @defaultValue '15m' (15 minutes)
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* // Set tokens to expire after 1 hour
|
|
60
|
+
* exp: '1h'
|
|
61
|
+
*
|
|
62
|
+
* // Set tokens to expire at a specific date
|
|
63
|
+
* exp: new Date('2023-12-31')
|
|
64
|
+
*
|
|
65
|
+
* // Set tokens to expire after 3600 seconds (1 hour)
|
|
66
|
+
* exp: 3600
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
exp?: number | string | Date;
|
|
70
|
+
/**
|
|
71
|
+
* Default JWT header parameters to include in every JWT.
|
|
72
|
+
*
|
|
73
|
+
* This allows you to specify additional metadata about the token.
|
|
74
|
+
*/
|
|
75
|
+
header?: JWSHeaderParameters;
|
|
76
|
+
/**
|
|
77
|
+
* Default payload values to include in every JWT.
|
|
78
|
+
*
|
|
79
|
+
* These values are merged with any additional payload provided during signing,
|
|
80
|
+
* allowing you to set standard claims that should be included in all tokens
|
|
81
|
+
* generated by your application.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* payload: {
|
|
86
|
+
* iss: 'my-api', // Issuer
|
|
87
|
+
* aud: 'my-frontend', // Audience
|
|
88
|
+
* }
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
payload?: JWTPayload;
|
|
92
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Redis } from '../../store/redis';
|
|
2
|
+
/**
|
|
3
|
+
* Options to configure the rate limit plugin.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const options: RateLimitOptions = {
|
|
8
|
+
* redis: redisInstance, // Your Redis instance
|
|
9
|
+
* limit: 100, // Allow 100 requests
|
|
10
|
+
* window: 60, // Per 60 seconds
|
|
11
|
+
* message: 'You have exceeded the rate limit. Please try again later.'
|
|
12
|
+
* };
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface RateLimitOptions {
|
|
16
|
+
/**
|
|
17
|
+
* The Redis instance to store rate limit data.
|
|
18
|
+
* @see {@link Redis}
|
|
19
|
+
*/
|
|
20
|
+
redis: Redis;
|
|
21
|
+
/**
|
|
22
|
+
* Maximum number of requests allowed in the time window.
|
|
23
|
+
*
|
|
24
|
+
* This defines how many requests a client can make within the specified time window
|
|
25
|
+
* before rate limiting is applied.
|
|
26
|
+
*/
|
|
27
|
+
limit: number;
|
|
28
|
+
/**
|
|
29
|
+
* Time window in seconds during which the request limit applies.
|
|
30
|
+
*
|
|
31
|
+
* This defines the duration of the rate limiting window. For example, a window of 60
|
|
32
|
+
* with a limit of 100 allows 100 requests per minute per client.
|
|
33
|
+
*/
|
|
34
|
+
window: number;
|
|
35
|
+
/**
|
|
36
|
+
* Custom error message when rate limit is exceeded.
|
|
37
|
+
*
|
|
38
|
+
* If provided, this message will be included in the error response when a client
|
|
39
|
+
* exceeds their rate limit. If not provided, a default message is used.
|
|
40
|
+
*
|
|
41
|
+
* @defaultValue 'Rate limit exceeded'
|
|
42
|
+
*/
|
|
43
|
+
message?: string;
|
|
44
|
+
}
|