modelence 0.5.14-dev.0 → 0.6.0-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,44 @@
17
17
  [![Discord](https://img.shields.io/discord/1386659657535455253?label=Discord&logo=discord&logoColor=white&labelColor=5865F2&cacheSeconds=30)](https://discord.gg/ghxu5PDnkZ)
18
18
  </div>
19
19
 
20
+ ## 🚀 Showcase
21
+
22
+ See what you can build in just a few hours with Modelence's batteries-included approach:
23
+
24
+ <div align="center">
25
+ <table>
26
+ <tr>
27
+ <td align="center">
28
+ <a href="https://finchat.modelence.app/" target="_blank">
29
+ <img src="/assets/finchat-preview.png" alt="FinChat" style="object-fit: cover; border-radius: 12px; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); transition: transform 0.2s ease, box-shadow 0.2s ease;" />
30
+ </a>
31
+ <br />
32
+ <strong>FinChat</strong>
33
+ <br />
34
+ <small>AI-powered financial chat assistant</small>
35
+ </td>
36
+ <td align="center">
37
+ <a href="https://smartrepos.modelence.app/" target="_blank">
38
+ <img src="/assets/smartrepos-preview.png" alt="SmartRepos" style="object-fit: cover; border-radius: 12px; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); transition: transform 0.2s ease, box-shadow 0.2s ease;" />
39
+ </a>
40
+ <br />
41
+ <strong>SmartRepos</strong>
42
+ <br />
43
+ <small>Intelligent repository management</small>
44
+ </td>
45
+ <td align="center">
46
+ <a href="https://typesonic.modelence.app/" target="_blank">
47
+ <img src="/assets/typesonic-preview.png" alt="TypeSonic" style="object-fit: cover; border-radius: 12px; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15); transition: transform 0.2s ease, box-shadow 0.2s ease;" />
48
+ </a>
49
+ <br />
50
+ <strong>TypeSonic</strong>
51
+ <br />
52
+ <small>Competitive typing racing app</small>
53
+ </td>
54
+ </tr>
55
+ </table>
56
+ </div>
57
+
20
58
  ## Getting Started
21
59
  Modelence is an all-in-one TypeScript framework for startups shipping production apps, with the mission to eliminate all boilerplate for standard features that modern web applications need, like authentication, database setup, cron jobs, AI observability, email and more.
22
60
 
@@ -0,0 +1,2 @@
1
+ import {a as a$1}from'./chunk-R7MPLJMA.js';import {b,h as h$1}from'./chunk-2QLNYYBR.js';import {ObjectId}from'mongodb';import {z as z$1}from'zod';import {randomBytes}from'crypto';import {Server}from'socket.io';var d=class extends Error{},w=class extends d{constructor(t){super(t);this.status=401;this.name="AuthError";}},x=class extends d{constructor(t){super(t);this.status=400;this.name="ValidationError";}},D=class extends d{constructor(t){super(t);this.status=429;this.name="RateLimitError";}};var h=class{constructor(e,{stores:t=[],queries:n={},mutations:s={},routes:c=[],cronJobs:b={},configSchema:k={},rateLimits:A=[],rooms:j=[]}){this.name=e,this.stores=t,this.queries=n,this.mutations=s,this.routes=c,this.cronJobs=b,this.configSchema=k,this.rateLimits=A,this.rooms=j;}};var a=class{constructor(e,t){this.name=e,this.schema=t.schema,this.methods=t.methods,this.indexes=t.indexes;}getName(){return this.name}getSchema(){return this.schema}init(e){if(this.collection)throw new Error(`Collection ${this.name} is already initialized`);this.client=e,this.collection=this.client.db().collection(this.name);}async createIndexes(){this.indexes.length>0&&await this.requireCollection().createIndexes(this.indexes);}wrapDocument(e){return this.methods?Object.create(null,Object.getOwnPropertyDescriptors({...e,...this.methods})):e}getSelector(e){return typeof e=="string"?{_id:new ObjectId(e)}:e instanceof ObjectId?{_id:e}:e}requireCollection(){if(!this.collection)throw new Error(`Collection ${this.name} is not provisioned`);return this.collection}requireClient(){if(!this.client)throw new Error("Database is not connected");return this.client}async findOne(e,t){let n=await this.requireCollection().findOne(e,t);return n?this.wrapDocument(n):null}async requireOne(e,t,n){let s=await this.findOne(e,t);if(!s)throw n?n():new Error(`Record not found in ${this.name}`);return s}find(e,t){let n=this.requireCollection().find(e);return t?.sort&&n.sort(t.sort),t?.limit&&n.limit(t.limit),t?.skip&&n.skip(t.skip),n}async findById(e){let t=typeof e=="string"?{_id:new ObjectId(e)}:{_id:e};return await this.findOne(t)}async requireById(e,t){let n=await this.findById(e);if(!n)throw t?t():new Error(`Record with id ${e} not found in ${this.name}`);return n}countDocuments(e){return this.requireCollection().countDocuments(e)}async fetch(e,t){return (await this.find(e,t).toArray()).map(this.wrapDocument.bind(this))}async insertOne(e){return await this.requireCollection().insertOne(e)}async insertMany(e){return await this.requireCollection().insertMany(e)}async updateOne(e,t){return await this.requireCollection().updateOne(this.getSelector(e),t)}async upsertOne(e,t){return await this.requireCollection().updateOne(this.getSelector(e),t,{upsert:true})}async updateMany(e,t,n){return await this.requireCollection().updateMany(e,t,n)}async upsertMany(e,t){return await this.requireCollection().updateMany(e,t,{upsert:true})}async deleteOne(e){return await this.requireCollection().deleteOne(e)}async deleteMany(e){return await this.requireCollection().deleteMany(e)}aggregate(e,t){return this.requireCollection().aggregate(e,t)}bulkWrite(e){return this.requireCollection().bulkWrite(e)}getDatabase(){return this.requireClient().db()}rawCollection(){return this.requireCollection()}async renameFrom(e,t){let n=this.getDatabase();if(!this.collection||!n)throw new Error(`Store ${this.name} is not provisioned`);if((await n.listCollections({name:e}).toArray()).length===0)throw new Error(`Collection ${e} not found`);if((await n.listCollections({name:this.name}).toArray()).length>0)throw new Error(`Collection ${this.name} already exists`);await n.collection(e).rename(this.name,t);}};var v=z$1.string.bind(z$1),q=z$1.number.bind(z$1),P=z$1.date.bind(z$1),M=z$1.boolean.bind(z$1),F=z$1.array.bind(z$1),U=z$1.object.bind(z$1),z=z$1.enum.bind(z$1),o={string:v,number:q,date:P,boolean:M,array:F,object:U,enum:z,objectId(){return z$1.instanceof(ObjectId)},userId(){return z$1.instanceof(ObjectId)},ref(i){return z$1.instanceof(ObjectId)},union:z$1.union.bind(z$1),infer(i){return {}}};var T=new a("_modelenceUsers",{schema:{handle:o.string(),emails:o.array(o.object({address:o.string(),verified:o.boolean()})).optional(),createdAt:o.date(),authMethods:o.object({password:o.object({hash:o.string()}).optional(),google:o.object({id:o.string()}).optional()})},indexes:[{key:{handle:1},unique:true,collation:{locale:"en",strength:2}}]}),Se=new a("_modelenceDisposableEmailDomains",{schema:{domain:o.string(),addedAt:o.date()},indexes:[{key:{domain:1},unique:true}]}),we=new a("_modelenceEmailVerificationTokens",{schema:{userId:o.objectId(),email:o.string().optional(),token:o.string(),createdAt:o.date(),expiresAt:o.date()},indexes:[{key:{token:1},unique:true},{key:{expiresAt:1},expireAfterSeconds:0}]}),xe=new a("_modelenceResetPasswordTokens",{schema:{userId:o.objectId(),token:o.string(),createdAt:o.date(),expiresAt:o.date()},indexes:[{key:{token:1},unique:true},{key:{expiresAt:1},expireAfterSeconds:0}]});var l=new a("_modelenceSessions",{schema:{authToken:o.string(),createdAt:o.date(),expiresAt:o.date(),userId:o.userId().nullable()},indexes:[{key:{authToken:1},unique:true},{key:{expiresAt:1}}]});async function R(i){let e=i?await l.findOne({authToken:i}):null;return e?{authToken:String(e.authToken),expiresAt:new Date(e.expiresAt),userId:e.userId??null}:await $()}async function ke(i,e){await l.updateOne({authToken:i},{$set:{userId:e}});}async function Ae(i){await l.updateOne({authToken:i},{$set:{userId:null}});}async function $(i=null){let e=randomBytes(32).toString("base64url"),t=Date.now(),n=new Date(t+a$1.days(7));return await l.insertOne({authToken:e,createdAt:new Date(t),expiresAt:n,userId:i}),{authToken:e,expiresAt:n,userId:i}}async function K(i){let e=Date.now(),t=new Date(e+a$1.days(7));await l.updateOne({authToken:i.authToken},{$set:{lastActiveDate:new Date(e),expiresAt:t}});}var je=new h("_system.session",{stores:[l],mutations:{init:async function(i,{session:e,user:t}){return {session:e,user:t,configs:b()}},heartbeat:async function(i,{session:e}){e&&await K(e);}}});var I=new Map,u={authenticated:null,unauthenticated:null};function qe(i,e){u.authenticated=e.authenticated,u.unauthenticated=e.unauthenticated;for(let[t,n]of Object.entries(i))I.set(t,n);}function O(){return u.unauthenticated?[u.unauthenticated]:[]}function C(){return u.authenticated?[u.authenticated]:[]}function Pe(i,e){let t=e.find(n=>!B(i,n));if(t)throw new Error(`Access denied - missing permission: '${t}'`)}function B(i,e){for(let t of i){let n=I.get(t);if(n&&n.permissions.includes(e))return true}return false}async function _(i){let e=await R(i),t=e.userId?await T.findOne({_id:new ObjectId(e.userId)}):null,n=t?{id:t._id.toString(),handle:t.handle}:null,s=n?C():O();return {user:n,session:e,roles:s}}var m=null;function Je(i,e){return m=new Server(i,{cors:{origin:"*",methods:["GET","POST"]}}),m.use(async(t,n)=>{let s=t.handshake.auth.token;try{t.data=await _(s);}finally{n();}}),m.on("connection",t=>{h$1("Socket.IO client connected",{source:"websocket",socketId:t.id}),t.on("disconnect",()=>{h$1("Socket.IO client disconnected",{source:"websocket",socketId:t.id});}),t.on("joinRoom",async n=>{let[s]=n.split(":");for(let c of e)c.roomCategory===s&&(!c.canAccessRoom||await c.canAccessRoom(t.data))&&t.join(n);t.join(n),console.log(`User ${t.id} joined room ${n}`),t.emit("joinedRoom",n);}),t.on("leaveRoom",n=>{t.leave(n),console.log(`User ${t.id} left room ${n}`),t.emit("leftRoom",n);});}),h$1("Socket.IO server initialized",{source:"websocket"}),m}function Le(){return m}export{d as a,w as b,x as c,D as d,h as e,a as f,o as g,ke as h,Ae as i,$ as j,je as k,T as l,Se as m,we as n,xe as o,qe as p,O as q,Pe as r,_ as s,Je as t,Le as u};//# sourceMappingURL=chunk-5TLHWYXF.js.map
2
+ //# sourceMappingURL=chunk-5TLHWYXF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/error.ts","../src/app/module.ts","../src/data/store.ts","../src/data/types.ts","../src/auth/db.ts","../src/auth/session.ts","../src/auth/role.ts","../src/auth/index.ts","../src/socket/server.ts"],"names":["ModelenceError","AuthError","message","ValidationError","RateLimitError","Module","name","stores","queries","mutations","routes","cronJobs","configSchema","rateLimits","rooms","Store","options","client","document","selector","ObjectId","query","errorHandler","result","cursor","id","idSelector","documents","update","pipeline","operations","oldName","db","schemaString","z","schemaNumber","schemaDate","schemaBoolean","schemaArray","schemaObject","schemaEnum","schema","collection","usersCollection","dbDisposableEmailDomains","emailVerificationTokensCollection","resetPasswordTokensCollection","sessionsCollection","obtainSession","authToken","existingSession","createSession","setSessionUser","userId","clearSessionUser","randomBytes","now","expiresAt","time","processSessionHeartbeat","session","newExpiresAt","session_default","args","user","getPublicConfigs","roleMap","defaultRoles","initRoles","roles","_defaultRoles","definition","getUnauthenticatedRoles","getDefaultAuthenticatedRoles","requireAccess","requiredPermissions","missingPermission","permission","hasPermission","role","authenticate","userDoc","socketServer","initSocketServer","httpServer","SocketServer","socket","next","token","logInfo","roomName","roomCategory","room","getSocketServer"],"mappings":"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","file":"chunk-5TLHWYXF.js","sourcesContent":["export abstract class ModelenceError extends Error {\n abstract status: number;\n}\n\nexport class AuthError extends ModelenceError {\n status = 401;\n\n constructor(message: string) {\n super(message);\n this.name = 'AuthError';\n }\n}\n\nexport class ValidationError extends ModelenceError {\n status = 400;\n\n constructor(message: string) {\n super(message);\n this.name = 'ValidationError';\n }\n}\n\nexport class RateLimitError extends ModelenceError {\n status = 429;\n\n constructor(message: string) {\n super(message);\n this.name = 'RateLimitError';\n }\n}\n","import { ConfigSchema } from '../config/types';\nimport { CronJobInputParams } from '../cron/types';\nimport { Store } from '../data/store';\nimport { MethodDefinition } from '../methods/types';\nimport { RouteDefinition } from '../routes/types';\nimport { RateLimitRule } from '../rate-limit/types';\nimport { ServerRoom } from '@/socket/serverRoom';\n\n/** Array of Store instances that will be provisioned when the module is loaded */\ntype Stores = Store<any, any>[];\n\n/** Record of query methods that can be called from the client */\ntype Queries = Record<string, MethodDefinition<any>>;\n\n/** Record of mutation methods that can be called from the client */\ntype Mutations = Record<string, MethodDefinition<any>>;\n\n/**\n * The Module class is a core building block of a Modelence application that encapsulates related functionality.\n * Modules can contain stores, queries, mutations, routes, cron jobs and configurations.\n * \n * @category Module\n * \n * @example\n * ```ts\n * const todoModule = new Module('todo', {\n * stores: [dbTodos],\n * queries: {\n * async getAll() {\n * // Fetch and return all Todo items\n * }\n * },\n * mutations: {\n * async create({ title }, { user }) {\n * // Create a new Todo item\n * }\n * }\n * });\n * ```\n */\nexport class Module {\n /** @internal */\n public readonly name: string;\n\n /** @internal */\n public readonly stores: Stores;\n\n /** @internal */\n public readonly queries: Queries;\n\n /** @internal */\n public readonly mutations: Mutations;\n\n /** @internal */\n public readonly routes: RouteDefinition[];\n\n /** @internal */\n public readonly cronJobs: Record<string, CronJobInputParams>;\n\n /** @internal */\n public readonly configSchema: ConfigSchema;\n\n /** @internal */\n public readonly rateLimits: RateLimitRule[];\n\n /** @internal */\n public readonly rooms: ServerRoom[];\n\n /**\n * Creates a new Module instance\n * \n * @param name - The unique name of the module.\n * This name is used to namespace queries, mutations,\n * cron jobs and configuration values with a prefix (e.g. \"todo.create\")\n * \n * @param options - Module configuration options\n */\n constructor(\n name: string,\n { \n stores = [], \n queries = {}, \n mutations = {},\n routes = [],\n cronJobs = {},\n configSchema = {},\n rateLimits = [],\n rooms = []\n }: { \n stores?: Store<any, any>[],\n queries?: Queries,\n mutations?: Mutations,\n routes?: RouteDefinition[],\n cronJobs?: Record<string, CronJobInputParams>,\n configSchema?: ConfigSchema,\n rateLimits?: RateLimitRule[],\n rooms?: ServerRoom[],\n }\n ) {\n this.name = name;\n this.stores = stores;\n this.queries = queries;\n this.mutations = mutations;\n this.routes = routes;\n this.cronJobs = cronJobs;\n this.configSchema = configSchema;\n this.rateLimits = rateLimits;\n this.rooms = rooms;\n }\n}\n","import {\n AggregateOptions,\n AggregationCursor,\n Collection,\n DeleteResult,\n Document,\n IndexDescription,\n InsertOneResult,\n MongoClient,\n UpdateResult,\n Filter,\n WithId,\n OptionalUnlessRequiredId,\n FindOptions,\n UpdateFilter,\n ObjectId,\n BulkWriteResult,\n AnyBulkWriteOperation,\n InsertManyResult,\n Db,\n ClientSession,\n} from 'mongodb';\n\nimport { ModelSchema, InferDocumentType } from './types';\n\n/**\n * The Store class provides a type-safe interface for MongoDB collections with built-in schema validation and helper methods.\n * \n * @category Store\n * @typeParam TSchema - The document schema type\n * @typeParam TMethods - Custom methods that will be added to documents\n * \n * @example\n * ```ts\n * const dbTodos = new Store('todos', {\n * schema: {\n * title: schema.string(),\n * completed: schema.boolean(),\n * dueDate: schema.date().optional(),\n * userId: schema.userId(),\n * },\n * methods: {\n * isOverdue() {\n * return this.dueDate < new Date();\n * }\n * }\n * });\n * ```\n */\nexport class Store<\n TSchema extends ModelSchema,\n TMethods extends Record<string, (this: WithId<InferDocumentType<TSchema>> & TMethods, ...args: Parameters<any>) => any>\n> {\n /** @internal */\n readonly _type!: InferDocumentType<TSchema>;\n /** @internal */\n readonly _rawDoc!: WithId<this['_type']>;\n /** @internal */\n readonly _doc!: this['_rawDoc'] & TMethods;\n \n readonly Doc!: this['_doc'];\n\n private name: string;\n private readonly schema: TSchema;\n private readonly methods?: TMethods;\n private readonly indexes: IndexDescription[];\n private collection?: Collection<this['_type']>;\n private client?: MongoClient;\n\n /**\n * Creates a new Store instance\n * \n * @param name - The collection name in MongoDB\n * @param options - Store configuration\n */\n constructor(\n name: string,\n options: {\n /** Document schema using Modelence schema types */\n schema: TSchema;\n /** Custom methods to add to documents */\n methods?: TMethods;\n /** MongoDB indexes to create */\n indexes: IndexDescription[];\n }\n ) {\n this.name = name;\n this.schema = options.schema;\n this.methods = options.methods;\n this.indexes = options.indexes;\n }\n\n getName() {\n return this.name;\n }\n\n /** @internal */\n getSchema() {\n return this.schema;\n }\n\n /** @internal */\n init(client: MongoClient) {\n if (this.collection) {\n throw new Error(`Collection ${this.name} is already initialized`);\n }\n\n this.client = client;\n this.collection = this.client.db().collection<this['_type']>(this.name);\n }\n\n /** @internal */\n async createIndexes() {\n if (this.indexes.length > 0) {\n await this.requireCollection().createIndexes(this.indexes);\n }\n }\n\n private wrapDocument(document: this['_rawDoc']): this['_doc'] {\n if (!this.methods) {\n return document as unknown as this['_doc'];\n }\n\n const result = Object.create(\n null,\n Object.getOwnPropertyDescriptors({\n ...document,\n ...this.methods\n })\n );\n\n return result as this['_doc'];\n }\n\n /**\n * For convenience, to also allow directy passing a string or ObjectId as the selector\n */\n private getSelector(selector: Filter<this['_type']> | string | ObjectId) {\n if (typeof selector === 'string') {\n return { _id: new ObjectId(selector) } as Filter<this['_type']>;\n }\n\n if (selector instanceof ObjectId) {\n return { _id: selector } as Filter<this['_type']>;\n }\n\n return selector;\n }\n\n /** @internal */\n requireCollection() {\n if (!this.collection) {\n throw new Error(`Collection ${this.name} is not provisioned`);\n }\n\n return this.collection;\n }\n\n /** @internal */\n requireClient() {\n if (!this.client) {\n throw new Error(`Database is not connected`);\n }\n\n return this.client;\n }\n\n async findOne(\n query: Filter<this['_type']>, \n options?: FindOptions\n ) {\n const document = await this.requireCollection().findOne<this['_rawDoc']>(query, options);\n return document ? this.wrapDocument(document) : null;\n }\n\n async requireOne(\n query: Filter<this['_type']>, \n options?: FindOptions,\n errorHandler?: () => Error\n ): Promise<this['_doc']> {\n \n const result = await this.findOne(query, options);\n if (!result) {\n throw errorHandler ? errorHandler() : new Error(`Record not found in ${this.name}`);\n }\n return result;\n }\n\n private find(query: Filter<this['_type']>, options?: { sort?: Document, limit?: number, skip?: number }) {\n const cursor = this.requireCollection().find(query);\n if (options?.sort) {\n cursor.sort(options.sort);\n }\n if (options?.limit) {\n cursor.limit(options.limit);\n }\n if (options?.skip) {\n cursor.skip(options.skip);\n }\n return cursor;\n }\n\n /**\n * Fetches a single document by its ID\n * \n * @param id - The ID of the document to find\n * @returns The document, or null if not found\n */\n async findById(id: string | ObjectId): Promise<this['_doc'] | null> {\n const idSelector = typeof id === 'string' ? { _id: new ObjectId(id) } : { _id: id };\n return await this.findOne(idSelector as Filter<this['_type']>);\n }\n\n /**\n * Fetches a single document by its ID, or throws an error if not found\n * \n * @param id - The ID of the document to find\n * @param errorHandler - Optional error handler to return a custom error if the document is not found\n * @returns The document\n */\n async requireById(id: string | ObjectId, errorHandler?: () => Error): Promise<this['_doc']> {\n const result = await this.findById(id);\n if (!result) {\n throw errorHandler ? errorHandler() : new Error(`Record with id ${id} not found in ${this.name}`);\n }\n return result;\n }\n\n /**\n * Counts the number of documents that match a query\n * \n * @param query - The query to filter documents\n * @returns The number of documents that match the query\n */\n countDocuments(query: Filter<this['_type']>): Promise<number> {\n return this.requireCollection().countDocuments(query);\n }\n\n /**\n * Fetches multiple documents, equivalent to Node.js MongoDB driver's `find` and `toArray` methods combined.\n * \n * @param query - The query to filter documents\n * @param options - Options\n * @returns The documents\n */\n async fetch(query: Filter<this['_type']>, options?: { sort?: Document, limit?: number, skip?: number }): Promise<this['_doc'][]> {\n const cursor = this.find(query, options)\n return (await cursor.toArray()).map(this.wrapDocument.bind(this));\n }\n\n /**\n * Inserts a single document\n * \n * @param document - The document to insert\n * @returns The result of the insert operation\n */\n async insertOne(document: OptionalUnlessRequiredId<this['_type']>): Promise<InsertOneResult> {\n return await this.requireCollection().insertOne(document);\n }\n\n /**\n * Inserts multiple documents\n * \n * @param documents - The documents to insert\n * @returns The result of the insert operation\n */\n async insertMany(documents: OptionalUnlessRequiredId<this['_type']>[]): Promise<InsertManyResult> {\n return await this.requireCollection().insertMany(documents);\n }\n\n /**\n * Updates a single document\n * \n * @param selector - The selector to find the document to update\n * @param update - The update to apply to the document\n * @returns The result of the update operation\n */\n async updateOne(selector: Filter<this['_type']> | string | ObjectId, update: UpdateFilter<this['_type']>): Promise<UpdateResult> {\n return await this.requireCollection().updateOne(this.getSelector(selector), update);\n }\n\n /**\n * Updates a single document, or inserts it if it doesn't exist\n * \n * @param selector - The selector to find the document to update\n * @param update - The MongoDB modifier to apply to the document\n * @returns The result of the update operation\n */\n async upsertOne(selector: Filter<this['_type']> | string | ObjectId, update: UpdateFilter<this['_type']>): Promise<UpdateResult> {\n return await this.requireCollection().updateOne(this.getSelector(selector), update, { upsert: true });\n }\n\n /**\n * Updates multiple documents\n * \n * @param selector - The selector to find the documents to update\n * @param update - The MongoDB modifier to apply to the documents\n * @returns The result of the update operation\n */\n async updateMany(\n selector: Filter<this['_type']>, \n update: UpdateFilter<this['_type']>, \n options?: { session?: ClientSession }\n ): Promise<UpdateResult> {\n return await this.requireCollection().updateMany(selector, update, options);\n }\n\n /**\n * Updates multiple documents, or inserts them if they don't exist\n * \n * @param selector - The selector to find the documents to update\n * @param update - The MongoDB modifier to apply to the documents\n * @returns The result of the update operation\n */\n async upsertMany(selector: Filter<this['_type']>, update: UpdateFilter<this['_type']>): Promise<UpdateResult> {\n return await this.requireCollection().updateMany(selector, update, { upsert: true });\n }\n\n /**\n * Deletes a single document\n * \n * @param selector - The selector to find the document to delete\n * @returns The result of the delete operation\n */\n async deleteOne(selector: Filter<this['_type']>): Promise<DeleteResult> {\n return await this.requireCollection().deleteOne(selector);\n }\n\n /**\n * Deletes multiple documents\n * \n * @param selector - The selector to find the documents to delete\n * @returns The result of the delete operation\n */\n async deleteMany(selector: Filter<this['_type']>): Promise<DeleteResult> {\n return await this.requireCollection().deleteMany(selector);\n }\n\n /**\n * Aggregates documents using MongoDB's aggregation framework\n * \n * @param pipeline - The aggregation pipeline\n * @param options - Optional options\n * @returns The aggregation cursor\n */\n aggregate(pipeline: Document[], options?: AggregateOptions): AggregationCursor<Document> {\n return this.requireCollection().aggregate(pipeline, options);\n }\n\n /**\n * Performs a bulk write operation on the collection\n * \n * @param operations - The operations to perform\n * @returns The result of the bulk write operation\n */\n bulkWrite(operations: AnyBulkWriteOperation<this['_type']>[]): Promise<BulkWriteResult> {\n return this.requireCollection().bulkWrite(operations);\n }\n\n /**\n * Returns the raw MongoDB database instance for advanced operations\n * @returns The MongoDB database instance\n * @throws Error if the store is not provisioned\n */\n getDatabase() {\n return this.requireClient().db();\n }\n\n /**\n * Returns the raw MongoDB collection instance for advanced operations\n * @returns The MongoDB collection instance\n * @throws Error if the store is not provisioned\n */\n rawCollection() {\n return this.requireCollection();\n }\n\n /**\n * Renames an existing collection to this store's name, used for migrations\n * @param oldName - The previous name of the collection\n * @throws Error if the old collection doesn't exist or if this store's collection already exists\n */\n async renameFrom(oldName: string, options?: { session?: ClientSession }) {\n const db = this.getDatabase();\n\n if (!this.collection || !db) {\n throw new Error(`Store ${this.name} is not provisioned`);\n }\n\n const oldCollections = await db.listCollections({ name: oldName }).toArray();\n if (oldCollections.length === 0) {\n throw new Error(`Collection ${oldName} not found`);\n }\n\n const newCollections = await db.listCollections({ name: this.name }).toArray();\n if (newCollections.length > 0) {\n throw new Error(`Collection ${this.name} already exists`);\n }\n\n const existingCollection = db.collection<this['_type']>(oldName);\n\n await existingCollection.rename(this.name, options);\n }\n}\n","import { ObjectId } from 'mongodb';\nimport { z } from 'zod';\nimport { Store } from './store';\n\ntype ObjectTypeDefinition = {\n [key: string]: SchemaTypeDefinition;\n};\n\ntype SingularSchemaTypeDefinition = z.ZodType | ObjectTypeDefinition; // ReturnType<typeof schema[keyof typeof schema]>;\n\ntype SchemaTypeDefinition = SingularSchemaTypeDefinition | Array<SingularSchemaTypeDefinition>;\n\nexport type ModelSchema = {\n [key: string]: SchemaTypeDefinition;\n};\n\nconst schemaString: typeof z.string = z.string.bind(z);\n\nconst schemaNumber: typeof z.number = z.number.bind(z);\n\nconst schemaDate: typeof z.date = z.date.bind(z);\n\nconst schemaBoolean: typeof z.boolean = z.boolean.bind(z);\n\nconst schemaArray: typeof z.array = z.array.bind(z);\n\nconst schemaObject: typeof z.object = z.object.bind(z);\n\nconst schemaEnum: typeof z.enum = z.enum.bind(z);\n\nexport const schema = {\n string: schemaString,\n number: schemaNumber,\n date: schemaDate,\n boolean: schemaBoolean,\n array: schemaArray,\n object: schemaObject,\n enum: schemaEnum,\n objectId(): z.ZodType<ObjectId> {\n return z.instanceof(ObjectId);\n },\n userId(): z.ZodType<ObjectId> {\n return z.instanceof(ObjectId);\n },\n ref(collection: string | Store<any, any>): z.ZodType<ObjectId> {\n return z.instanceof(ObjectId);\n },\n union: z.union.bind(z),\n infer<T extends SchemaTypeDefinition>(schema: T): InferDocumentType<T> {\n return {} as InferDocumentType<T>;\n }\n} as const;\n\nexport type InferDocumentType<T extends SchemaTypeDefinition> = {\n [K in keyof T as T[K] extends z.ZodOptional<any> ? K : never]?: (T[K] extends z.ZodType ? z.infer<T[K]> : never);\n} & {\n [K in keyof T as T[K] extends z.ZodOptional<any> ? never : K]:\n T[K] extends z.ZodType ? z.infer<T[K]> :\n T[K] extends Array<infer ElementType extends SchemaTypeDefinition> ? Array<InferDocumentType<ElementType>> :\n T[K] extends ObjectTypeDefinition ? InferDocumentType<T[K]> :\n never;\n};\n\nexport namespace schema {\n export type infer<T extends SchemaTypeDefinition> = InferDocumentType<T>;\n}\n","import { schema } from '../data/types';\nimport { Store } from '../data/store';\n\n/**\n * Database collection for storing user accounts with authentication methods and profile information.\n * \n * This is where **signupWithPassword** automatically creates new users.\n * \n * @example\n * ```typescript\n * // Find user by email\n * const user = await dbUsers.findOne(\n * { 'emails.address': 'john@example.com' }\n * );\n * ```\n * \n */\nexport const usersCollection = new Store('_modelenceUsers', {\n schema: {\n handle: schema.string(),\n emails: schema.array(schema.object({\n address: schema.string(),\n verified: schema.boolean(),\n })).optional(),\n createdAt: schema.date(),\n authMethods: schema.object({\n password: schema.object({\n hash: schema.string(),\n }).optional(),\n google: schema.object({\n id: schema.string(),\n }).optional(),\n }),\n },\n indexes: [\n {\n key: { handle: 1 },\n unique: true,\n collation: { locale: 'en', strength: 2 } // Case-insensitive\n },\n ]\n});\n\nexport const dbDisposableEmailDomains = new Store('_modelenceDisposableEmailDomains', {\n schema: {\n domain: schema.string(),\n addedAt: schema.date(),\n },\n indexes: [\n {\n key: { domain: 1 },\n unique: true\n }\n ]\n});\n\nexport const emailVerificationTokensCollection = new Store('_modelenceEmailVerificationTokens', {\n schema: {\n userId: schema.objectId(),\n email: schema.string().optional(),\n token: schema.string(),\n createdAt: schema.date(),\n expiresAt: schema.date(),\n },\n indexes: [\n {\n key: { token: 1 },\n unique: true\n },\n {\n key: { expiresAt: 1 },\n expireAfterSeconds: 0\n }\n ]\n});\n\nexport const resetPasswordTokensCollection = new Store('_modelenceResetPasswordTokens', {\n schema: {\n userId: schema.objectId(),\n token: schema.string(),\n createdAt: schema.date(),\n expiresAt: schema.date(),\n },\n indexes: [\n {\n key: { token: 1 },\n unique: true\n },\n {\n key: { expiresAt: 1 },\n expireAfterSeconds: 0\n }\n ]\n});\n","import { randomBytes } from 'crypto';\nimport { ObjectId } from 'mongodb';\nimport { Module } from '../app/module';\nimport { getPublicConfigs } from '../config/server';\nimport { Store } from '../data/store';\nimport { schema } from '../data/types';\nimport { time } from '../time';\nimport { Session } from './types';\n\nexport const sessionsCollection = new Store('_modelenceSessions', {\n schema: {\n authToken: schema.string(),\n createdAt: schema.date(),\n expiresAt: schema.date(),\n userId: schema.userId().nullable(),\n },\n indexes: [\n { key: { authToken: 1 }, unique: true },\n { key: { expiresAt: 1 }},\n ]\n // TODO: add TTL index on expiresAt\n});\n\nexport async function obtainSession(authToken: string | null): Promise<Session> {\n const existingSession = authToken ? await sessionsCollection.findOne({ authToken }) : null;\n\n if (existingSession) {\n return {\n authToken: String(existingSession.authToken),\n expiresAt: new Date(existingSession.expiresAt),\n userId: existingSession.userId ?? null,\n }\n }\n\n return await createSession();\n}\n\nexport async function setSessionUser(authToken: string, userId: ObjectId) {\n await sessionsCollection.updateOne({ authToken }, {\n $set: { userId }\n });\n}\n\nexport async function clearSessionUser(authToken: string) {\n await sessionsCollection.updateOne({ authToken }, {\n $set: { userId: null }\n });\n}\n\nexport async function createSession(userId: ObjectId | null = null): Promise<Session> {\n // TODO: add rate-limiting and captcha handling\n\n const authToken = randomBytes(32).toString('base64url');\n const now = Date.now();\n const expiresAt = new Date(now + time.days(7));\n\n await sessionsCollection.insertOne({\n authToken,\n createdAt: new Date(now),\n expiresAt,\n userId,\n });\n\n return {\n authToken,\n expiresAt,\n userId,\n };\n}\n\nasync function processSessionHeartbeat(session: Session) {\n const now = Date.now();\n const newExpiresAt = new Date(now + time.days(7));\n\n await sessionsCollection.updateOne({ authToken: session.authToken }, {\n $set: {\n lastActiveDate: new Date(now),\n expiresAt: newExpiresAt\n }\n });\n}\n\nexport default new Module('_system.session', {\n stores: [sessionsCollection],\n mutations: {\n init: async function(args, { session, user }) {\n // TODO: mark or track app load somewhere\n \n return {\n session,\n user,\n configs: getPublicConfigs(),\n };\n },\n heartbeat: async function(args, { session }) {\n // Session might not exist if there is no database/authentication setup\n if (session) {\n await processSessionHeartbeat(session);\n }\n }\n },\n});\n","import { RoleDefinition, Role, DefaultRoles, Permission } from './types';\n\nconst roleMap = new Map<Role, RoleDefinition>();\nconst defaultRoles: DefaultRoles = {\n authenticated: null,\n unauthenticated: null,\n};\n\nexport function initRoles(roles: Record<Role, RoleDefinition>, _defaultRoles: Record<string, Role>) {\n defaultRoles.authenticated = _defaultRoles.authenticated;\n defaultRoles.unauthenticated = _defaultRoles.unauthenticated;\n\n for (const [name, definition] of Object.entries(roles)) {\n roleMap.set(name, definition);\n }\n}\n\nexport function getUnauthenticatedRoles() {\n return defaultRoles.unauthenticated ? [defaultRoles.unauthenticated] : [];\n}\n\nexport function getDefaultAuthenticatedRoles() {\n return defaultRoles.authenticated ? [defaultRoles.authenticated] : [];\n}\n\nexport function hasAccess(roles: Role[], requiredPermissions: Permission[]) {\n return requiredPermissions.every(permission => hasPermission(roles, permission));\n}\n\nexport function requireAccess(roles: Role[], requiredPermissions: Permission[]) {\n const missingPermission = requiredPermissions.find(permission => !hasPermission(roles, permission));\n\n if (missingPermission) {\n throw new Error(`Access denied - missing permission: '${missingPermission}'`);\n }\n}\n\nexport function hasPermission(roles: Role[], permission: Permission) {\n for (const role of roles) {\n const definition = roleMap.get(role);\n\n if (definition && definition.permissions.includes(permission)) {\n return true;\n }\n }\n\n return false;\n}\n","import { ObjectId } from 'mongodb';\n\nimport { obtainSession } from './session';\nimport { usersCollection } from './db';\nimport { getDefaultAuthenticatedRoles, getUnauthenticatedRoles } from './role';\nimport { Role, Session, UserInfo } from './types';\n\nexport async function authenticate(authToken: string | null): Promise<{ session: Session, user: UserInfo | null, roles: Role[] }> {\n const session = await obtainSession(authToken);\n\n const userDoc = session.userId ? await usersCollection.findOne({ _id: new ObjectId(session.userId) }) : null;\n const user = userDoc ? {\n id: userDoc._id.toString(),\n handle: userDoc.handle,\n } : null;\n\n const roles = user ? getDefaultAuthenticatedRoles() : getUnauthenticatedRoles();\n\n return {\n user,\n session,\n roles,\n };\n}\n","import { Server as SocketServer, Socket } from 'socket.io';\nimport { logInfo } from '../telemetry';\nimport { authenticate } from '@/auth';\nimport type { ServerRoom } from './serverRoom';\nimport type { Server } from 'http';\n\nlet socketServer: SocketServer | null = null;\n\nexport function initSocketServer(\n httpServer: Server,\n rooms: ServerRoom[],\n) {\n socketServer = new SocketServer(httpServer, {\n cors: {\n origin: \"*\",\n methods: [\"GET\", \"POST\"]\n }\n });\n\n socketServer.use(async (socket, next) => {\n const token = socket.handshake.auth.token;\n\n try {\n socket.data = await authenticate(token);\n } finally {\n next();\n }\n });\n\n socketServer.on('connection', (socket: Socket) => {\n logInfo(`Socket.IO client connected`, { source: 'websocket', socketId: socket.id });\n \n socket.on('disconnect', () => {\n logInfo(`Socket.IO client disconnected`, { source: 'websocket', socketId: socket.id });\n });\n\n socket.on('joinRoom', async (roomName) => {\n const [roomCategory] = roomName.split(':');\n for (const room of rooms) {\n if (\n room.roomCategory === roomCategory &&\n (\n !room.canAccessRoom ||\n await room.canAccessRoom(socket.data)\n )\n ) {\n socket.join(roomName);\n }\n }\n\n socket.join(roomName);\n console.log(`User ${socket.id} joined room ${roomName}`);\n socket.emit('joinedRoom', roomName);\n });\n\n socket.on('leaveRoom', (roomName) => {\n socket.leave(roomName);\n console.log(`User ${socket.id} left room ${roomName}`);\n socket.emit('leftRoom', roomName);\n });\n });\n\n logInfo(`Socket.IO server initialized`, { source: 'websocket' });\n\n return socketServer;\n}\n\nexport function getSocketServer() {\n return socketServer;\n}\n"]}
package/dist/client.d.ts CHANGED
@@ -108,6 +108,17 @@ declare function resetPassword(options: {
108
108
  password: string;
109
109
  }): Promise<void>;
110
110
 
111
+ declare class ClientRoom<T> {
112
+ readonly roomCategory: string;
113
+ private readonly onMessage;
114
+ constructor(roomCategory: string, onMessage: (data: T) => void);
115
+ init(): void;
116
+ joinRoom(roomId: string): void;
117
+ leaveRoom(roomId: string): void;
118
+ }
119
+
120
+ declare function initSocketClient(rooms: ClientRoom<unknown>[]): void;
121
+
111
122
  declare const AppProvider: any;
112
123
 
113
- export { AppProvider, type MethodArgs, type UserInfo, callMethod, getConfig, loginWithPassword, logout, renderApp, resetPassword, sendResetPasswordToken, signupWithPassword, useSession, verifyEmail };
124
+ export { AppProvider, ClientRoom, type MethodArgs, type UserInfo, callMethod, getConfig, initSocketClient, loginWithPassword, logout, renderApp, resetPassword, sendResetPasswordToken, signupWithPassword, useSession, verifyEmail };
package/dist/client.js CHANGED
@@ -1,2 +1,2 @@
1
- import {b as b$1}from'./chunk-DN5SVAO2.js';import {a}from'./chunk-R7MPLJMA.js';import A,{useState,useEffect}from'react';import {create}from'zustand';import {z as z$1}from'zod';import {jsx}from'react/jsx-runtime';import O from'react-dom/client';function u(){let e=localStorage.getItem("modelence.session");try{return e?JSON.parse(e):null}catch(r){return console.error("Error parsing session from localStorage",r),null}}function m(e){localStorage.setItem("modelence.session",JSON.stringify(e));}var g=(e,r)=>{throw new Error(`Error calling method '${r}': ${e.toString()}`)};function w(e){g=e;}function h(e,r){return g(e,r)}async function n(e,r={}){try{return await k(`/api/_internal/method/${e}`,r)}catch(o){throw h(o,e),o}}async function k(e,r){let o=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({args:r,authToken:u()?.authToken,clientInfo:{screenWidth:window.screen.width,screenHeight:window.screen.height,windowWidth:window.innerWidth,windowHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,orientation:window.screen.orientation?.type}})});if(!o.ok){let i=await o.text();throw new Error(i)}let t=await o.text(),s=t?JSON.parse(t):void 0;if(!s)throw new Error("Invalid response from server");return b$1(s.data,s.typeMap)}var y={};function T(e){return y[e]?.value}function S(e){y=e;}var c=create(e=>({user:null,setUser:r=>e({user:r})})),E=false,H=a.seconds(30);async function x(){if(E)return;E=true;let{configs:e,session:r,user:o}=await n("_system.session.init");S(e),m(r);let t=o?Object.freeze(z$1.object({id:z$1.string(),handle:z$1.string()}).parse(o)):null;c.getState().setUser(t),await P();}async function P(){await n("_system.session.heartbeat"),setTimeout(P,H);}function d(e){c.getState().setUser(e);}function I(){return {user:c(r=>r.user)}}var R=false;function l({children:e,loadingElement:r}){let[o,t]=useState(true);return useEffect(()=>{async function s(){R||(R=true,await x(),t(false));}s();},[]),o?r??jsx("div",{children:"Loading..."}):e}function W({loadingElement:e,routesElement:r,favicon:o,errorHandler:t}){if(t&&w(t),window.addEventListener("unload",()=>{}),O.createRoot(document.getElementById("root")).render(jsx(A.StrictMode,{children:jsx(C,{loadingElement:e,children:r})})),o){let s=document.querySelector("link[rel~='icon']");if(s)s.href=o;else {let i=document.createElement("link");i.rel="icon",i.href=o,document.head.appendChild(i);}}}async function b(e){let{email:r,password:o}=e;await n("_system.user.signupWithPassword",{email:r,password:o});}async function J(e){let{email:r,password:o}=e,{user:t}=await n("_system.user.loginWithPassword",{email:r,password:o});return d(t),t}async function j(e){let{token:r}=e;await n("_system.user.verifyEmail",{token:r});}async function z(){await n("_system.user.logout"),d(null);}async function K(e){let{email:r}=e;await n("_system.user.sendResetPasswordToken",{email:r});}async function $(e){let{token:r,password:o}=e;await n("_system.user.resetPassword",{token:r,password:o});}var C="useClient"in A?A.useClient(l):l;export{C as AppProvider,n as callMethod,T as getConfig,J as loginWithPassword,z as logout,W as renderApp,$ as resetPassword,K as sendResetPasswordToken,b as signupWithPassword,I as useSession,j as verifyEmail};//# sourceMappingURL=client.js.map
1
+ import {b}from'./chunk-DN5SVAO2.js';import {a as a$1}from'./chunk-R7MPLJMA.js';import M,{useState,useEffect}from'react';import {create}from'zustand';import {z as z$1}from'zod';import {jsx}from'react/jsx-runtime';import $ from'react-dom/client';import V from'socket.io-client';function a(){let e=localStorage.getItem("modelence.session");try{return e?JSON.parse(e):null}catch(o){return console.error("Error parsing session from localStorage",o),null}}function w(e){localStorage.setItem("modelence.session",JSON.stringify(e));}var h=(e,o)=>{throw new Error(`Error calling method '${o}': ${e.toString()}`)};function y(e){h=e;}function S(e,o){return h(e,o)}async function n(e,o={}){try{return await U(`/api/_internal/method/${e}`,o)}catch(t){throw S(t,e),t}}async function U(e,o){let t=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({args:o,authToken:a()?.authToken,clientInfo:{screenWidth:window.screen.width,screenHeight:window.screen.height,windowWidth:window.innerWidth,windowHeight:window.innerHeight,pixelRatio:window.devicePixelRatio,orientation:window.screen.orientation?.type}})});if(!t.ok){let i=await t.text();throw new Error(i)}let r=await t.text(),s=r?JSON.parse(r):void 0;if(!s)throw new Error("Invalid response from server");return b(s.data,s.typeMap)}var x={};function H(e){return x[e]?.value}function E(e){x=e;}var d=create(e=>({user:null,setUser:o=>e({user:o})})),C=false,L=a$1.seconds(30);async function R(){if(C)return;C=true;let{configs:e,session:o,user:t}=await n("_system.session.init");E(e),w(o);let r=t?Object.freeze(z$1.object({id:z$1.string(),handle:z$1.string()}).parse(t)):null;d.getState().setUser(r),await k();}async function k(){await n("_system.session.heartbeat"),setTimeout(k,L);}function p(e){d.getState().setUser(e);}function _(){return {user:d(o=>o.user)}}var v=false;function m({children:e,loadingElement:o}){let[t,r]=useState(true);return useEffect(()=>{async function s(){v||(v=true,await R(),r(false));}s();},[]),t?o??jsx("div",{children:"Loading..."}):e}function j({loadingElement:e,routesElement:o,favicon:t,errorHandler:r}){if(r&&y(r),window.addEventListener("unload",()=>{}),$.createRoot(document.getElementById("root")).render(jsx(M.StrictMode,{children:jsx(T,{loadingElement:e,children:o})})),t){let s=document.querySelector("link[rel~='icon']");if(s)s.href=t;else {let i=document.createElement("link");i.rel="icon",i.href=t,document.head.appendChild(i);}}}async function J(e){let{email:o,password:t}=e;await n("_system.user.signupWithPassword",{email:o,password:t});}async function z(e){let{email:o,password:t}=e,{user:r}=await n("_system.user.loginWithPassword",{email:o,password:t});return p(r),r}async function K(e){let{token:o}=e;await n("_system.user.verifyEmail",{token:o});}async function B(){await n("_system.user.logout"),p(null);}async function q(e){let{email:o}=e;await n("_system.user.sendResetPasswordToken",{email:o});}async function D(e){let{token:o,password:t}=e;await n("_system.user.resetPassword",{token:o,password:t});}var A;function F(e){A=V("/",{auth:{token:a()?.authToken}}),e.forEach(o=>o.init());}function c(){return A}var f=class{constructor(o,t){this.roomCategory=o,this.onMessage=t;}init(){c()?.on(this.roomCategory,this.onMessage);}joinRoom(o){c()?.emit("joinRoom",`${this.roomCategory}:${o}`);}leaveRoom(o){c()?.emit("leaveRoom",`${this.roomCategory}:${o}`);}};var T="useClient"in M?M.useClient(m):m;export{T as AppProvider,f as ClientRoom,n as callMethod,H as getConfig,F as initSocketClient,z as loginWithPassword,B as logout,j as renderApp,D as resetPassword,q as sendResetPasswordToken,J as signupWithPassword,_ as useSession,K as verifyEmail};//# sourceMappingURL=client.js.map
2
2
  //# sourceMappingURL=client.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/localStorage.ts","../src/client/errorHandler.ts","../src/client/method.ts","../src/config/client.ts","../src/client/session.ts","../src/client/AppProvider.tsx","../src/client/renderApp.tsx","../src/auth/client/index.ts","../src/client.ts"],"names":["getLocalStorageSession","sessionJson","e","setLocalStorageSession","session","errorHandler","error","methodName","setErrorHandler","handler","handleError","callMethod","args","call","endpoint","response","text","result","reviveResponseTypes","config","getConfig","key","_setConfig","configs","useSessionStore","create","set","user","isInitialized","SESSION_HEARTBEAT_INTERVAL","time","initSession","parsedUser","z","loopSessionHeartbeat","setCurrentUser","useSession","state","AppProvider","children","loadingElement","isLoading","setIsLoading","useState","useEffect","initConfig","jsx","renderApp","routesElement","favicon","ReactDOM","React","link","newLink","signupWithPassword","options","email","password","loginWithPassword","verifyEmail","token","logout","sendResetPasswordToken","resetPassword"],"mappings":"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","file":"client.js","sourcesContent":["export function getLocalStorageSession() {\n const sessionJson = localStorage.getItem('modelence.session');\n try {\n return sessionJson ? JSON.parse(sessionJson) : null;\n } catch (e) {\n console.error('Error parsing session from localStorage', e);\n return null;\n }\n}\n\nexport function setLocalStorageSession(session: object) {\n localStorage.setItem('modelence.session', JSON.stringify(session));\n}\n","export type ErrorHandler = (error: Error, methodName: string) => void;\n\nlet errorHandler: ErrorHandler = (error, methodName) => {\n throw new Error(`Error calling method '${methodName}': ${error.toString()}`);\n};\n\nexport function setErrorHandler(handler: ErrorHandler) {\n errorHandler = handler;\n}\n\nexport function handleError(error: Error, methodName: string) {\n return errorHandler(error, methodName);\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport { getLocalStorageSession } from './localStorage';\nimport { handleError } from './errorHandler';\nimport { reviveResponseTypes } from '../methods/serialize';\n\nexport type MethodArgs = Record<string, unknown>;\n\nexport async function callMethod<T = unknown>(methodName: string, args: MethodArgs = {}): Promise<T> {\n try {\n return await call<T>(`/api/_internal/method/${methodName}`, args);\n } catch (error) {\n handleError(error as Error, methodName);\n throw error;\n }\n}\n\nasync function call<T = unknown>(endpoint: string, args: MethodArgs): Promise<T> {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n args,\n authToken: getLocalStorageSession()?.authToken,\n clientInfo: {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n pixelRatio: window.devicePixelRatio,\n orientation: window.screen.orientation?.type\n }\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(error);\n }\n\n const text = await response.text();\n const result = text ? JSON.parse(text) : undefined;\n if (!result) {\n throw new Error('Invalid response from server');\n }\n\n return reviveResponseTypes(result.data, result.typeMap);\n}\n","import { AppConfig, ConfigKey, Configs } from './types';\n\nlet config: Record<ConfigKey, AppConfig> = {};\n\n/**\n * @sidebarTitle getConfig (client)\n * \n * @param key\n * @returns \n */\nexport function getConfig(key: ConfigKey) {\n return config[key]?.value;\n}\n\nexport function _setConfig(configs: Configs) {\n config = configs;\n}\n","import { create } from 'zustand';\nimport { z } from 'zod';\nimport { callMethod } from './method';\nimport { _setConfig } from '../config/client';\nimport { setLocalStorageSession } from './localStorage';\nimport { time } from '../time';\nimport { Configs } from '../config/types';\n\ntype User = {\n id: string;\n handle: string;\n};\n\ntype SessionStore = {\n user: User | null;\n setUser: (user: User | null) => void;\n};\n\nexport const useSessionStore = create<SessionStore>((set) => ({\n user: null,\n setUser: (user) => set({ user }),\n}));\n\nlet isInitialized = false;\nconst SESSION_HEARTBEAT_INTERVAL = time.seconds(30);\n\nexport async function initSession() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n const { configs, session, user } = await callMethod<{ configs: Configs, session: object, user: object }>('_system.session.init');\n _setConfig(configs);\n setLocalStorageSession(session);\n \n const parsedUser = user ? Object.freeze(z.object({\n id: z.string(),\n handle: z.string(),\n }).parse(user)) : null;\n\n useSessionStore.getState().setUser(parsedUser);\n\n await loopSessionHeartbeat();\n}\n\nasync function loopSessionHeartbeat() {\n await callMethod('_system.session.heartbeat');\n setTimeout(loopSessionHeartbeat, SESSION_HEARTBEAT_INTERVAL);\n}\n\nexport function setCurrentUser(user: User | null) {\n useSessionStore.getState().setUser(user);\n}\n\n/**\n * `useSession` is a hook that returns the current user, and in the future will also return other details about the current session.\n * \n * @example\n * ```ts\n * import { useSession } from 'modelence/client';\n * \n * function MyComponent() {\n * const { user } = useSession();\n * return <div>{user?.handle}</div>;\n * }\n * ```\n */\nexport function useSession() {\n const user = useSessionStore(state => state.user);\n return { user };\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport React, { useState, useEffect, ReactNode } from 'react';\nimport { initSession } from './session';\n\ninterface AppProviderProps {\n children: ReactNode;\n loadingElement?: ReactNode;\n}\n\nlet isInitialized = false;\n\nexport function AppProvider({ children, loadingElement }: AppProviderProps) {\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n async function initConfig() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n await initSession();\n setIsLoading(false);\n }\n\n initConfig();\n }, []);\n\n if (isLoading) {\n return loadingElement ?? <div>Loading...</div>;\n }\n\n return children;\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { AppProvider } from '../client';\nimport { setErrorHandler, ErrorHandler } from './errorHandler';\n\nexport function renderApp({ loadingElement, routesElement, favicon, errorHandler }: {\n loadingElement: React.ReactNode,\n routesElement: React.ReactNode,\n favicon?: string,\n errorHandler?: ErrorHandler\n}) {\n if (errorHandler) {\n setErrorHandler(errorHandler);\n }\n\n window.addEventListener('unload', () => {\n // The presence of any 'unload' event handler, even empty,\n // prevents bfcache in most browsers\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <AppProvider loadingElement={loadingElement}>\n {routesElement}\n </AppProvider>\n </React.StrictMode>\n );\n\n if (favicon) {\n const link = document.querySelector(\"link[rel~='icon']\") as HTMLLinkElement;\n if (!link) {\n const newLink = document.createElement('link');\n newLink.rel = 'icon';\n newLink.href = favicon;\n document.head.appendChild(newLink);\n } else {\n link.href = favicon;\n }\n }\n}\n","import { setCurrentUser } from '../../client/session';\nimport { callMethod } from '../../client/method';\n\nexport type UserInfo = {\n id: string;\n handle: string;\n};\n\n/**\n * Sign up a new user with an email and password.\n * \n * @example\n * ```ts\n * await signupWithPassword({ email: 'test@example.com', password: '12345678' });\n * ```\n * @param options.email - The email of the user.\n * @param options.password - The password of the user.\n */\nexport async function signupWithPassword(options: { email: string, password: string }) {\n const { email, password } = options;\n await callMethod('_system.user.signupWithPassword', { email, password });\n}\n\n/**\n * Login a user with an email and password.\n * \n * @example\n * ```ts\n * await loginWithPassword({ email: 'test@example.com', password: '12345678' });\n * ```\n * @param options.email - The email of the user.\n * @param options.password - The password of the user.\n */\nexport async function loginWithPassword(options: { email: string, password: string }) {\n const { email, password } = options;\n const { user } = await callMethod<{ user: UserInfo }>('_system.user.loginWithPassword', { email, password });\n setCurrentUser(user);\n return user;\n}\n\n/**\n * Verify user's email with a verification token.\n * \n * @example\n * ```ts\n * await verifyEmail({ token: 'verification-token' });\n * ```\n * @param options.token - The email verification token.\n */\nexport async function verifyEmail(options: { token: string }) {\n const { token } = options;\n await callMethod<{ user: UserInfo }>('_system.user.verifyEmail', { token });\n}\n\n/**\n * Logout the current user.\n * \n */\nexport async function logout() {\n await callMethod('_system.user.logout');\n setCurrentUser(null);\n}\n\n/**\n * Send reset password token.\n * @param options.email - The email of the user. \n */\nexport async function sendResetPasswordToken(options: { email: string }) {\n const { email } = options;\n await callMethod('_system.user.sendResetPasswordToken', {\n email,\n });\n}\n\n/**\n * Reset password.\n * @param options.token - The password reset token.\n * @param options.password - The new password.\n */\nexport async function resetPassword(options: { token: string, password: string }) {\n const { token, password } = options;\n await callMethod('_system.user.resetPassword', {\n token,\n password,\n });\n}\n","import React from 'react';\n\nimport { AppProvider as OriginalAppProvider } from './client/AppProvider';\n\nexport { getConfig } from './config/client';\n\nexport const AppProvider = 'useClient' in React\n // @ts-ignore: React.useClient only exists in Next.js\n ? React.useClient(OriginalAppProvider)\n : OriginalAppProvider;\n\nexport { renderApp } from './client/renderApp';\nexport { callMethod, type MethodArgs } from './client/method';\nexport { useSession } from './client/session';\nexport {\n signupWithPassword,\n loginWithPassword,\n verifyEmail,\n logout,\n sendResetPasswordToken,\n resetPassword,\n type UserInfo,\n} from './auth/client';\n"]}
