modelence 0.6.18 → 0.6.19
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/dist/bin/modelence.js +1 -1
- package/dist/{chunk-OEVRIT3I.js → chunk-ARRGI5M7.js} +3 -3
- package/dist/{chunk-OEVRIT3I.js.map → chunk-ARRGI5M7.js.map} +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/{package-DSL4EF4B.js → package-V6P5ZO4M.js} +2 -2
- package/dist/{package-DSL4EF4B.js.map → package-V6P5ZO4M.js.map} +1 -1
- package/dist/server.d.ts +6 -489
- package/dist/server.js +1 -1
- package/dist/types-D6nwUab6.d.ts +543 -0
- package/dist/{types-CmI2APz9.d.ts → types-Ds1ESQSs.d.ts} +3 -1
- package/dist/types.d.ts +5 -27
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -13,6 +13,6 @@ import {a as a$2}from'./chunk-3S2FFBNS.js';import {d,a as a$3}from'./chunk-C3UES
|
|
|
13
13
|
<p>If you did not request this password reset, please ignore this email.</p>
|
|
14
14
|
`}var ge={success:true,message:"If an account with that email exists, a password reset link has been sent"};async function lt(t,{connectionInfo:e}){let o=j(t.email),n=await p.findOne({"emails.address":o,status:{$nin:["deleted","disabled"]}},{collation:{locale:"en",strength:2}});if(!n||!n.authMethods?.password)return ge;let r=w().provider;if(!r)throw new Error("Email provider is not configured");let i=randomBytes(32).toString("hex"),s=new Date(Date.now()+a.hours(1));await C.insertOne({userId:n._id,token:i,createdAt:new Date,expiresAt:s});let a$1=process.env.MODELENCE_SITE_URL||e?.baseUrl,d=`${So(a$1,w().passwordReset?.redirectUrl)}?token=${i}`,u=(w()?.passwordReset?.template||xo)({email:o,resetUrl:d,name:""}),S=X(u);return await r.sendEmail({to:o,from:w()?.from||"noreply@modelence.com",subject:w()?.passwordReset?.subject||"Reset your password",text:S,html:u}),ge}async function dt(t,{}){let e=z.string().parse(t.token),o=te(t.password),n=await C.findOne({token:e});if(!n)throw new Error("Invalid or expired reset token");if(n.expiresAt<new Date)throw await C.deleteOne({token:e}),new Error("Reset token has expired");let r=await p.findOne({_id:n.userId});if(!r)throw new Error("User not found");let i=await fo.hash(o,10);return await p.updateOne({_id:r._id},{$set:{"authMethods.password.hash":i}}),await C.deleteOne({token:e}),{success:true,message:"Password has been reset successfully"}}var ut=new b("_system.user",{stores:[p,W,v,C],queries:{getOwnProfile:st},mutations:{signupWithPassword:ct,loginWithPassword:rt,logout:it,sendResetPasswordToken:lt,resetPassword:dt},cronJobs:{updateDisposableEmailList:Qe},rateLimits:[{bucket:"signup",type:"ip",window:a.minutes(15),limit:20},{bucket:"signup",type:"ip",window:a.days(1),limit:200},{bucket:"signupAttempt",type:"ip",window:a.minutes(15),limit:50},{bucket:"signupAttempt",type:"ip",window:a.days(1),limit:500},{bucket:"signin",type:"ip",window:a.minutes(15),limit:50},{bucket:"signin",type:"ip",window:a.days(1),limit:500},{bucket:"verification",type:"user",window:a.minutes(15),limit:3},{bucket:"verification",type:"user",window:a.days(1),limit:10}],configSchema:{"auth.email.enabled":{type:"boolean",isPublic:true,default:true},"auth.email.from":{type:"string",isPublic:false,default:""},"auth.email.verification":{type:"boolean",isPublic:true,default:false},"auth.google.enabled":{type:"boolean",isPublic:true,default:false},"auth.google.clientId":{type:"string",isPublic:false,default:""},"auth.google.clientSecret":{type:"secret",isPublic:false,default:""},"auth.github.enabled":{type:"boolean",isPublic:true,default:false},"auth.github.clientId":{type:"string",isPublic:false,default:""},"auth.github.clientSecret":{type:"secret",isPublic:false,default:""}},routes:[{path:"/api/_internal/auth/verify-email",handlers:{get:et}}]});async function mt({configSchema:t,cronJobsMetadata:e,stores:o}){let n=process.env.MODELENCE_CONTAINER_ID;if(!n)throw new Error("Unable to connect to Modelence Cloud: MODELENCE_CONTAINER_ID is not set");try{let r=Object.values(o).map(s=>({name:s.getName(),schema:s.getSerializedSchema(),collections:[s.getName()],version:2})),i=await ye("/api/connect","POST",{hostname:To.hostname(),containerId:n,dataModels:r,configSchema:t,cronJobsMetadata:e});if(i.status==="error")throw new Error(i.error);return console.log("Successfully connected to Modelence Cloud"),i}catch(r){throw console.error("Unable to connect to Modelence Cloud:",r),r}}async function pt(){return await ye("/api/configs","GET")}async function ft(){return await ye("/api/sync","POST",{containerId:process.env.MODELENCE_CONTAINER_ID})}async function ye(t,e,o){let{MODELENCE_SERVICE_ENDPOINT:n,MODELENCE_SERVICE_TOKEN:r}=process.env;if(!n)throw new Error("Unable to connect to Modelence Cloud: MODELENCE_SERVICE_ENDPOINT is not set");let i=await fetch(`${n}${t}`,{method:e,headers:{Authorization:`Bearer ${r}`,...o?{"Content-Type":"application/json"}:{}},body:o?JSON.stringify(o):void 0});if(!i.ok){let s=await i.text();try{let a=JSON.parse(s);throw new Error(`Unable to connect to Modelence Cloud: HTTP status: ${i.status}, ${a?.error}`)}catch{throw new Error(`Unable to connect to Modelence Cloud: HTTP status: ${i.status}, ${s}`)}}return await i.json()}var we=false,vo=a.seconds(10);function ht(){setInterval(async()=>{if(!we){we=true;try{await ft();}catch(t){console.error("Error syncing status",t);}try{await Do();}catch(t){console.error("Error syncing config",t);}we=false;}},vo);}async function Do(){let{configs:t}=await pt();c$1(t);}var J=new y("_modelenceLocks",{schema:{resource:c.string(),instanceId:c.string(),acquiredAt:c.date()},indexes:[{key:{resource:1},unique:true},{key:{resource:1,instanceId:1}},{key:{resource:1,acquiredAt:1}}]});var N={},gt=a.seconds(10),yt=randomBytes(32).toString("base64url"),ko=a.seconds(30);async function oe(t,{lockDuration:e=ko,successfulLockCacheDuration:o=gt,failedLockCacheDuration:n=gt,instanceId:r=yt}={}){let i=Date.now();if(N[t]&&i<N[t].expiresAt)return N[t].value;let s=new Date(i-e);h(`Attempting to acquire lock: ${t}`,{source:"lock",resource:t,instanceId:r});try{let a=await J.upsertOne({$or:[{resource:t,instanceId:r},{resource:t,acquiredAt:{$lt:s}}]},{$set:{resource:t,instanceId:r,acquiredAt:new Date}}),l=a.upsertedCount>0||a.modifiedCount>0;return N[t]={value:l,expiresAt:i+(l?o:n)},l?h(`Lock acquired: ${t}`,{source:"lock",resource:t,instanceId:r}):h(`Failed to acquire lock (already held): ${t}`,{source:"lock",resource:t,instanceId:r}),l}catch{return N[t]={value:false,expiresAt:i+n},h(`Failed to acquire lock (already held): ${t}`,{source:"lock",resource:t,instanceId:r}),false}}async function wt(t,{instanceId:e=yt}={}){let o=await J.deleteOne({resource:t,instanceId:e});return delete N[t],o.deletedCount>0}var Ro=a.minutes(1),A={},be=null,Ee=new y("_modelenceCronJobs",{schema:{alias:c.string(),lastStartDate:c.date().optional()},indexes:[{key:{alias:1},unique:true,background:true}]});function Et(t,{description:e="",interval:o,timeout:n=Ro,handler:r}){if(A[t])throw new Error(`Duplicate cron job declaration: '${t}' already exists`);if(be)throw new Error(`Unable to add a cron job - cron jobs have already been initialized: [${t}]`);if(o<a.seconds(5))throw new Error(`Cron job interval should not be less than 5 second [${t}]`);if(n>a.days(1))throw new Error(`Cron job timeout should not be longer than 1 day [${t}]`);A[t]={alias:t,params:{description:e,interval:o,timeout:n},handler:r,state:{isRunning:false}};}async function Ct(){if(be)throw new Error("Cron jobs already started");let t=Object.keys(A);if(t.length>0){let e={alias:{$in:t}},o=await Ee.fetch(e),n=Date.now();o.forEach(r=>{let i=A[r.alias];i&&(i.state.scheduledRunTs=r.lastStartDate?r.lastStartDate.getTime()+i.params.interval:n);}),Object.values(A).forEach(r=>{r.state.scheduledRunTs||(r.state.scheduledRunTs=n);}),be=setInterval(Oo,a.seconds(1));}}async function Oo(){let t=Date.now();await oe("cron",{successfulLockCacheDuration:a.seconds(10),failedLockCacheDuration:a.seconds(30)})&&Object.values(A).forEach(async o=>{let{params:n,state:r}=o;if(r.isRunning){r.startTs&&r.startTs+n.timeout<t&&(r.isRunning=false);return}r.scheduledRunTs&&r.scheduledRunTs<=t&&await Mo(o);});}async function Mo(t){let{alias:e,params:o,handler:n,state:r}=t;r.isRunning=true,r.startTs=Date.now(),await Ee.updateOne({alias:e},{$set:{lastStartDate:new Date(r.startTs)}});let i=k$1("cron",`cron:${e}`);try{await n(),bt(r,o),i.end("success");}catch(s){bt(r,o);let a=s instanceof Error?s:new Error(String(s));l(a),i.end("error"),console.error(`Error in cron job '${e}':`,s);}}function bt(t,e){t.scheduledRunTs=t.startTs?t.startTs+e.interval:Date.now(),t.startTs=void 0,t.isRunning=false;}function St(){return Object.values(A).map(({alias:t,params:e})=>({alias:t,description:e.description,interval:e.interval,timeout:e.timeout}))}var xt=new b("_system.cron",{stores:[Ee]});var Ce={};function Se(t,e){return O(),_t(t),ne("query",t,e)}function Tt(t,e){return O(),_t(t),ne("mutation",t,e)}function vt(t,e){return O(),kt(t),ne("query",t,e)}function Dt(t,e){return O(),kt(t),ne("mutation",t,e)}function _t(t){if(t.toLowerCase().startsWith("_system."))throw new Error(`Method name cannot start with a reserved prefix: '_system.' (${t})`)}function kt(t){if(!t.toLowerCase().startsWith("_system."))throw new Error(`System method name must start with a prefix: '_system.' (${t})`)}function ne(t,e,o){if(O(),Ce[e])throw new Error(`Method with name '${e}' is already defined.`);let n=typeof o=="function"?o:o.handler,r=typeof o=="function"?[]:o.permissions??[];Ce[e]={type:t,name:e,handler:n,permissions:r};}async function Rt(t,e,o){O();let n=Ce[t];if(!n)throw new Error(`Method with name '${t}' is not defined.`);let{type:r,handler:i}=n,s=k$1("method",`method:${t}`,{type:r,args:e}),a;try{He(o.roles,n.permissions),a=await i(e,o);}catch(l){throw s.end("error"),l}return s.end(),a}var xe=new b("_system.lock",{stores:[J]});var H=new y("_modelenceMigrations",{schema:{version:c.number(),status:c.enum(["completed","failed"]),description:c.string().optional(),output:c.string().optional(),appliedAt:c.date()},indexes:[{key:{version:1},unique:true},{key:{version:1,status:1}}]});async function Ao(t){if(t.length===0)return;if(!await oe("migrations")){i("Another instance is running migrations. Skipping migration run.",{source:"migrations"});return}let o=t.map(({version:s})=>s),n=await H.fetch({version:{$in:o}}),r=new Set(n.map(({version:s})=>s)),i$1=t.filter(({version:s})=>!r.has(s));if(i$1.length!==0){i(`Running migrations (${i$1.length})...`,{source:"migrations"});for(let{version:s,description:a,handler:l}of i$1){i(`Running migration v${s}: ${a}`,{source:"migrations"});try{let d=await l();await H.upsertOne({version:s},{$set:{version:s,status:"completed",description:a,output:d||"",appliedAt:new Date}}),i(`Migration v${s} complete`,{source:"migrations"});}catch(d){d instanceof Error&&(await H.upsertOne({version:s},{$set:{version:s,status:"failed",description:a,output:d.message||"",appliedAt:new Date}}),i(`Migration v${s} is failed: ${d.message}`,{source:"migrations"}));}}await wt("migrations");}}function Ot(t){setTimeout(()=>{Ao(t).catch(e=>{console.error("Error running migrations:",e);});},0);}var Mt=new b("_system.migration",{stores:[H]});var At=new b("_system.rateLimit",{stores:[B]});var It=new b("_system",{configSchema:{mongodbUrl:{type:"string",isPublic:false,default:""},env:{type:"string",isPublic:true,default:""},"site.url":{type:"string",isPublic:true,default:""},"log.level":{type:"string",isPublic:false,default:"info"}}});var ve=class{async init(){this.config=await qo(),this.isDev()&&(console.log("Starting Vite dev server..."),this.viteServer=await createServer(this.config));}middlewares(){if(this.isDev())return this.viteServer?.middlewares??[];let e=[ke.static("./.modelence/build/client".replace(/\\/g,"/"))];return this.config?.publicDir&&e.push(ke.static(this.config.publicDir)),e}handler(e,o){if(this.isDev())try{o.sendFile("index.html",{root:"./src/client"});}catch(n){console.error("Error serving index.html:",n),o.status(500).send("Internal Server Error");}else o.sendFile("index.html",{root:"./.modelence/build/client".replace(/\\/g,"/")});}isDev(){return process.env.NODE_ENV!=="production"}};async function $o(){let t=process.cwd();try{return (await loadConfigFromFile({command:"serve",mode:"development"},void 0,t))?.config||{}}catch(e){return console.warn("Could not load vite config:",e),{}}}function zo(t,e){let o=mergeConfig(t,e);if(o.plugins&&Array.isArray(o.plugins)){let n=new Set;o.plugins=o.plugins.flat().filter(r=>{if(!r||typeof r!="object"||Array.isArray(r))return true;let i=r.name;return !i||n.has(i)?false:(n.add(i),true)}).reverse(),o.plugins.reverse();}return o}async function qo(){let t=process.cwd(),e=await $o(),o=[".eslintrc.js",".eslintrc.json",".eslintrc","eslint.config.js",".eslintrc.yml",".eslintrc.yaml"].find(i=>No.existsSync(Te.join(t,i))),n=[jo(),Zo()];if(o){let i=(await import('vite-plugin-eslint')).default;n.push(i({failOnError:false,include:["src/**/*.js","src/**/*.jsx","src/**/*.ts","src/**/*.tsx"],cwd:t,overrideConfigFile:Te.resolve(t,o)}));}let r=defineConfig({plugins:n,build:{outDir:".modelence/build/client".replace(/\\/g,"/"),emptyOutDir:true},server:{middlewareMode:true},root:"./src/client",resolve:{alias:{"@":Te.resolve(t,"src").replace(/\\/g,"/")}}});return zo(r,e)}function Zo(){return {name:"modelence-asset-handler",async transform(t,e){if(/\.(png|jpe?g|gif|svg|mpwebm|ogg|mp3|wav|flac|aac)$/.test(e))return process.env.NODE_ENV==="development",t}}}var Ut=new ve;async function Lt(t,e){let{authToken:o}=await me(e);t.cookie("authToken",o,{httpOnly:true,secure:process.env.NODE_ENV==="production",sameSite:"strict"}),t.status(301),t.redirect("/");}function $(t){return `${a$1("_system.site.url")}/api/_internal/auth/${t}/callback`}async function re(t,e,o){let n=await p.findOne({[`authMethods.${o.providerName}.id`]:o.id}),{session:r,connectionInfo:i}=await De(t);try{if(n){await Lt(e,n._id),g().onAfterLogin?.({user:n,session:r,connectionInfo:i}),g().login?.onSuccess?.(n);return}}catch(s){throw s instanceof Error&&(g().login?.onError?.(s),g().onLoginError?.({error:s,session:r,connectionInfo:i})),s}try{if(!o.email){e.status(400).json({error:`Email address is required for ${o.providerName} authentication.`});return}if(await p.findOne({"emails.address":o.email},{collation:{locale:"en",strength:2}})){e.status(400).json({error:"User with this email already exists. Please log in instead."});return}let a=await p.insertOne({handle:o.email,status:"active",emails:[{address:o.email,verified:o.emailVerified}],createdAt:new Date,authMethods:{[o.providerName]:{id:o.id}}});await Lt(e,a.insertedId);let l=await p.findOne({_id:a.insertedId},{readPreference:"primary"});l&&(g().onAfterSignup?.({user:l,session:r,connectionInfo:i}),g().signup?.onSuccess?.(l));}catch(s){throw s instanceof Error&&(g().onSignupError?.({error:s,session:r,connectionInfo:i}),g().signup?.onError?.(s)),s}}function ie(t){return !t||typeof t!="string"?null:t}async function Fo(t,e,o,n){let r=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({code:t,client_id:e,client_secret:o,redirect_uri:n,grant_type:"authorization_code"})});if(!r.ok)throw new Error(`Failed to exchange code for token: ${r.statusText}`);return r.json()}async function Bo(t){let e=await fetch("https://www.googleapis.com/oauth2/v2/userinfo",{headers:{Authorization:`Bearer ${t}`}});if(!e.ok)throw new Error(`Failed to fetch user info: ${e.statusText}`);return e.json()}async function Jo(t,e){let o=ie(t.query.code);if(!o){e.status(400).json({error:"Missing authorization code"});return}let n=String(a$1("_system.user.auth.google.clientId")),r=String(a$1("_system.user.auth.google.clientSecret")),i=$("google");try{let s=await Fo(o,n,r,i),a=await Bo(s.access_token),l={id:a.id,email:a.email,emailVerified:a.verified_email,providerName:"google"};await re(t,e,l);}catch(s){console.error("Google OAuth error:",s),e.status(500).json({error:"Authentication failed"});}}function Ho(){let t=Router(),e=(o,n,r)=>{let i=!!a$1("_system.user.auth.google.enabled"),s=String(a$1("_system.user.auth.google.clientId")),a=String(a$1("_system.user.auth.google.clientSecret"));if(!i||!s||!a){n.status(503).json({error:"Google authentication is not configured"});return}r();};return t.get("/api/_internal/auth/google",e,(o,n)=>{let r=String(a$1("_system.user.auth.google.clientId")),i=$("google"),s=new URL("https://accounts.google.com/o/oauth2/v2/auth");s.searchParams.append("client_id",r),s.searchParams.append("redirect_uri",i),s.searchParams.append("response_type","code"),s.searchParams.append("scope","profile email"),s.searchParams.append("access_type","online"),n.redirect(s.toString());}),t.get("/api/_internal/auth/google/callback",e,Jo),t}var jt=Ho;async function Go(t,e,o,n){let r=await fetch("https://github.com/login/oauth/access_token",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({client_id:e,client_secret:o,code:t,redirect_uri:n})});if(!r.ok)throw new Error(`Failed to exchange code for token: ${r.statusText}`);return r.json()}async function Ko(t){let e=await fetch("https://api.github.com/user",{headers:{Authorization:`Bearer ${t}`,Accept:"application/vnd.github.v3+json"}});if(!e.ok)throw new Error(`Failed to fetch user info: ${e.statusText}`);return e.json()}async function Qo(t,e){let o=ie(t.query.code);if(!o){e.status(400).json({error:"Missing authorization code"});return}let n=String(a$1("_system.user.auth.github.clientId")),r=String(a$1("_system.user.auth.github.clientSecret")),i=$("github");try{let s=await Go(o,n,r,i),a=await Ko(s.access_token),l=a.email||"";if(!l){e.status(400).json({error:"Unable to retrieve email from GitHub. Please ensure your email is public or grant email permissions."});return}let d={id:String(a.id),email:l,emailVerified:!0,providerName:"github"};await re(t,e,d);}catch(s){console.error("GitHub OAuth error:",s),e.status(500).json({error:"Authentication failed"});}}function Yo(){let t=Router(),e=(o,n,r)=>{let i=!!a$1("_system.user.auth.github.enabled"),s=String(a$1("_system.user.auth.github.clientId")),a=String(a$1("_system.user.auth.github.clientSecret"));if(!i||!s||!a){n.status(503).json({error:"GitHub authentication is not configured"});return}r();};return t.get("/api/_internal/auth/github",e,(o,n)=>{let r=String(a$1("_system.user.auth.github.clientId")),i=$("github"),s=a$1("_system.user.auth.github.scopes"),a=s?String(s).split(",").map(d=>d.trim()).join(" "):"user:email",l=new URL("https://github.com/login/oauth/authorize");l.searchParams.append("client_id",r),l.searchParams.append("redirect_uri",i),l.searchParams.append("scope",a),n.redirect(l.toString());}),t.get("/api/_internal/auth/github/callback",e,Qo),t}var Nt=Yo;function $t(t,e,o){return async(n,r,i)=>{let s=n.headers["x-modelence-auth-token"],a={session:null,user:null};if(typeof s=="string"&&R())try{let{session:d,user:E}=await k(s);a={session:d,user:E};}catch{}let l=k$1("route",`route:${t.toLowerCase()}:${e}`,{method:t,path:e,query:n.query,body:n.body,params:n.params});try{let d=await o({query:n.query,body:n.body,params:n.params,headers:n.headers,cookies:n.cookies,req:n,res:r,next:i},a);l.end(),d&&(r.status(d.status||200),d.redirect&&r.redirect(d.redirect),d.headers&&Object.entries(d.headers).forEach(([E,u])=>{r.setHeader(E,u);}),r.send(d.data));}catch(d){l.end("error"),d instanceof a$3?r.status(d.status).send(d.message):(console.error(`Error in route handler: ${n.path}`),console.error(d),r.status(500).send(String(d)));}}}var _e=Object.freeze({});function zt(t){_e=Object.freeze(Object.assign({},_e,t));}function se(){return _e}function tn(t,e){for(let o of e)for(let n of o.routes){let{path:r,handlers:i}=n;Object.entries(i).forEach(([s,a])=>{t[s](r,$t(s,r,a));});}}async function qt(t,{combinedModules:e,channels:o}){let n=ke();n.use(ke.json({limit:"16mb"})),n.use(ke.urlencoded({extended:true,limit:"16mb"})),n.use(Xo()),n.use(jt()),n.use(Nt()),n.post("/api/_internal/method/:methodName(*)",async(a,l)=>{let{methodName:d}=a.params,E=await De(a);try{let u=await Rt(d,a.body.args,E);l.json({data:u,typeMap:a$2(u)});}catch(u){if(console.error(`Error in method ${d}:`,u),u instanceof a$3)l.status(u.status).send(u.message);else if(u instanceof Error&&u?.constructor?.name==="ZodError"&&"errors"in u){let I=u.flatten(),ae=Object.entries(I.fieldErrors).map(([z,le])=>`${z}: ${(le??[]).join(", ")}`).join("; "),ce=I.formErrors.join("; "),V=[ae,ce].filter(Boolean).join("; ");l.status(400).send(V);}else l.status(500).send(u instanceof Error?u.message:String(u));}}),tn(n,e),await t.init(),t.middlewares&&n.use(t.middlewares()),n.all("*",(a,l)=>t.handler(a,l)),process.on("unhandledRejection",(a,l)=>{console.error("Unhandled Promise Rejection:"),console.error(a instanceof Error?a.stack:a),console.error("Promise:",l);}),process.on("uncaughtException",a=>{console.error("Uncaught Exception:"),console.error(a.stack),console.trace("Full application stack:");});let r=en.createServer(n),i$1=se()?.provider;i$1&&i$1.init({httpServer:r,channels:o});let s=process.env.MODELENCE_PORT||process.env.PORT||3e3;r.listen(s,()=>{i("Application started",{source:"app"}),console.log(`
|
|
15
15
|
Application started on http://localhost:${s}
|
|
16
|
-
`);});}async function De(t){let e=_.string().nullish().transform(i=>i??null).parse(t.cookies.authToken||t.body.authToken),o=_.object({screenWidth:_.number(),screenHeight:_.number(),windowWidth:_.number(),windowHeight:_.number(),pixelRatio:_.number(),orientation:_.string().nullable()}).nullish().parse(t.body.clientInfo)??{screenWidth:0,screenHeight:0,windowWidth:0,windowHeight:0,pixelRatio:1,orientation:null},n={ip:on(t),userAgent:t.get("user-agent"),acceptLanguage:t.get("accept-language"),referrer:t.get("referrer"),baseUrl:t.protocol+"://"+t.get("host")};if(!!R()){let{session:i,user:s,roles:a}=await k(e);return {clientInfo:o,connectionInfo:n,session:i,user:s,roles:a}}return {clientInfo:o,connectionInfo:n,session:null,user:null,roles:Q()}}function on(t){let e=t.headers["x-forwarded-for"];if(e)return (Array.isArray(e)?e[0]:e.split(",")[0]).trim();let o=t.ip||t.socket?.remoteAddress;if(o)return o.startsWith("::ffff:")?o.substring(7):o}async function an({modules:t=[],roles:e$1={},defaultRoles:o={},server:n=Ut,migrations:r=[],email:i={},auth:s={},websocket:a={}}){Zt.config(),Zt.config({path:".modelence.env"});let l=!!process.env.MODELENCE_SERVICE_ENDPOINT,d=process.env.MODELENCE_CRON_ENABLED==="true";bn().then(()=>{}).catch(()=>{});let E=[ut,We,xt,Mt,At,It,xe],u=[...E,...t];e(),ln(E),cn(t),Be(e$1,o);let S=pn(u);d$1(S);let I=dn(u),ae=un(u);d&&fn(u);let ce=mn(u);if(at(ce),l){let{configs:z,environmentId:le,appAlias:Ft,environmentAlias:Bt,telemetry:Jt}=await mt({configSchema:S,cronJobsMetadata:d?St():void 0,stores:I});c$1(z),f({environmentId:le,appAlias:Ft,environmentAlias:Bt,telemetry:Jt});}else c$1(wn(S));Ye(i),ot(s),zt({...a,provider:a.provider||Ge});let V=R();if(V&&(await Ve(),hn(I)),d&&Ot(r),V)for(let z of I)z.createIndexes();l&&(await g$1(),ht()),d&&Ct().catch(console.error),await qt(n,{combinedModules:u,channels:ae});}function cn(t){for(let e of t){for(let[o,n]of Object.entries(e.queries))Se(`${e.name}.${o}`,n);for(let[o,n]of Object.entries(e.mutations))Tt(`${e.name}.${o}`,n);}}function ln(t){for(let e of t){for(let[o,n]of Object.entries(e.queries))vt(`${e.name}.${o}`,n);for(let[o,n]of Object.entries(e.mutations))Dt(`${e.name}.${o}`,n);}}function dn(t){return t.flatMap(e=>e.stores)}function un(t){return t.flatMap(e=>e.channels)}function mn(t){return t.flatMap(e=>e.rateLimits)}function pn(t){let e={};for(let o of t)for(let[n,r]of Object.entries(o.configSchema)){let i=`${o.name}.${n}`;if(i in e)throw new Error(`Duplicate config schema key: ${i} (${o.name})`);e[i]=r;}return e}function fn(t){for(let e of t)for(let[o,n]of Object.entries(e.cronJobs))Et(`${e.name}.${o}`,n);}function hn(t){let e=Y();if(!e)throw new Error("Failed to initialize stores: MongoDB client not initialized");for(let o of t)o.init(e);}var gn={MONGODB_URI:"_system.mongodbUri",MODELENCE_AUTH_GOOGLE_ENABLED:"_system.user.auth.google.enabled",MODELENCE_AUTH_GOOGLE_CLIENT_ID:"_system.user.auth.google.clientId",MODELENCE_AUTH_GOOGLE_CLIENT_SECRET:"_system.user.auth.google.clientSecret",MODELENCE_AUTH_GITHUB_ENABLED:"_system.user.auth.github.enabled",MODELENCE_AUTH_GITHUB_CLIENT_ID:"_system.user.auth.github.clientId",MODELENCE_AUTH_GITHUB_CLIENT_SECRET:"_system.user.auth.github.clientSecret",MODELENCE_AUTH_GITHUB_CLIENT_SCOPES:"_system.user.auth.github.scopes",MODELENCE_EMAIL_RESEND_API_KEY:"_system.email.resend.apiKey",MODELENCE_EMAIL_AWS_SES_REGION:"_system.email.awsSes.region",MODELENCE_EMAIL_AWS_SES_ACCESS_KEY_ID:"_system.email.awsSes.accessKeyId",MODELENCE_EMAIL_AWS_SES_SECRET_ACCESS_KEY:"_system.email.awsSes.secretAccessKey",MODELENCE_EMAIL_SMTP_HOST:"_system.email.smtp.host",MODELENCE_EMAIL_SMTP_PORT:"_system.email.smtp.port",MODELENCE_EMAIL_SMTP_USER:"_system.email.smtp.user",MODELENCE_EMAIL_SMTP_PASS:"_system.email.smtp.pass",MODELENCE_LOG_LEVEL:"_system.log.level",MODELENCE_SITE_URL:"_system.site.url",MODELENCE_ENV:"_system.env",GOOGLE_AUTH_ENABLED:"_system.user.auth.google.enabled",GOOGLE_AUTH_CLIENT_ID:"_system.user.auth.google.clientId",GOOGLE_AUTH_CLIENT_SECRET:"_system.user.auth.google.clientSecret"};function yn(t,e){if(e==="number"){let o=Number(t);if(isNaN(o))throw new Error(`Invalid number value for config: ${t}`);return o}if(e==="boolean"){if(t.toLowerCase()==="true")return true;if(t.toLowerCase()==="false")return false;throw new Error(`Invalid boolean value for config: ${t}`)}return t}function wn(t){let e=[];for(let[o,n]of Object.entries(gn)){let r=process.env[o],i=t[n];if(r){let s=i?.type??"string";e.push({key:n,type:s,value:yn(r,s)});}}return e}async function bn(){if(process.env.MODELENCE_TRACKING_ENABLED!=="false"){let e=process.env.MODELENCE_SERVICE_ENDPOINT??"https://cloud.modelence.com",o=process.env.MODELENCE_ENVIRONMENT_ID,n=await En(),r=await import('./package-
|
|
16
|
+
`);});}async function De(t){let e=_.string().nullish().transform(i=>i??null).parse(t.cookies.authToken||t.body.authToken),o=_.object({screenWidth:_.number(),screenHeight:_.number(),windowWidth:_.number(),windowHeight:_.number(),pixelRatio:_.number(),orientation:_.string().nullable()}).nullish().parse(t.body.clientInfo)??{screenWidth:0,screenHeight:0,windowWidth:0,windowHeight:0,pixelRatio:1,orientation:null},n={ip:on(t),userAgent:t.get("user-agent"),acceptLanguage:t.get("accept-language"),referrer:t.get("referrer"),baseUrl:t.protocol+"://"+t.get("host")};if(!!R()){let{session:i,user:s,roles:a}=await k(e);return {clientInfo:o,connectionInfo:n,session:i,user:s,roles:a}}return {clientInfo:o,connectionInfo:n,session:null,user:null,roles:Q()}}function on(t){let e=t.headers["x-forwarded-for"];if(e)return (Array.isArray(e)?e[0]:e.split(",")[0]).trim();let o=t.ip||t.socket?.remoteAddress;if(o)return o.startsWith("::ffff:")?o.substring(7):o}async function an({modules:t=[],roles:e$1={},defaultRoles:o={},server:n=Ut,migrations:r=[],email:i={},auth:s={},websocket:a={}}){Zt.config(),Zt.config({path:".modelence.env"});let l=!!process.env.MODELENCE_SERVICE_ENDPOINT,d=process.env.MODELENCE_CRON_ENABLED==="true";bn().then(()=>{}).catch(()=>{});let E=[ut,We,xt,Mt,At,It,xe],u=[...E,...t];e(),ln(E),cn(t),Be(e$1,o);let S=pn(u);d$1(S);let I=dn(u),ae=un(u);d&&fn(u);let ce=mn(u);if(at(ce),l){let{configs:z,environmentId:le,appAlias:Ft,environmentAlias:Bt,telemetry:Jt}=await mt({configSchema:S,cronJobsMetadata:d?St():void 0,stores:I});c$1(z),f({environmentId:le,appAlias:Ft,environmentAlias:Bt,telemetry:Jt});}else c$1(wn(S));Ye(i),ot(s),zt({...a,provider:a.provider||Ge});let V=R();if(V&&(await Ve(),hn(I)),d&&Ot(r),V)for(let z of I)z.createIndexes();l&&(await g$1(),ht()),d&&Ct().catch(console.error),await qt(n,{combinedModules:u,channels:ae});}function cn(t){for(let e of t){for(let[o,n]of Object.entries(e.queries))Se(`${e.name}.${o}`,n);for(let[o,n]of Object.entries(e.mutations))Tt(`${e.name}.${o}`,n);}}function ln(t){for(let e of t){for(let[o,n]of Object.entries(e.queries))vt(`${e.name}.${o}`,n);for(let[o,n]of Object.entries(e.mutations))Dt(`${e.name}.${o}`,n);}}function dn(t){return t.flatMap(e=>e.stores)}function un(t){return t.flatMap(e=>e.channels)}function mn(t){return t.flatMap(e=>e.rateLimits)}function pn(t){let e={};for(let o of t)for(let[n,r]of Object.entries(o.configSchema)){let i=`${o.name}.${n}`;if(i in e)throw new Error(`Duplicate config schema key: ${i} (${o.name})`);e[i]=r;}return e}function fn(t){for(let e of t)for(let[o,n]of Object.entries(e.cronJobs))Et(`${e.name}.${o}`,n);}function hn(t){let e=Y();if(!e)throw new Error("Failed to initialize stores: MongoDB client not initialized");for(let o of t)o.init(e);}var gn={MONGODB_URI:"_system.mongodbUri",MODELENCE_AUTH_GOOGLE_ENABLED:"_system.user.auth.google.enabled",MODELENCE_AUTH_GOOGLE_CLIENT_ID:"_system.user.auth.google.clientId",MODELENCE_AUTH_GOOGLE_CLIENT_SECRET:"_system.user.auth.google.clientSecret",MODELENCE_AUTH_GITHUB_ENABLED:"_system.user.auth.github.enabled",MODELENCE_AUTH_GITHUB_CLIENT_ID:"_system.user.auth.github.clientId",MODELENCE_AUTH_GITHUB_CLIENT_SECRET:"_system.user.auth.github.clientSecret",MODELENCE_AUTH_GITHUB_CLIENT_SCOPES:"_system.user.auth.github.scopes",MODELENCE_EMAIL_RESEND_API_KEY:"_system.email.resend.apiKey",MODELENCE_EMAIL_AWS_SES_REGION:"_system.email.awsSes.region",MODELENCE_EMAIL_AWS_SES_ACCESS_KEY_ID:"_system.email.awsSes.accessKeyId",MODELENCE_EMAIL_AWS_SES_SECRET_ACCESS_KEY:"_system.email.awsSes.secretAccessKey",MODELENCE_EMAIL_SMTP_HOST:"_system.email.smtp.host",MODELENCE_EMAIL_SMTP_PORT:"_system.email.smtp.port",MODELENCE_EMAIL_SMTP_USER:"_system.email.smtp.user",MODELENCE_EMAIL_SMTP_PASS:"_system.email.smtp.pass",MODELENCE_LOG_LEVEL:"_system.log.level",MODELENCE_SITE_URL:"_system.site.url",MODELENCE_ENV:"_system.env",GOOGLE_AUTH_ENABLED:"_system.user.auth.google.enabled",GOOGLE_AUTH_CLIENT_ID:"_system.user.auth.google.clientId",GOOGLE_AUTH_CLIENT_SECRET:"_system.user.auth.google.clientSecret"};function yn(t,e){if(e==="number"){let o=Number(t);if(isNaN(o))throw new Error(`Invalid number value for config: ${t}`);return o}if(e==="boolean"){if(t.toLowerCase()==="true")return true;if(t.toLowerCase()==="false")return false;throw new Error(`Invalid boolean value for config: ${t}`)}return t}function wn(t){let e=[];for(let[o,n]of Object.entries(gn)){let r=process.env[o],i=t[n];if(r){let s=i?.type??"string";e.push({key:n,type:s,value:yn(r,s)});}}return e}async function bn(){if(process.env.MODELENCE_TRACKING_ENABLED!=="false"){let e=process.env.MODELENCE_SERVICE_ENDPOINT??"https://cloud.modelence.com",o=process.env.MODELENCE_ENVIRONMENT_ID,n=await En(),r=await import('./package-V6P5ZO4M.js');await fetch(`${e}/api/track/app-start`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({projectName:n.name,version:r.default.version,localHostname:To.hostname(),environmentId:o})});}}async function En(){try{let t=Te.join(process.cwd(),"package.json"),e=await nn.readFile(t,"utf-8");return {name:JSON.parse(e).name||"unknown"}}catch{return {name:"unknown"}}}async function Wt(t){await v.deleteMany({userId:t}),await C.deleteMany({userId:t});}async function Sn(t){await Wt(t),await p.updateOne(t,{$set:{status:"disabled",disabledAt:new Date}});}async function xn(t){await Wt(t),await p.updateOne({_id:t},{$set:{handle:`deleted-${t}-${randomUUID()}`,status:"deleted",deletedAt:new Date,authMethods:{},emails:[]}});}var Re=class{constructor(e,o){this.category=e,this.canAccessChannel=o||null;}broadcast(e,o){let n=se().provider;if(!n){j$1("Websockets provider should be added to startApp",{});return}n.broadcast({category:this.category,id:e,data:o});}};function Tn(t){if(!w().provider)throw new Error("Email provider is not configured, see https://docs.modelence.com/email for more details.");return w().provider?.sendEmail(t)}
|
|
17
17
|
export{b as Module,Re as ServerChannel,y as Store,k as authenticate,M as consumeRateLimit,Se as createQuery,p as dbUsers,xn as deleteUser,Sn as disableUser,c as schema,Tn as sendEmail,an as startApp};//# sourceMappingURL=server.js.map
|
|
18
18
|
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,543 @@
|
|
|
1
|
+
import './index-CwdohC5n.js';
|
|
2
|
+
import { P as Permission, d as Session, e as UserInfo } from './types-Ds1ESQSs.js';
|
|
3
|
+
import * as mongodb from 'mongodb';
|
|
4
|
+
import { WithId, IndexDescription, SearchIndexDescription, MongoClient, Collection, Filter, FindOptions, ObjectId, Document, OptionalUnlessRequiredId, InsertOneResult, InsertManyResult, UpdateFilter, UpdateResult, ClientSession, DeleteResult, AggregateOptions, AggregationCursor, AnyBulkWriteOperation, BulkWriteResult } from 'mongodb';
|
|
5
|
+
import { z, ZodNumber, ZodArray } from 'zod';
|
|
6
|
+
import { Request, Response, NextFunction } from 'express';
|
|
7
|
+
|
|
8
|
+
type EmailAttachment = {
|
|
9
|
+
filename: string;
|
|
10
|
+
content: Buffer | string;
|
|
11
|
+
contentType: string;
|
|
12
|
+
};
|
|
13
|
+
type EmailPayload = {
|
|
14
|
+
from: string;
|
|
15
|
+
to: string | string[];
|
|
16
|
+
subject: string;
|
|
17
|
+
html?: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
cc?: string | string[];
|
|
20
|
+
bcc?: string | string[];
|
|
21
|
+
replyTo?: string | string[];
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
attachments?: EmailAttachment[];
|
|
24
|
+
} & ({
|
|
25
|
+
html: string;
|
|
26
|
+
} | {
|
|
27
|
+
text: string;
|
|
28
|
+
});
|
|
29
|
+
interface EmailProvider {
|
|
30
|
+
sendEmail(data: EmailPayload): Promise<void>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type ClientInfo = {
|
|
34
|
+
screenWidth: number;
|
|
35
|
+
screenHeight: number;
|
|
36
|
+
windowWidth: number;
|
|
37
|
+
windowHeight: number;
|
|
38
|
+
pixelRatio: number;
|
|
39
|
+
orientation: string | null;
|
|
40
|
+
};
|
|
41
|
+
type ConnectionInfo = {
|
|
42
|
+
ip?: string;
|
|
43
|
+
userAgent?: string;
|
|
44
|
+
acceptLanguage?: string;
|
|
45
|
+
referrer?: string;
|
|
46
|
+
baseUrl?: string;
|
|
47
|
+
};
|
|
48
|
+
type Context = {
|
|
49
|
+
session: Session | null;
|
|
50
|
+
user: UserInfo | null;
|
|
51
|
+
roles: string[];
|
|
52
|
+
clientInfo: ClientInfo;
|
|
53
|
+
connectionInfo: ConnectionInfo;
|
|
54
|
+
};
|
|
55
|
+
type Args = Record<string, unknown>;
|
|
56
|
+
type Handler<T = unknown> = (args: Args, context: Context) => Promise<T> | T;
|
|
57
|
+
type MethodType = 'query' | 'mutation';
|
|
58
|
+
type MethodDefinition<T = unknown> = {
|
|
59
|
+
permissions?: Permission[];
|
|
60
|
+
handler: Handler<T>;
|
|
61
|
+
} | Handler<T>;
|
|
62
|
+
type Method<T = unknown> = {
|
|
63
|
+
type: MethodType;
|
|
64
|
+
name: string;
|
|
65
|
+
permissions: Permission[];
|
|
66
|
+
handler: Handler<T>;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type RateLimitType = 'ip' | 'user';
|
|
70
|
+
type RateLimitRule = {
|
|
71
|
+
/** Logical action being limited, e.g. "signup" */
|
|
72
|
+
bucket: string;
|
|
73
|
+
/** Identifier type of the actor this rule applies to */
|
|
74
|
+
type: RateLimitType;
|
|
75
|
+
/** Time window size in milliseconds */
|
|
76
|
+
window: number;
|
|
77
|
+
/** Maximum allowed hits within the window */
|
|
78
|
+
limit: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type CronJobHandler = () => Promise<void>;
|
|
82
|
+
type CronJob = {
|
|
83
|
+
alias: string;
|
|
84
|
+
params: {
|
|
85
|
+
description: string;
|
|
86
|
+
interval: number;
|
|
87
|
+
timeout: number;
|
|
88
|
+
};
|
|
89
|
+
handler: CronJobHandler;
|
|
90
|
+
state: {
|
|
91
|
+
startTs?: number;
|
|
92
|
+
scheduledRunTs?: number;
|
|
93
|
+
isRunning: boolean;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
type CronJobInputParams = {
|
|
97
|
+
description?: string;
|
|
98
|
+
interval: number;
|
|
99
|
+
timeout?: number;
|
|
100
|
+
handler: CronJobHandler;
|
|
101
|
+
};
|
|
102
|
+
type CronJobMetadata = {
|
|
103
|
+
alias: string;
|
|
104
|
+
description: string;
|
|
105
|
+
interval: number;
|
|
106
|
+
timeout: number;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
interface SerializedModelSchema {
|
|
110
|
+
[key: string]: SerializedSchema | (SerializedSchema | SerializedModelSchema)[] | SerializedModelSchema | 'v2';
|
|
111
|
+
}
|
|
112
|
+
type BaseSerializedSchema = {
|
|
113
|
+
type: 'string';
|
|
114
|
+
} | {
|
|
115
|
+
type: 'number';
|
|
116
|
+
} | {
|
|
117
|
+
type: 'boolean';
|
|
118
|
+
} | {
|
|
119
|
+
type: 'date';
|
|
120
|
+
} | {
|
|
121
|
+
type: 'array';
|
|
122
|
+
items: SerializedSchema;
|
|
123
|
+
} | {
|
|
124
|
+
type: 'object';
|
|
125
|
+
items: Record<string, SerializedSchema>;
|
|
126
|
+
} | {
|
|
127
|
+
type: 'enum';
|
|
128
|
+
items: readonly string[];
|
|
129
|
+
} | {
|
|
130
|
+
type: 'union';
|
|
131
|
+
items: SerializedSchema[];
|
|
132
|
+
} | {
|
|
133
|
+
type: 'custom';
|
|
134
|
+
typeName: string;
|
|
135
|
+
};
|
|
136
|
+
type SerializedSchema = BaseSerializedSchema | (BaseSerializedSchema & {
|
|
137
|
+
optional: true;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Helper type to preserve method types when extending a store.
|
|
142
|
+
* Maps each method to work with the extended schema while preserving signatures.
|
|
143
|
+
* @internal
|
|
144
|
+
*/
|
|
145
|
+
type PreserveMethodsForExtendedSchema<TBaseMethods extends Record<string, (...args: never[]) => unknown>, TExtendedSchema extends ModelSchema> = {
|
|
146
|
+
[K in keyof TBaseMethods]: TBaseMethods[K] extends (this: any, ...args: infer Args) => infer Return ? (this: WithId<InferDocumentType<TExtendedSchema>> & any, ...args: Args) => Return : never;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* The Store class provides a type-safe interface for MongoDB collections with built-in schema validation and helper methods.
|
|
150
|
+
*
|
|
151
|
+
* @category Store
|
|
152
|
+
* @typeParam TSchema - The document schema type
|
|
153
|
+
* @typeParam TMethods - Custom methods that will be added to documents
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const dbTodos = new Store('todos', {
|
|
158
|
+
* schema: {
|
|
159
|
+
* title: schema.string(),
|
|
160
|
+
* completed: schema.boolean(),
|
|
161
|
+
* dueDate: schema.date().optional(),
|
|
162
|
+
* userId: schema.userId(),
|
|
163
|
+
* },
|
|
164
|
+
* methods: {
|
|
165
|
+
* isOverdue() {
|
|
166
|
+
* return this.dueDate < new Date();
|
|
167
|
+
* }
|
|
168
|
+
* }
|
|
169
|
+
* });
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
declare class Store<TSchema extends ModelSchema, TMethods extends Record<string, (this: WithId<InferDocumentType<TSchema>> & TMethods, ...args: any[]) => any>> {
|
|
173
|
+
/** @internal */
|
|
174
|
+
readonly _type: InferDocumentType<TSchema>;
|
|
175
|
+
/** @internal */
|
|
176
|
+
readonly _rawDoc: WithId<this['_type']>;
|
|
177
|
+
/** @internal */
|
|
178
|
+
readonly _doc: this['_rawDoc'] & TMethods;
|
|
179
|
+
readonly Doc: this['_doc'];
|
|
180
|
+
private name;
|
|
181
|
+
private readonly schema;
|
|
182
|
+
private readonly methods?;
|
|
183
|
+
private readonly indexes;
|
|
184
|
+
private readonly searchIndexes;
|
|
185
|
+
private collection?;
|
|
186
|
+
private client?;
|
|
187
|
+
/**
|
|
188
|
+
* Creates a new Store instance
|
|
189
|
+
*
|
|
190
|
+
* @param name - The collection name in MongoDB
|
|
191
|
+
* @param options - Store configuration
|
|
192
|
+
*/
|
|
193
|
+
constructor(name: string, options: {
|
|
194
|
+
/** Document schema using Modelence schema types */
|
|
195
|
+
schema: TSchema;
|
|
196
|
+
/** Custom methods to add to documents */
|
|
197
|
+
methods?: TMethods;
|
|
198
|
+
/** MongoDB indexes to create */
|
|
199
|
+
indexes: IndexDescription[];
|
|
200
|
+
/** MongoDB Atlas Search */
|
|
201
|
+
searchIndexes?: SearchIndexDescription[];
|
|
202
|
+
});
|
|
203
|
+
getName(): string;
|
|
204
|
+
/** @internal */
|
|
205
|
+
getSchema(): TSchema;
|
|
206
|
+
/** @internal */
|
|
207
|
+
getSerializedSchema(): SerializedModelSchema;
|
|
208
|
+
/**
|
|
209
|
+
* Extends the store with additional schema fields, indexes, methods, and search indexes.
|
|
210
|
+
* Returns a new Store instance with the extended schema and updated types.
|
|
211
|
+
* Methods from the original store are preserved with updated type signatures.
|
|
212
|
+
*
|
|
213
|
+
* @param config - Additional schema fields, indexes, methods, and search indexes to add
|
|
214
|
+
* @returns A new Store instance with the extended schema
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* // Extend the users collection
|
|
219
|
+
* export const dbUsers = baseUsersCollection.extend({
|
|
220
|
+
* schema: {
|
|
221
|
+
* firstName: schema.string(),
|
|
222
|
+
* lastName: schema.string(),
|
|
223
|
+
* companyId: schema.objectId().optional(),
|
|
224
|
+
* },
|
|
225
|
+
* indexes: [
|
|
226
|
+
* { key: { companyId: 1 } },
|
|
227
|
+
* { key: { lastName: 1, firstName: 1 } },
|
|
228
|
+
* ],
|
|
229
|
+
* methods: {
|
|
230
|
+
* getFullName() {
|
|
231
|
+
* return `${this.firstName} ${this.lastName}`;
|
|
232
|
+
* }
|
|
233
|
+
* }
|
|
234
|
+
* });
|
|
235
|
+
*
|
|
236
|
+
* // Now fully typed with new fields
|
|
237
|
+
* const user = await dbUsers.findOne({ firstName: 'John' });
|
|
238
|
+
* console.log(user?.getFullName());
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
extend<TExtendedSchema extends ModelSchema, TExtendedMethods extends Record<string, (this: WithId<InferDocumentType<TSchema & TExtendedSchema>> & any, ...args: any[]) => any> = Record<string, never>>(config: {
|
|
242
|
+
schema?: TExtendedSchema;
|
|
243
|
+
indexes?: IndexDescription[];
|
|
244
|
+
methods?: TExtendedMethods;
|
|
245
|
+
searchIndexes?: SearchIndexDescription[];
|
|
246
|
+
}): Store<TSchema & TExtendedSchema, PreserveMethodsForExtendedSchema<TMethods, TSchema & TExtendedSchema> & TExtendedMethods>;
|
|
247
|
+
/** @internal */
|
|
248
|
+
init(client: MongoClient): void;
|
|
249
|
+
/** @internal */
|
|
250
|
+
createIndexes(): Promise<void>;
|
|
251
|
+
private wrapDocument;
|
|
252
|
+
/**
|
|
253
|
+
* For convenience, to also allow directy passing a string or ObjectId as the selector
|
|
254
|
+
*/
|
|
255
|
+
private getSelector;
|
|
256
|
+
/** @internal */
|
|
257
|
+
requireCollection(): Collection<this["_type"]>;
|
|
258
|
+
/** @internal */
|
|
259
|
+
requireClient(): MongoClient;
|
|
260
|
+
findOne(query: Filter<this['_type']>, options?: FindOptions): Promise<this["_doc"] | null>;
|
|
261
|
+
requireOne(query: Filter<this['_type']>, options?: FindOptions, errorHandler?: () => Error): Promise<this['_doc']>;
|
|
262
|
+
private find;
|
|
263
|
+
/**
|
|
264
|
+
* Fetches a single document by its ID
|
|
265
|
+
*
|
|
266
|
+
* @param id - The ID of the document to find
|
|
267
|
+
* @returns The document, or null if not found
|
|
268
|
+
*/
|
|
269
|
+
findById(id: string | ObjectId): Promise<this['_doc'] | null>;
|
|
270
|
+
/**
|
|
271
|
+
* Fetches a single document by its ID, or throws an error if not found
|
|
272
|
+
*
|
|
273
|
+
* @param id - The ID of the document to find
|
|
274
|
+
* @param errorHandler - Optional error handler to return a custom error if the document is not found
|
|
275
|
+
* @returns The document
|
|
276
|
+
*/
|
|
277
|
+
requireById(id: string | ObjectId, errorHandler?: () => Error): Promise<this['_doc']>;
|
|
278
|
+
/**
|
|
279
|
+
* Counts the number of documents that match a query
|
|
280
|
+
*
|
|
281
|
+
* @param query - The query to filter documents
|
|
282
|
+
* @returns The number of documents that match the query
|
|
283
|
+
*/
|
|
284
|
+
countDocuments(query: Filter<this['_type']>): Promise<number>;
|
|
285
|
+
/**
|
|
286
|
+
* Fetches multiple documents, equivalent to Node.js MongoDB driver's `find` and `toArray` methods combined.
|
|
287
|
+
*
|
|
288
|
+
* @param query - The query to filter documents
|
|
289
|
+
* @param options - Options
|
|
290
|
+
* @returns The documents
|
|
291
|
+
*/
|
|
292
|
+
fetch(query: Filter<this['_type']>, options?: {
|
|
293
|
+
sort?: Document;
|
|
294
|
+
limit?: number;
|
|
295
|
+
skip?: number;
|
|
296
|
+
}): Promise<this['_doc'][]>;
|
|
297
|
+
/**
|
|
298
|
+
* Inserts a single document
|
|
299
|
+
*
|
|
300
|
+
* @param document - The document to insert
|
|
301
|
+
* @returns The result of the insert operation
|
|
302
|
+
*/
|
|
303
|
+
insertOne(document: OptionalUnlessRequiredId<InferDocumentType<TSchema>>): Promise<InsertOneResult>;
|
|
304
|
+
/**
|
|
305
|
+
* Inserts multiple documents
|
|
306
|
+
*
|
|
307
|
+
* @param documents - The documents to insert
|
|
308
|
+
* @returns The result of the insert operation
|
|
309
|
+
*/
|
|
310
|
+
insertMany(documents: OptionalUnlessRequiredId<InferDocumentType<TSchema>>[]): Promise<InsertManyResult>;
|
|
311
|
+
/**
|
|
312
|
+
* Updates a single document
|
|
313
|
+
*
|
|
314
|
+
* @param selector - The selector to find the document to update
|
|
315
|
+
* @param update - The update to apply to the document
|
|
316
|
+
* @returns The result of the update operation
|
|
317
|
+
*/
|
|
318
|
+
updateOne(selector: Filter<this['_type']> | string | ObjectId, update: UpdateFilter<this['_type']>): Promise<UpdateResult>;
|
|
319
|
+
/**
|
|
320
|
+
* Updates a single document, or inserts it if it doesn't exist
|
|
321
|
+
*
|
|
322
|
+
* @param selector - The selector to find the document to update
|
|
323
|
+
* @param update - The MongoDB modifier to apply to the document
|
|
324
|
+
* @returns The result of the update operation
|
|
325
|
+
*/
|
|
326
|
+
upsertOne(selector: Filter<this['_type']> | string | ObjectId, update: UpdateFilter<this['_type']>): Promise<UpdateResult>;
|
|
327
|
+
/**
|
|
328
|
+
* Updates multiple documents
|
|
329
|
+
*
|
|
330
|
+
* @param selector - The selector to find the documents to update
|
|
331
|
+
* @param update - The MongoDB modifier to apply to the documents
|
|
332
|
+
* @returns The result of the update operation
|
|
333
|
+
*/
|
|
334
|
+
updateMany(selector: Filter<this['_type']>, update: UpdateFilter<this['_type']>, options?: {
|
|
335
|
+
session?: ClientSession;
|
|
336
|
+
}): Promise<UpdateResult>;
|
|
337
|
+
/**
|
|
338
|
+
* Updates multiple documents, or inserts them if they don't exist
|
|
339
|
+
*
|
|
340
|
+
* @param selector - The selector to find the documents to update
|
|
341
|
+
* @param update - The MongoDB modifier to apply to the documents
|
|
342
|
+
* @returns The result of the update operation
|
|
343
|
+
*/
|
|
344
|
+
upsertMany(selector: Filter<this['_type']>, update: UpdateFilter<this['_type']>): Promise<UpdateResult>;
|
|
345
|
+
/**
|
|
346
|
+
* Deletes a single document
|
|
347
|
+
*
|
|
348
|
+
* @param selector - The selector to find the document to delete
|
|
349
|
+
* @returns The result of the delete operation
|
|
350
|
+
*/
|
|
351
|
+
deleteOne(selector: Filter<this['_type']>): Promise<DeleteResult>;
|
|
352
|
+
/**
|
|
353
|
+
* Deletes multiple documents
|
|
354
|
+
*
|
|
355
|
+
* @param selector - The selector to find the documents to delete
|
|
356
|
+
* @returns The result of the delete operation
|
|
357
|
+
*/
|
|
358
|
+
deleteMany(selector: Filter<this['_type']>): Promise<DeleteResult>;
|
|
359
|
+
/**
|
|
360
|
+
* Aggregates documents using MongoDB's aggregation framework
|
|
361
|
+
*
|
|
362
|
+
* @param pipeline - The aggregation pipeline
|
|
363
|
+
* @param options - Optional options
|
|
364
|
+
* @returns The aggregation cursor
|
|
365
|
+
*/
|
|
366
|
+
aggregate(pipeline: Document[], options?: AggregateOptions): AggregationCursor<Document>;
|
|
367
|
+
/**
|
|
368
|
+
* Performs a bulk write operation on the collection
|
|
369
|
+
*
|
|
370
|
+
* @param operations - The operations to perform
|
|
371
|
+
* @returns The result of the bulk write operation
|
|
372
|
+
*/
|
|
373
|
+
bulkWrite(operations: AnyBulkWriteOperation<this['_type']>[]): Promise<BulkWriteResult>;
|
|
374
|
+
/**
|
|
375
|
+
* Returns the raw MongoDB database instance for advanced operations
|
|
376
|
+
* @returns The MongoDB database instance
|
|
377
|
+
* @throws Error if the store is not provisioned
|
|
378
|
+
*/
|
|
379
|
+
getDatabase(): mongodb.Db;
|
|
380
|
+
/**
|
|
381
|
+
* Returns the raw MongoDB collection instance for advanced operations
|
|
382
|
+
* @returns The MongoDB collection instance
|
|
383
|
+
* @throws Error if the store is not provisioned
|
|
384
|
+
*/
|
|
385
|
+
rawCollection(): Collection<this["_type"]>;
|
|
386
|
+
/**
|
|
387
|
+
* Renames an existing collection to this store's name, used for migrations
|
|
388
|
+
* @param oldName - The previous name of the collection
|
|
389
|
+
* @throws Error if the old collection doesn't exist or if this store's collection already exists
|
|
390
|
+
*/
|
|
391
|
+
renameFrom(oldName: string, options?: {
|
|
392
|
+
session?: ClientSession;
|
|
393
|
+
}): Promise<void>;
|
|
394
|
+
/**
|
|
395
|
+
* Performs a vector similarity search using MongoDB Atlas Vector Search
|
|
396
|
+
*
|
|
397
|
+
* @param params - Vector search parameters
|
|
398
|
+
* @param params.field - The field name containing the vector embeddings
|
|
399
|
+
* @param params.embedding - The query vector to search for
|
|
400
|
+
* @param params.numCandidates - Number of nearest neighbors to consider (default: 100)
|
|
401
|
+
* @param params.limit - Maximum number of results to return (default: 10)
|
|
402
|
+
* @param params.projection - Additional fields to include in the results
|
|
403
|
+
* @param params.indexName - Name of index (default: field + VectorSearch)
|
|
404
|
+
* @returns An aggregation cursor with search results and scores
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* const results = await store.vectorSearch({
|
|
409
|
+
* field: 'embedding',
|
|
410
|
+
* embedding: [0.1, 0.2, 0.3, ...],
|
|
411
|
+
* numCandidates: 100,
|
|
412
|
+
* limit: 10,
|
|
413
|
+
* projection: { title: 1, description: 1 }
|
|
414
|
+
* });
|
|
415
|
+
* ```
|
|
416
|
+
*/
|
|
417
|
+
vectorSearch({ field, embedding, numCandidates, limit, projection, indexName, }: {
|
|
418
|
+
field: string;
|
|
419
|
+
embedding: number[];
|
|
420
|
+
numCandidates?: number;
|
|
421
|
+
limit?: number;
|
|
422
|
+
projection?: Document;
|
|
423
|
+
indexName?: string;
|
|
424
|
+
}): Promise<AggregationCursor<Document>>;
|
|
425
|
+
/**
|
|
426
|
+
* Creates a MongoDB Atlas Vector Search index definition
|
|
427
|
+
*
|
|
428
|
+
* @param params - Vector index parameters
|
|
429
|
+
* @param params.field - The field name to create the vector index on
|
|
430
|
+
* @param params.dimensions - The number of dimensions in the vector embeddings
|
|
431
|
+
* @param params.similarity - The similarity metric to use (default: 'cosine')
|
|
432
|
+
* @param params.indexName - Name of index (default: field + VectorSearch)
|
|
433
|
+
* @returns A search index description object
|
|
434
|
+
*
|
|
435
|
+
* @example
|
|
436
|
+
* ```ts
|
|
437
|
+
* const store = new Store('documents', {
|
|
438
|
+
* schema: {
|
|
439
|
+
* title: schema.string(),
|
|
440
|
+
* embedding: schema.array(schema.number()),
|
|
441
|
+
* },
|
|
442
|
+
* indexes: [],
|
|
443
|
+
* searchIndexes: [
|
|
444
|
+
* Store.vectorIndex({
|
|
445
|
+
* field: 'embedding',
|
|
446
|
+
* dimensions: 1536,
|
|
447
|
+
* similarity: 'cosine'
|
|
448
|
+
* })
|
|
449
|
+
* ]
|
|
450
|
+
* });
|
|
451
|
+
* ```
|
|
452
|
+
*/
|
|
453
|
+
static vectorIndex({ field, dimensions, similarity, indexName, }: {
|
|
454
|
+
field: string;
|
|
455
|
+
dimensions: number;
|
|
456
|
+
similarity?: 'cosine' | 'euclidean' | 'dotProduct';
|
|
457
|
+
indexName?: string;
|
|
458
|
+
}): {
|
|
459
|
+
type: string;
|
|
460
|
+
name: string;
|
|
461
|
+
definition: {
|
|
462
|
+
fields: {
|
|
463
|
+
type: string;
|
|
464
|
+
path: string;
|
|
465
|
+
numDimensions: number;
|
|
466
|
+
similarity: "cosine" | "euclidean" | "dotProduct";
|
|
467
|
+
}[];
|
|
468
|
+
};
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
type ObjectTypeDefinition = {
|
|
473
|
+
[key: string]: SchemaTypeDefinition;
|
|
474
|
+
};
|
|
475
|
+
type SingularSchemaTypeDefinition = z.ZodType | ObjectTypeDefinition;
|
|
476
|
+
type SchemaTypeDefinition = SingularSchemaTypeDefinition | Array<SingularSchemaTypeDefinition>;
|
|
477
|
+
type ModelSchema = {
|
|
478
|
+
[key: string]: SchemaTypeDefinition;
|
|
479
|
+
};
|
|
480
|
+
type InferDocumentType<T extends SchemaTypeDefinition> = {
|
|
481
|
+
[K in keyof T as T[K] extends z.ZodOptional<z.ZodTypeAny> ? K : never]?: T[K] extends z.ZodType ? z.infer<T[K]> : never;
|
|
482
|
+
} & {
|
|
483
|
+
[K in keyof T as T[K] extends z.ZodOptional<z.ZodTypeAny> ? never : K]: T[K] extends z.ZodType ? z.infer<T[K]> : T[K] extends Array<infer ElementType extends SchemaTypeDefinition> ? Array<InferDocumentType<ElementType>> : T[K] extends ObjectTypeDefinition ? InferDocumentType<T[K]> : never;
|
|
484
|
+
};
|
|
485
|
+
declare const schema: {
|
|
486
|
+
readonly string: (params?: z.RawCreateParams & {
|
|
487
|
+
coerce?: true;
|
|
488
|
+
}) => z.ZodString;
|
|
489
|
+
readonly number: (params?: z.RawCreateParams & {
|
|
490
|
+
coerce?: boolean;
|
|
491
|
+
}) => ZodNumber;
|
|
492
|
+
readonly date: (params?: z.RawCreateParams & {
|
|
493
|
+
coerce?: boolean;
|
|
494
|
+
}) => z.ZodDate;
|
|
495
|
+
readonly boolean: (params?: z.RawCreateParams & {
|
|
496
|
+
coerce?: boolean;
|
|
497
|
+
}) => z.ZodBoolean;
|
|
498
|
+
readonly array: <El extends z.ZodTypeAny>(schema: El, params?: z.RawCreateParams) => ZodArray<El>;
|
|
499
|
+
readonly object: <Shape extends z.ZodRawShape>(shape: Shape, params?: z.RawCreateParams) => z.ZodObject<Shape, "strip", z.ZodTypeAny, z.objectOutputType<Shape, z.ZodTypeAny, "strip">, z.objectInputType<Shape, z.ZodTypeAny, "strip">>;
|
|
500
|
+
readonly enum: {
|
|
501
|
+
<U extends string, T extends Readonly<[U, ...U[]]>>(values: T, params?: z.RawCreateParams): z.ZodEnum<z.Writeable<T>>;
|
|
502
|
+
<U extends string, T extends [U, ...U[]]>(values: T, params?: z.RawCreateParams): z.ZodEnum<T>;
|
|
503
|
+
};
|
|
504
|
+
readonly embedding: () => ZodArray<ZodNumber>;
|
|
505
|
+
readonly objectId: () => z.ZodType<ObjectId>;
|
|
506
|
+
readonly userId: () => z.ZodType<ObjectId>;
|
|
507
|
+
readonly ref: <T extends ModelSchema>(_collection: string | Store<T, InferDocumentType<T>>) => z.ZodType<ObjectId>;
|
|
508
|
+
readonly union: <Options extends Readonly<[z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]]>>(types: Options, params?: z.RawCreateParams) => z.ZodUnion<Options>;
|
|
509
|
+
readonly infer: <T extends SchemaTypeDefinition>(_schema: T) => InferDocumentType<T>;
|
|
510
|
+
};
|
|
511
|
+
declare namespace schema {
|
|
512
|
+
type infer<T extends SchemaTypeDefinition> = InferDocumentType<T>;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
type HttpMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options' | 'head' | 'all' | 'use';
|
|
516
|
+
type RouteParams<T = unknown> = {
|
|
517
|
+
query: Record<string, string>;
|
|
518
|
+
body: T;
|
|
519
|
+
params: Record<string, string>;
|
|
520
|
+
headers: Record<string, string>;
|
|
521
|
+
cookies: Record<string, string>;
|
|
522
|
+
req: Request;
|
|
523
|
+
res: Response;
|
|
524
|
+
next: NextFunction;
|
|
525
|
+
};
|
|
526
|
+
type RouteResponse<T = unknown> = {
|
|
527
|
+
data?: T;
|
|
528
|
+
status?: number;
|
|
529
|
+
headers?: Record<string, string>;
|
|
530
|
+
redirect?: string;
|
|
531
|
+
} | null;
|
|
532
|
+
type RouteHandler<T = unknown> = (params: RouteParams, context: Pick<Context, 'session' | 'user'>) => Promise<RouteResponse<T>> | RouteResponse<T>;
|
|
533
|
+
type RouteHandlers = {
|
|
534
|
+
[key in HttpMethod]?: RouteHandler;
|
|
535
|
+
};
|
|
536
|
+
type RouteDefinition = {
|
|
537
|
+
path: string;
|
|
538
|
+
handlers: RouteHandlers;
|
|
539
|
+
errorHandler?: RouteHandler;
|
|
540
|
+
};
|
|
541
|
+
type ExpressHandler = (req: Request, res: Response) => Promise<void> | void;
|
|
542
|
+
|
|
543
|
+
export { type Args as A, type CronJobInputParams as C, type EmailProvider as E, type HttpMethod as H, type InferDocumentType as I, type MethodDefinition as M, type RouteDefinition as R, Store as S, type RateLimitRule as a, type ConnectionInfo as b, type RateLimitType as c, type EmailPayload as d, type RouteHandler as e, type RouteParams as f, type RouteResponse as g, type EmailAttachment as h, type ClientInfo as i, type Context as j, type Handler as k, type MethodType as l, type Method as m, type CronJob as n, type CronJobMetadata as o, type ModelSchema as p, type RouteHandlers as q, type ExpressHandler as r, schema as s };
|
|
@@ -16,6 +16,7 @@ type AppConfig = {
|
|
|
16
16
|
type ConfigSchema = {
|
|
17
17
|
[key: string]: ConfigParams;
|
|
18
18
|
};
|
|
19
|
+
type Configs = Record<ConfigKey, AppConfig>;
|
|
19
20
|
type ValueType<T> = T extends 'number' ? number : T extends 'string' ? string : T extends 'text' ? string : T extends 'boolean' ? boolean : T extends 'secret' ? string : never;
|
|
20
21
|
|
|
21
22
|
type User = Document;
|
|
@@ -27,6 +28,7 @@ type UserInfo = {
|
|
|
27
28
|
requireRole: (role: string) => void;
|
|
28
29
|
};
|
|
29
30
|
type Role = string;
|
|
31
|
+
type DefaultRoles = Record<'authenticated' | 'unauthenticated', Role | null>;
|
|
30
32
|
type Session = {
|
|
31
33
|
authToken: string;
|
|
32
34
|
expiresAt: Date;
|
|
@@ -101,4 +103,4 @@ interface WebsocketClientProvider {
|
|
|
101
103
|
}): void;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
export { type AppConfig as A, type ConfigSchema as C, type Permission as P, type RoleDefinition as R,
|
|
106
|
+
export { type AppConfig as A, type ConfigSchema as C, type DefaultRoles as D, type Permission as P, type RoleDefinition as R, ServerChannel as S, type User as U, type WebsocketServerProvider as W, type WebsocketClientProvider as a, type ConfigKey as b, ClientChannel as c, type Session as d, type UserInfo as e, type Role as f, type ConfigType as g, type Configs as h };
|