1
+ {"version":3,"sources":["../src/client/localStorage.ts","../src/client/errorHandler.ts","../src/client/method.ts","../src/config/client.ts","../src/client/session.ts","../src/client/AppProvider.tsx","../src/client/renderApp.tsx","../src/auth/client/index.ts","../src/socket/client.ts","../src/socket/clientRoom.ts","../src/client.ts"],"names":["getLocalStorageSession","sessionJson","e","setLocalStorageSession","session","errorHandler","error","methodName","setErrorHandler","handler","handleError","callMethod","args","call","endpoint","response","text","result","reviveResponseTypes","config","getConfig","key","_setConfig","configs","useSessionStore","create","set","user","isInitialized","SESSION_HEARTBEAT_INTERVAL","time","initSession","parsedUser","z","loopSessionHeartbeat","setCurrentUser","useSession","state","AppProvider","children","loadingElement","isLoading","setIsLoading","useState","useEffect","initConfig","jsx","renderApp","routesElement","favicon","ReactDOM","React","link","newLink","signupWithPassword","options","email","password","loginWithPassword","verifyEmail","token","logout","sendResetPasswordToken","resetPassword","socketClient","initSocketClient","rooms","io","room","getSocketClient","ClientRoom","roomCategory","onMessage","roomId"],"mappings":"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","file":"client.js","sourcesContent":["export function getLocalStorageSession() {\n const sessionJson = localStorage.getItem('modelence.session');\n try {\n return sessionJson ? JSON.parse(sessionJson) : null;\n } catch (e) {\n console.error('Error parsing session from localStorage', e);\n return null;\n }\n}\n\nexport function setLocalStorageSession(session: object) {\n localStorage.setItem('modelence.session', JSON.stringify(session));\n}\n","export type ErrorHandler = (error: Error, methodName: string) => void;\n\nlet errorHandler: ErrorHandler = (error, methodName) => {\n throw new Error(`Error calling method '${methodName}': ${error.toString()}`);\n};\n\nexport function setErrorHandler(handler: ErrorHandler) {\n errorHandler = handler;\n}\n\nexport function handleError(error: Error, methodName: string) {\n return errorHandler(error, methodName);\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport { getLocalStorageSession } from './localStorage';\nimport { handleError } from './errorHandler';\nimport { reviveResponseTypes } from '../methods/serialize';\n\nexport type MethodArgs = Record<string, unknown>;\n\nexport async function callMethod<T = unknown>(methodName: string, args: MethodArgs = {}): Promise<T> {\n try {\n return await call<T>(`/api/_internal/method/${methodName}`, args);\n } catch (error) {\n handleError(error as Error, methodName);\n throw error;\n }\n}\n\nasync function call<T = unknown>(endpoint: string, args: MethodArgs): Promise<T> {\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n args,\n authToken: getLocalStorageSession()?.authToken,\n clientInfo: {\n screenWidth: window.screen.width,\n screenHeight: window.screen.height,\n windowWidth: window.innerWidth,\n windowHeight: window.innerHeight,\n pixelRatio: window.devicePixelRatio,\n orientation: window.screen.orientation?.type\n }\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(error);\n }\n\n const text = await response.text();\n const result = text ? JSON.parse(text) : undefined;\n if (!result) {\n throw new Error('Invalid response from server');\n }\n\n return reviveResponseTypes(result.data, result.typeMap);\n}\n","import { AppConfig, ConfigKey, Configs } from './types';\n\nlet config: Record<ConfigKey, AppConfig> = {};\n\n/**\n * @sidebarTitle getConfig (client)\n * \n * @param key\n * @returns \n */\nexport function getConfig(key: ConfigKey) {\n return config[key]?.value;\n}\n\nexport function _setConfig(configs: Configs) {\n config = configs;\n}\n","import { create } from 'zustand';\nimport { z } from 'zod';\nimport { callMethod } from './method';\nimport { _setConfig } from '../config/client';\nimport { setLocalStorageSession } from './localStorage';\nimport { time } from '../time';\nimport { Configs } from '../config/types';\n\ntype User = {\n id: string;\n handle: string;\n};\n\ntype SessionStore = {\n user: User | null;\n setUser: (user: User | null) => void;\n};\n\nexport const useSessionStore = create<SessionStore>((set) => ({\n user: null,\n setUser: (user) => set({ user }),\n}));\n\nlet isInitialized = false;\nconst SESSION_HEARTBEAT_INTERVAL = time.seconds(30);\n\nexport async function initSession() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n const { configs, session, user } = await callMethod<{ configs: Configs, session: object, user: object }>('_system.session.init');\n _setConfig(configs);\n setLocalStorageSession(session);\n \n const parsedUser = user ? Object.freeze(z.object({\n id: z.string(),\n handle: z.string(),\n }).parse(user)) : null;\n\n useSessionStore.getState().setUser(parsedUser);\n\n await loopSessionHeartbeat();\n}\n\nasync function loopSessionHeartbeat() {\n await callMethod('_system.session.heartbeat');\n setTimeout(loopSessionHeartbeat, SESSION_HEARTBEAT_INTERVAL);\n}\n\nexport function setCurrentUser(user: User | null) {\n useSessionStore.getState().setUser(user);\n}\n\n/**\n * `useSession` is a hook that returns the current user, and in the future will also return other details about the current session.\n * \n * @example\n * ```ts\n * import { useSession } from 'modelence/client';\n * \n * function MyComponent() {\n * const { user } = useSession();\n * return <div>{user?.handle}</div>;\n * }\n * ```\n */\nexport function useSession() {\n const user = useSessionStore(state => state.user);\n return { user };\n}\n","/*\n The \"use client\" directive is specifically for the Next.js layout component, which is rendered on the server by default.\n Because of this, we are explicitly marking it as a client component, so we can render this component on the client\n and properly initialize config on the client side.\n \n While this is specific to Next.js, it is simply ignored outside of Next.js and should not cause errors.\n*/\n\"use client\";\n\nimport React, { useState, useEffect, ReactNode } from 'react';\nimport { initSession } from './session';\n\ninterface AppProviderProps {\n children: ReactNode;\n loadingElement?: ReactNode;\n}\n\nlet isInitialized = false;\n\nexport function AppProvider({ children, loadingElement }: AppProviderProps) {\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\n async function initConfig() {\n if (isInitialized) {\n return;\n }\n\n isInitialized = true;\n\n await initSession();\n setIsLoading(false);\n }\n\n initConfig();\n }, []);\n\n if (isLoading) {\n return loadingElement ?? <div>Loading...</div>;\n }\n\n return children;\n}\n","import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { AppProvider } from '../client';\nimport { setErrorHandler, ErrorHandler } from './errorHandler';\n\nexport function renderApp({ loadingElement, routesElement, favicon, errorHandler }: {\n loadingElement: React.ReactNode,\n routesElement: React.ReactNode,\n favicon?: string,\n errorHandler?: ErrorHandler\n}) {\n if (errorHandler) {\n setErrorHandler(errorHandler);\n }\n\n window.addEventListener('unload', () => {\n // The presence of any 'unload' event handler, even empty,\n // prevents bfcache in most browsers\n });\n\n ReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <AppProvider loadingElement={loadingElement}>\n {routesElement}\n </AppProvider>\n </React.StrictMode>\n );\n\n if (favicon) {\n const link = document.querySelector(\"link[rel~='icon']\") as HTMLLinkElement;\n if (!link) {\n const newLink = document.createElement('link');\n newLink.rel = 'icon';\n newLink.href = favicon;\n document.head.appendChild(newLink);\n } else {\n link.href = favicon;\n }\n }\n}\n","import { setCurrentUser } from '../../client/session';\nimport { callMethod } from '../../client/method';\n\nexport type UserInfo = {\n id: string;\n handle: string;\n};\n\n/**\n * Sign up a new user with an email and password.\n * \n * @example\n * ```ts\n * await signupWithPassword({ email: 'test@example.com', password: '12345678' });\n * ```\n * @param options.email - The email of the user.\n * @param options.password - The password of the user.\n */\nexport async function signupWithPassword(options: { email: string, password: string }) {\n const { email, password } = options;\n await callMethod('_system.user.signupWithPassword', { email, password });\n}\n\n/**\n * Login a user with an email and password.\n * \n * @example\n * ```ts\n * await loginWithPassword({ email: 'test@example.com', password: '12345678' });\n * ```\n * @param options.email - The email of the user.\n * @param options.password - The password of the user.\n */\nexport async function loginWithPassword(options: { email: string, password: string }) {\n const { email, password } = options;\n const { user } = await callMethod<{ user: UserInfo }>('_system.user.loginWithPassword', { email, password });\n setCurrentUser(user);\n return user;\n}\n\n/**\n * Verify user's email with a verification token.\n * \n * @example\n * ```ts\n * await verifyEmail({ token: 'verification-token' });\n * ```\n * @param options.token - The email verification token.\n */\nexport async function verifyEmail(options: { token: string }) {\n const { token } = options;\n await callMethod<{ user: UserInfo }>('_system.user.verifyEmail', { token });\n}\n\n/**\n * Logout the current user.\n * \n */\nexport async function logout() {\n await callMethod('_system.user.logout');\n setCurrentUser(null);\n}\n\n/**\n * Send reset password token.\n * @param options.email - The email of the user. \n */\nexport async function sendResetPasswordToken(options: { email: string }) {\n const { email } = options;\n await callMethod('_system.user.sendResetPasswordToken', {\n email,\n });\n}\n\n/**\n * Reset password.\n * @param options.token - The password reset token.\n * @param options.password - The new password.\n */\nexport async function resetPassword(options: { token: string, password: string }) {\n const { token, password } = options;\n await callMethod('_system.user.resetPassword', {\n token,\n password,\n });\n}\n","import { getLocalStorageSession } from '@/client/localStorage';\nimport io, { Socket } from 'socket.io-client';\nimport { ClientRoom } from './clientRoom';\n\nlet socketClient: Socket;\n\nexport function initSocketClient(rooms: ClientRoom<unknown>[]) {\n socketClient = io('/', {\n auth: {\n token: getLocalStorageSession()?.authToken,\n },\n });\n\n rooms.forEach(room => room.init());\n}\n\nexport function getSocketClient() {\n return socketClient;\n}\n","import { getSocketClient } from \"./client\";\n\nexport class ClientRoom<T> {\n public readonly roomCategory: string;\n private readonly onMessage: (data: T) => void;\n\n constructor(\n roomCategory: string,\n onMessage: (data: T) => void\n ) {\n this.roomCategory = roomCategory;\n this.onMessage = onMessage;\n }\n\n init() {\n getSocketClient()?.on(this.roomCategory, this.onMessage);\n }\n\n joinRoom(roomId: string) {\n getSocketClient()?.emit('joinRoom', `${this.roomCategory}:${roomId}`);\n }\n\n leaveRoom(roomId: string) {\n getSocketClient()?.emit('leaveRoom', `${this.roomCategory}:${roomId}`);\n }\n}\n","import React from 'react';\n\nimport { AppProvider as OriginalAppProvider } from './client/AppProvider';\n\nexport { getConfig } from './config/client';\n\nexport const AppProvider = 'useClient' in React\n // @ts-ignore: React.useClient only exists in Next.js\n ? React.useClient(OriginalAppProvider)\n : OriginalAppProvider;\n\nexport { renderApp } from './client/renderApp';\nexport { callMethod, type MethodArgs } from './client/method';\nexport { useSession } from './client/session';\nexport {\n signupWithPassword,\n loginWithPassword,\n verifyEmail,\n logout,\n sendResetPasswordToken,\n resetPassword,\n type UserInfo,\n} from './auth/client';\nexport { ClientRoom } from './socket/clientRoom';\nexport { initSocketClient } from './socket/client';\n"]}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { C as ConfigSchema } from './types-B7qvJJOF.js';
2
+ export { S as ServerRoom } from './serverRoom-Dn45ATJ4.js';
3
+ import 'mongodb';
2
4
 
3
5
  declare const time: {
4
6
  seconds: (x: number) => number;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export{b as AuthError,d as RateLimitError,c as ValidationError}from'./chunk-C3UESBRX.js';export{a as time}from'./chunk-R7MPLJMA.js';//# sourceMappingURL=index.js.map
1
+ import {u}from'./chunk-5TLHWYXF.js';export{b as AuthError,d as RateLimitError,c as ValidationError}from'./chunk-5TLHWYXF.js';export{a as time}from'./chunk-R7MPLJMA.js';import'./chunk-2QLNYYBR.js';var e=class{constructor(o,r){this.roomCategory=o,this.canAccessRoom=r||null;}broadcast(o,r){u()?.to(`${this.roomCategory}:${o}`).emit(this.roomCategory,r);}};export{e as ServerRoom};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
1
+ {"version":3,"sources":["../src/socket/serverRoom.ts"],"names":["ServerRoom","roomCategory","canAccessRoom","roomId","data","getSocketServer"],"mappings":"oMAUO,IAAMA,CAAAA,CAAN,KAA0B,CAI/B,WAAA,CACEC,EACAC,CAAAA,CACA,CACA,IAAA,CAAK,YAAA,CAAeD,EACpB,IAAA,CAAK,aAAA,CAAgBC,CAAAA,EAAiB,KACxC,CAEA,SAAA,CAAUC,CAAAA,CAAgBC,CAAAA,CAAS,CACjCC,CAAAA,EAAgB,EAAG,EAAA,CAAG,CAAA,EAAG,KAAK,YAAY,CAAA,CAAA,EAAIF,CAAM,CAAA,CAAE,EAAE,IAAA,CAAK,IAAA,CAAK,YAAA,CAAcC,CAAI,EACtF,CACF","file":"index.js","sourcesContent":["import { Session, User } from \"@/auth/types\";\nimport { Server as SocketServer } from 'socket.io';\nimport { getSocketServer } from \"./server\";\n\ntype CanAccessRoom = (props: {\n user: User | null,\n session: Session | null,\n roles: string[],\n}) => Promise<boolean>;\n\nexport class ServerRoom<T = any> {\n public readonly roomCategory: string;\n public readonly canAccessRoom: CanAccessRoom | null;\n\n constructor(\n roomCategory: string,\n canAccessRoom?: CanAccessRoom,\n ) {\n this.roomCategory = roomCategory;\n this.canAccessRoom = canAccessRoom || null;\n }\n\n broadcast(roomId: string, data: T) {\n getSocketServer()?.to(`${this.roomCategory}:${roomId}`).emit(this.roomCategory, data);\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ var s="module",i="modelence",o="0.6.0-dev.0",p="The Node.js Framework for Real-Time MongoDB Apps",r="dist/index.js",n="dist/global.d.ts",c={".":"./dist/index.js","./client":"./dist/client.js","./server":"./dist/server.js","./telemetry":"./dist/telemetry.js","./mongodb":"./dist/mongo.js"},d=["dist","dist/bin"],l={modelence:"./dist/bin/modelence.js"},a={build:"tsup",dev:"tsup --watch",prepublishOnly:"npm run build",test:'echo "Error: no test specified" && exit 1',postversion:"git push && git push --tags"},m={type:"git",url:"git+https://github.com/modelence/modelence.git"},u="Modelence",y="SEE LICENSE IN LICENSE",g={url:"https://github.com/modelence/modelence/issues"},h="https://modelence.com",b={"@types/archiver":"^6.0.3","@types/bcrypt":"^5.0.2","@types/cookie-parser":"^1.4.9","@types/express":"^5.0.0","@types/fs-extra":"^11.0.4","@types/node":"^22.5.1","@types/passport-google-oauth20":"^2.0.16","@types/react":"^19.0.0","@types/react-dom":"^19.0.1","@types/socket.io":"^3.0.1","@typescript-eslint/eslint-plugin":"^8.17.0","@typescript-eslint/parser":"^8.17.0",react:"^19.0.0","react-dom":"^19.0.0"},v={"@modelence/types":"^1.0.3","@vitejs/plugin-react":"^4.3.4",archiver:"^7.0.1",bcrypt:"^5.1.1",commander:"^12.0.0","cookie-parser":"^1.4.7",dotenv:"^16.4.5","elastic-apm-node":"^4.8.0",express:"^4.21.0","fs-extra":"^11.2.0",jiti:"^2.4.2",mongodb:"^6.8.1",open:"^10.1.0",passport:"^0.7.0","passport-google-oauth20":"^2.0.0","socket.io":"^4.8.1","socket.io-client":"^4.8.1",tsup:"^8.3.6",tsx:"^4.19.3",typescript:"^5.7.2",vite:"^6.0.3","vite-plugin-eslint":"^1.8.1",winston:"^3.15.0","winston-elasticsearch":"^0.19.0",zod:"^3.23.8",zustand:"^5.0.2"},j={react:">=18.0.0","react-dom":">=18.0.0"},x={type:s,name:i,version:o,description:p,main:r,types:n,exports:c,files:d,bin:l,scripts:a,repository:m,author:u,license:y,bugs:g,homepage:h,devDependencies:b,dependencies:v,peerDependencies:j};
2
+ export{u as author,l as bin,g as bugs,x as default,v as dependencies,p as description,b as devDependencies,c as exports,d as files,h as homepage,y as license,r as main,i as name,j as peerDependencies,m as repository,a as scripts,s as type,n as types,o as version};//# sourceMappingURL=package-EK42D2UK.js.map
3
+ //# sourceMappingURL=package-EK42D2UK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../package.json"],"names":["type","name","version","description","main","types","exports","files","bin","scripts","repository","author","license","bugs","homepage","devDependencies","dependencies","peerDependencies","package_default"],"mappings":"AACE,IAAAA,CAAAA,CAAQ,SACRC,CAAAA,CAAQ,WAAA,CACRC,EAAW,aAAA,CACXC,CAAAA,CAAe,mDACfC,CAAAA,CAAQ,eAAA,CACRC,EAAS,kBAAA,CACTC,CAAAA,CAAW,CACT,GAAA,CAAK,iBAAA,CACL,WAAY,kBAAA,CACZ,UAAA,CAAY,kBAAA,CACZ,aAAA,CAAe,qBAAA,CACf,WAAA,CAAa,iBACf,CAAA,CACAC,CAAAA,CAAS,CACP,MAAA,CACA,UACF,EACAC,CAAAA,CAAO,CACL,UAAa,yBACf,CAAA,CACAC,EAAW,CACT,KAAA,CAAS,OACT,GAAA,CAAO,cAAA,CACP,eAAkB,eAAA,CAClB,IAAA,CAAQ,2CAAA,CACR,WAAA,CAAe,6BACjB,CAAA,CACAC,EAAc,CACZ,IAAA,CAAQ,MACR,GAAA,CAAO,gDACT,EACAC,CAAAA,CAAU,WAAA,CACVC,EAAW,wBAAA,CACXC,CAAAA,CAAQ,CACN,GAAA,CAAO,+CACT,EACAC,CAAAA,CAAY,uBAAA,CACZC,EAAmB,CACjB,iBAAA,CAAmB,QAAA,CACnB,eAAA,CAAiB,QAAA,CACjB,sBAAA,CAAwB,SACxB,gBAAA,CAAkB,QAAA,CAClB,kBAAmB,SAAA,CACnB,aAAA,CAAe,UACf,gCAAA,CAAkC,SAAA,CAClC,eAAgB,SAAA,CAChB,kBAAA,CAAoB,UACpB,kBAAA,CAAoB,QAAA,CACpB,mCAAoC,SAAA,CACpC,2BAAA,CAA6B,UAC7B,KAAA,CAAS,SAAA,CACT,WAAA,CAAa,SACf,CAAA,CACAC,CAAAA,CAAgB,CACd,kBAAA,CAAoB,QAAA,CACpB,uBAAwB,QAAA,CACxB,QAAA,CAAY,SACZ,MAAA,CAAU,QAAA,CACV,UAAa,SAAA,CACb,eAAA,CAAiB,SACjB,MAAA,CAAU,SAAA,CACV,mBAAoB,QAAA,CACpB,OAAA,CAAW,UACX,UAAA,CAAY,SAAA,CACZ,IAAA,CAAQ,QAAA,CACR,OAAA,CAAW,QAAA,CACX,KAAQ,SAAA,CACR,QAAA,CAAY,SACZ,yBAAA,CAA2B,QAAA,CAC3B,YAAa,QAAA,CACb,kBAAA,CAAoB,SACpB,IAAA,CAAQ,QAAA,CACR,IAAO,SAAA,CACP,UAAA,CAAc,SACd,IAAA,CAAQ,QAAA,CACR,qBAAsB,QAAA,CACtB,OAAA,CAAW,SAAA,CACX,uBAAA,CAAyB,SAAA,CACzB,GAAA,CAAO,UACP,OAAA,CAAW,QACb,EACAC,CAAAA,CAAoB,CAClB,MAAS,UAAA,CACT,WAAA,CAAa,UACf,CAAA,CArFFC,CAAAA,CAAA,CACE,IAAA,CAAAlB,CAAAA,CACA,KAAAC,CAAAA,CACA,OAAA,CAAAC,EACA,WAAA,CAAAC,CAAAA,CACA,IAAA,CAAAC,CAAAA,CACA,KAAA,CAAAC,CAAAA,CACA,QAAAC,CAAAA,CAOA,KAAA,CAAAC,EAIA,GAAA,CAAAC,CAAAA,CAGA,QAAAC,CAAAA,CAOA,UAAA,CAAAC,EAIA,MAAA,CAAAC,CAAAA,CACA,QAAAC,CAAAA,CACA,IAAA,CAAAC,EAGA,QAAA,CAAAC,CAAAA,CACA,gBAAAC,CAAAA,CAgBA,YAAA,CAAAC,CAAAA,CA4BA,gBAAA,CAAAC,CAIF","file":"package-EK42D2UK.js","sourcesContent":["{\n \"type\": \"module\",\n \"name\": \"modelence\",\n \"version\": \"0.6.0-dev.0\",\n \"description\": \"The Node.js Framework for Real-Time MongoDB Apps\",\n \"main\": \"dist/index.js\",\n \"types\": \"dist/global.d.ts\",\n \"exports\": {\n \".\": \"./dist/index.js\",\n \"./client\": \"./dist/client.js\",\n \"./server\": \"./dist/server.js\",\n \"./telemetry\": \"./dist/telemetry.js\",\n \"./mongodb\": \"./dist/mongo.js\"\n },\n \"files\": [\n \"dist\",\n \"dist/bin\"\n ],\n \"bin\": {\n \"modelence\": \"./dist/bin/modelence.js\"\n },\n \"scripts\": {\n \"build\": \"tsup\",\n \"dev\": \"tsup --watch\",\n \"prepublishOnly\": \"npm run build\",\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n \"postversion\": \"git push && git push --tags\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/modelence/modelence.git\"\n },\n \"author\": \"Modelence\",\n \"license\": \"SEE LICENSE IN LICENSE\",\n \"bugs\": {\n \"url\": \"https://github.com/modelence/modelence/issues\"\n },\n \"homepage\": \"https://modelence.com\",\n \"devDependencies\": {\n \"@types/archiver\": \"^6.0.3\",\n \"@types/bcrypt\": \"^5.0.2\",\n \"@types/cookie-parser\": \"^1.4.9\",\n \"@types/express\": \"^5.0.0\",\n \"@types/fs-extra\": \"^11.0.4\",\n \"@types/node\": \"^22.5.1\",\n \"@types/passport-google-oauth20\": \"^2.0.16\",\n \"@types/react\": \"^19.0.0\",\n \"@types/react-dom\": \"^19.0.1\",\n \"@types/socket.io\": \"^3.0.1\",\n \"@typescript-eslint/eslint-plugin\": \"^8.17.0\",\n \"@typescript-eslint/parser\": \"^8.17.0\",\n \"react\": \"^19.0.0\",\n \"react-dom\": \"^19.0.0\"\n },\n \"dependencies\": {\n \"@modelence/types\": \"^1.0.3\",\n \"@vitejs/plugin-react\": \"^4.3.4\",\n \"archiver\": \"^7.0.1\",\n \"bcrypt\": \"^5.1.1\",\n \"commander\": \"^12.0.0\",\n \"cookie-parser\": \"^1.4.7\",\n \"dotenv\": \"^16.4.5\",\n \"elastic-apm-node\": \"^4.8.0\",\n \"express\": \"^4.21.0\",\n \"fs-extra\": \"^11.2.0\",\n \"jiti\": \"^2.4.2\",\n \"mongodb\": \"^6.8.1\",\n \"open\": \"^10.1.0\",\n \"passport\": \"^0.7.0\",\n \"passport-google-oauth20\": \"^2.0.0\",\n \"socket.io\": \"^4.8.1\",\n \"socket.io-client\": \"^4.8.1\",\n \"tsup\": \"^8.3.6\",\n \"tsx\": \"^4.19.3\",\n \"typescript\": \"^5.7.2\",\n \"vite\": \"^6.0.3\",\n \"vite-plugin-eslint\": \"^1.8.1\",\n \"winston\": \"^3.15.0\",\n \"winston-elasticsearch\": \"^0.19.0\",\n \"zod\": \"^3.23.8\",\n \"zustand\": \"^5.0.2\"\n },\n \"peerDependencies\": {\n \"react\": \">=18.0.0\",\n \"react-dom\": \">=18.0.0\"\n }\n}\n"]}
package/dist/server.d.ts CHANGED
@@ -1,26 +1,12 @@
1
1
  import { EmailProvider, AppServer } from '@modelence/types';
2
+ import { P as Permission, a as Session, U as UserInfo, S as ServerRoom, R as RoleDefinition } from './serverRoom-Dn45ATJ4.js';
3
+ import { C as ConfigSchema, a as ConfigKey, A as AppConfig } from './types-B7qvJJOF.js';
2
4
  import { ObjectId as ObjectId$1, WithId, IndexDescription, MongoClient, Collection, Filter, FindOptions, Document, OptionalUnlessRequiredId, InsertOneResult, InsertManyResult, UpdateFilter, UpdateResult, ClientSession, DeleteResult, AggregateOptions, AggregationCursor, AnyBulkWriteOperation, BulkWriteResult, Db } from 'mongodb';
3
5
  export { ObjectId } from 'mongodb';
4
- import { C as ConfigSchema, a as ConfigKey, A as AppConfig } from './types-B7qvJJOF.js';
5
6
  import * as zod from 'zod';
6
7
  import { z } from 'zod';
7
8
  import { Request } from 'express';
8
9
 
9
- type UserInfo = {
10
- id: string;
11
- handle: string;
12
- };
13
- type Session = {
14
- authToken: string;
15
- expiresAt: Date;
16
- userId: ObjectId$1 | null;
17
- };
18
- type Permission = string;
19
- type RoleDefinition = {
20
- description?: string;
21
- permissions: Permission[];
22
- };
23
-
24
10
  type CronJobHandler = () => Promise<void>;
25
11
  type CronJobInputParams = {
26
12
  description?: string;
@@ -387,6 +373,8 @@ declare class Module {
387
373
  readonly configSchema: ConfigSchema;
388
374
  /** @internal */
389
375
  readonly rateLimits: RateLimitRule[];
376
+ /** @internal */
377
+ readonly rooms: ServerRoom[];
390
378
  /**
391
379
  * Creates a new Module instance
392
380
  *
@@ -396,7 +384,7 @@ declare class Module {
396
384
  *
397
385
  * @param options - Module configuration options
398
386
  */
399
- constructor(name: string, { stores, queries, mutations, routes, cronJobs, configSchema, rateLimits }: {
387
+ constructor(name: string, { stores, queries, mutations, routes, cronJobs, configSchema, rateLimits, rooms }: {
400
388
  stores?: Store<any, any>[];
401
389
  queries?: Queries;
402
390
  mutations?: Mutations;
@@ -404,6 +392,7 @@ declare class Module {
404
392
  cronJobs?: Record<string, CronJobInputParams>;
405
393
  configSchema?: ConfigSchema;
406
394
  rateLimits?: RateLimitRule[];
395
+ rooms?: ServerRoom[];
407
396
  });
408
397
  }
409
398
 
@@ -444,8 +433,11 @@ type AppOptions = {
444
433
  roles?: Record<string, RoleDefinition>;
445
434
  defaultRoles?: Record<string, string>;
446
435
  migrations?: Array<MigrationScript>;
436
+ websockets?: {
437
+ enabled?: boolean;
438
+ };
447
439
  };
448
- declare function startApp({ modules, roles, defaultRoles, server, migrations, email }: AppOptions): Promise<void>;
440
+ declare function startApp({ modules, roles, defaultRoles, server, migrations, email, websockets }: AppOptions): Promise<void>;
449
441
 
450
442
  declare function createQuery<T extends any[]>(name: string, methodDef: MethodDefinition<T>): void;
451
443
 
@@ -685,7 +677,7 @@ declare const usersCollection: Store<{
685
677
  id: string;
686
678
  } | undefined;
687
679
  }>;
688
- }>, "handle" | "createdAt" | "emails" | "authMethods"> & {
680
+ }>, "createdAt" | "handle" | "emails" | "authMethods"> & {
689
681
  _id: ObjectId;
690
682
  }, ...args: Parameters<any>) => any>>;
691
683
 
@@ -734,4 +726,4 @@ type CloudBackendConnectErrorResponse = {
734
726
  };
735
727
  type CloudBackendConnectResponse = CloudBackendConnectOkResponse | CloudBackendConnectErrorResponse;
736
728
 
737
- export { type AppOptions, type CloudBackendConnectResponse, type CronJobInputParams, type HttpMethod, Module, type RateLimitRule, type RateLimitType, type RouteDefinition, type RouteHandler, type RouteParams, type RouteResponse, Store, type UserInfo, consumeRateLimit, createQuery, usersCollection as dbUsers, getConfig, schema, startApp };
729
+ export { type AppOptions, type CloudBackendConnectResponse, type CronJobInputParams, type HttpMethod, Module, type RateLimitRule, type RateLimitType, type RouteDefinition, type RouteHandler, type RouteParams, type RouteResponse, Store, UserInfo, consumeRateLimit, createQuery, usersCollection as dbUsers, getConfig, schema, startApp };