modelence 0.6.18 → 0.6.20

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/telemetry.js CHANGED
@@ -1,2 +1,2 @@
1
- export{l as captureError,h as logDebug,j as logError,i as logInfo,k as startTransaction}from'./chunk-EY7KRL57.js';//# sourceMappingURL=telemetry.js.map
1
+ export{l as captureError,h as logDebug,j as logError,i as logInfo,k as startTransaction}from'./chunk-CDNHR3S5.js';//# sourceMappingURL=telemetry.js.map
2
2
  //# sourceMappingURL=telemetry.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, type Session as S, type UserInfo as U, type WebsocketServerProvider as W, type WebsocketClientProvider as a, type ConfigKey as b, ClientChannel as c, ServerChannel as d, type User as e, type Role as f };
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 };
package/dist/types.d.ts CHANGED
@@ -1,29 +1,7 @@
1
1
  export { A as AppServer, E as ExpressMiddleware, M as ModelenceConfig } from './index-CwdohC5n.js';
2
+ export { A as Args, i as ClientInfo, b as ConnectionInfo, j as Context, n as CronJob, C as CronJobInputParams, o as CronJobMetadata, h as EmailAttachment, d as EmailPayload, E as EmailProvider, r as ExpressHandler, k as Handler, H as HttpMethod, I as InferDocumentType, m as Method, M as MethodDefinition, l as MethodType, p as ModelSchema, a as RateLimitRule, c as RateLimitType, R as RouteDefinition, e as RouteHandler, q as RouteHandlers, f as RouteParams, g as RouteResponse, s as schema } from './types-D6nwUab6.js';
3
+ export { A as AppConfig, b as ConfigKey, C as ConfigSchema, g as ConfigType, h as Configs, D as DefaultRoles, P as Permission, f as Role, R as RoleDefinition, d as Session, U as User, e as UserInfo, a as WebsocketClientProvider, W as WebsocketServerProvider } from './types-Ds1ESQSs.js';
2
4
  import 'express';
3
-
4
- type EmailAttachment = {
5
- filename: string;
6
- content: Buffer | string;
7
- contentType: string;
8
- };
9
- type EmailPayload = {
10
- from: string;
11
- to: string | string[];
12
- subject: string;
13
- html?: string;
14
- text?: string;
15
- cc?: string | string[];
16
- bcc?: string | string[];
17
- replyTo?: string | string[];
18
- headers?: Record<string, string>;
19
- attachments?: EmailAttachment[];
20
- } & ({
21
- html: string;
22
- } | {
23
- text: string;
24
- });
25
- interface EmailProvider {
26
- sendEmail(data: EmailPayload): Promise<void>;
27
- }
28
-
29
- export type { EmailAttachment, EmailPayload, EmailProvider };
5
+ import 'mongodb';
6
+ import 'zod';
7
+ import 'http';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "modelence",
4
- "version": "0.6.18",
4
+ "version": "0.6.20",
5
5
  "description": "The Node.js Framework for Real-Time MongoDB Apps",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/global.d.ts",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config/server.ts","../src/app/loggerProcess.ts","../src/app/state.ts","../src/app/metrics.ts","../src/telemetry/index.ts"],"names":["configSchema","config","isInitialized","getConfig","key","getPublicConfigs","_","schema","loadConfigs","configs","type","value","setSchema","isPublic","buffer","sequenceId","startLoggerProcess","_elasticCloudId","_elasticApiKey","originalStdoutWrite","process","originalStderrWrite","chunk","args","addToBuffer","loopSendLogs","timestamp","i","current","sendLogs","stdoutLogs","stderrLogs","log","logInfo","logError","metadata","markAppStarted","setMetadata","_metadata","getEnvironmentId","getAppAlias","getEnvironmentAlias","getTelemetryServiceName","isTelemetryEnabled","apm","logger","initMetrics","initElasticApm","elasticApmEndpoint","elasticCloudId","elasticApiKey","appAlias","environmentAlias","environmentId","serviceName","elasticApm","esTransport","ElasticsearchTransport","error","winston","getApm","getLogger","getLogLevel","logDebug","message","startTransaction","name","context","transaction","result","endTime","captureError"],"mappings":"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","file":"chunk-EY7KRL57.js","sourcesContent":["import { AppConfig, ConfigKey, ConfigSchema } from './types';\n\nlet configSchema: ConfigSchema = {};\nlet config: Record<ConfigKey, AppConfig> = {};\nlet isInitialized = false;\n\n/**\n * @sidebarTitle getConfig (server)\n *\n * Retrieves a configuration value by key. Configuration values can be set via environment variables\n * or managed through the Modelence Cloud Backend.\n *\n * @param key - The configuration key to retrieve\n * @returns The configuration value (string, number, or boolean)\n *\n * @example\n * ```ts\n * import { getConfig } from 'modelence/server';\n *\n * // Get the site URL\n * const siteUrl = getConfig('_system.site.url');\n * ```\n *\n * Set via environment variable:\n * ```bash\n * MODELENCE_SITE_URL=https://myapp.com\n * ```\n *\n * @example\n * ```ts\n * import { getConfig } from 'modelence/server';\n *\n * // Get the current environment (e.g., 'development', 'staging', 'production')\n * const env = getConfig('_system.env');\n *\n * if (env === 'production') {\n * // Enable production features\n * }\n * ```\n *\n * Set via environment variable:\n * ```bash\n * MODELENCE_SITE_ENV=production\n * ```\n */\nexport function getConfig(key: ConfigKey) {\n return config[key]?.value;\n}\n\nexport function getPublicConfigs() {\n if (!isInitialized) {\n throw new Error(\n 'Config is not initialized: an attempt was made to access configs before they were loaded'\n );\n }\n\n return Object.fromEntries(\n Object.entries(configSchema)\n .filter(([_, schema]) => schema.isPublic)\n .map(([key, schema]) => {\n return [\n key,\n {\n key,\n type: schema.type,\n value: config[key]?.value ?? schema.default,\n },\n ];\n })\n );\n}\n\nexport function loadConfigs(configs: AppConfig[]) {\n configs.forEach(({ key, type, value }) => {\n const isSystemConfig = key.toLowerCase().startsWith('_system.');\n\n if (!isSystemConfig && !configSchema[key]) {\n // Ignore unknown configs\n return;\n }\n\n config[key] = {\n key,\n type,\n value,\n };\n });\n\n isInitialized = true;\n}\n\nexport function setSchema(schema: ConfigSchema) {\n // TODO: more validation on the schema structure\n Object.entries(schema).forEach(([key, value]) => {\n const { type, isPublic } = value;\n\n if (type === 'secret' && isPublic) {\n throw new Error(`Config ${key} with type \"secret\" cannot be public`);\n }\n });\n\n configSchema = schema;\n}\n","// import { spawn } from 'child_process';\n// import { fileURLToPath } from 'url';\n// import { dirname, join } from 'path';\nimport { logInfo, logError } from '@/telemetry';\nimport process from 'process';\n\ntype LogEntry = { log: string; timestamp: Date | null; sequenceId?: number };\ntype LogBuffer = LogEntry[];\n\nconst buffer: { stdout: LogBuffer; stderr: LogBuffer } = {\n stdout: [{ log: '', timestamp: null }],\n stderr: [{ log: '', timestamp: null }],\n};\n\nlet sequenceId = 1;\n\nexport function startLoggerProcess({\n elasticCloudId: _elasticCloudId,\n elasticApiKey: _elasticApiKey,\n}: {\n elasticCloudId: string;\n elasticApiKey: string;\n}) {\n const originalStdoutWrite = process.stdout.write;\n const originalStderrWrite = process.stderr.write;\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n process.stdout.write = function (chunk: string | Uint8Array, ...args: any[]) {\n addToBuffer(chunk.toString(), buffer.stdout);\n return originalStdoutWrite.call(process.stdout, chunk, ...args);\n };\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n process.stderr.write = function (chunk: string | Uint8Array, ...args: any[]) {\n addToBuffer(chunk.toString(), buffer.stderr);\n return originalStderrWrite.call(process.stderr, chunk, ...args);\n };\n\n loopSendLogs();\n\n // const currentFilePath = fileURLToPath(import.meta.url);\n // const projectRoot = dirname(dirname(currentFilePath));\n // const loggerPath = join(projectRoot, 'bin', 'modelence-logger', 'index.js');\n // const logger = spawn(process.execPath, [loggerPath], {\n // env: {\n // NODE_ENV: process.env.NODE_ENV,\n // ELASTIC_CLOUD_ID: elasticCloudId,\n // ELASTIC_API_KEY: elasticApiKey\n // },\n // stdio: ['pipe', 'inherit', 'inherit'],\n // detached: true\n // });\n\n // const originalStdoutWrite = process.stdout.write;\n // const originalStderrWrite = process.stderr.write;\n\n // process.stdout.write = function(chunk: any, ...args: any[]) {\n // logger.stdin.write(chunk);\n // return originalStdoutWrite.apply(process.stdout, [chunk, ...args]);\n // };\n\n // process.stderr.write = function(chunk: any, ...args: any[]) {\n // logger.stdin.write(chunk);\n // return originalStderrWrite.apply(process.stderr, [chunk, ...args]);\n // };\n\n // process.on('exit', () => {\n // process.stdout.write = originalStdoutWrite;\n // process.stderr.write = originalStderrWrite;\n // });\n\n // logger.unref();\n}\n\nfunction addToBuffer(chunk: string, buffer: LogBuffer) {\n if (chunk.length === 0) {\n return;\n }\n\n const timestamp = new Date();\n\n for (let i = 0; i < chunk.length; i++) {\n const current = buffer[buffer.length - 1];\n if (!current.timestamp) {\n current.timestamp = timestamp;\n current.sequenceId = sequenceId++;\n }\n\n if (chunk[i] === '\\n') {\n buffer.push({ log: '', timestamp: null });\n } else {\n current.log += chunk[i];\n }\n }\n}\n\nasync function sendLogs() {\n const stdoutLogs = buffer.stdout.slice(0, -1);\n buffer.stdout = [buffer.stdout[buffer.stdout.length - 1]];\n\n const stderrLogs = buffer.stderr.slice(0, -1);\n buffer.stderr = [buffer.stderr[buffer.stderr.length - 1]];\n\n stdoutLogs.forEach(({ log, timestamp, sequenceId }: LogEntry) => {\n logInfo(log, { timestamp, source: 'console', sequenceId });\n });\n stderrLogs.forEach(({ log, timestamp, sequenceId }: LogEntry) => {\n logError(log, { timestamp, source: 'console', sequenceId });\n });\n}\n\nfunction loopSendLogs() {\n setTimeout(() => {\n sendLogs();\n loopSendLogs();\n }, 1000);\n}\n","type AppMetadata = {\n environmentId: string;\n appAlias: string;\n environmentAlias: string;\n telemetry: {\n isEnabled: boolean;\n serviceName: string;\n };\n};\n\nlet appStarted = false;\nlet metadata: AppMetadata | null = null;\n\nexport function markAppStarted() {\n appStarted = true;\n}\n\nexport function isAppStarted() {\n return appStarted;\n}\n\nexport function setMetadata(_metadata: AppMetadata) {\n metadata = Object.assign({}, metadata, _metadata);\n}\n\nexport function getEnvironmentId() {\n return metadata?.environmentId;\n}\n\nexport function getAppAlias() {\n return metadata?.appAlias;\n}\n\nexport function getEnvironmentAlias() {\n return metadata?.environmentAlias;\n}\n\nexport function getTelemetryServiceName() {\n return metadata?.telemetry?.serviceName;\n}\n\nexport function isTelemetryEnabled() {\n return Boolean(metadata?.telemetry?.isEnabled);\n}\n","import elasticApm from 'elastic-apm-node';\nimport winston from 'winston';\nimport { ElasticsearchTransport } from 'winston-elasticsearch';\n\nimport { getConfig } from '../config/server';\nimport { startLoggerProcess } from './loggerProcess';\nimport {\n getAppAlias,\n getEnvironmentAlias,\n getEnvironmentId,\n getTelemetryServiceName,\n isTelemetryEnabled,\n} from './state';\n\nlet isInitialized = false;\nlet apm: typeof elasticApm | null = null;\nlet logger: winston.Logger | null = null;\n\nexport const initMetrics = async () => {\n if (isInitialized) {\n throw new Error('Metrics are already initialized, duplicate \"initMetrics\" call received');\n }\n\n isInitialized = true;\n\n if (isTelemetryEnabled()) {\n await initElasticApm();\n }\n};\n\nasync function initElasticApm() {\n const elasticApmEndpoint = getConfig('_system.elastic.apmEndpoint') as string;\n const elasticCloudId = getConfig('_system.elastic.cloudId') as string;\n const elasticApiKey = getConfig('_system.elastic.apiKey') as string;\n\n const appAlias = getAppAlias() ?? 'unknown';\n const environmentAlias = getEnvironmentAlias() ?? 'unknown';\n const environmentId = getEnvironmentId() ?? 'unknown';\n const serviceName = getTelemetryServiceName();\n\n apm = elasticApm.start({\n serviceName,\n apiKey: elasticApiKey,\n serverUrl: elasticApmEndpoint,\n // environment: 'dev',\n transactionSampleRate: 1.0,\n centralConfig: false,\n globalLabels: {\n modelenceEnv: 'dev',\n appEnv: 'dev',\n environmentId,\n appAlias,\n environmentAlias,\n },\n // logLevel: 'debug'\n });\n\n const esTransport = new ElasticsearchTransport({\n apm,\n level: 'debug',\n clientOpts: {\n cloud: {\n id: elasticCloudId,\n },\n auth: {\n apiKey: elasticApiKey,\n },\n requestTimeout: 10000,\n tls: {\n rejectUnauthorized: false,\n },\n },\n bufferLimit: 1000,\n silent: false,\n });\n\n esTransport.on('error', (error) => {\n console.error('Elasticsearch Transport Error:', error);\n });\n\n logger = winston.createLogger({\n level: 'debug',\n defaultMeta: {\n serviceName,\n },\n format: winston.format.combine(winston.format.json()),\n transports: [\n // new winston.transports.Console(), // TODO: remove, just for debugging\n esTransport,\n ],\n });\n\n startLoggerProcess({\n elasticCloudId,\n elasticApiKey,\n });\n}\n\nexport function getApm() {\n if (!apm) {\n throw new Error('APM is not initialized');\n }\n return apm;\n}\n\nexport function getLogger() {\n if (!logger) {\n throw new Error('Logger is not initialized');\n }\n return logger;\n}\n","import { getLogger, getApm } from '@/app/metrics';\nimport { isTelemetryEnabled } from '@/app/state';\nimport { getConfig } from '@/config/server';\n\nfunction getLogLevel() {\n return getConfig('_system.log.level') as 'debug' | 'info' | 'error';\n}\n\nexport function logDebug(message: string, args: object) {\n if (getLogLevel() === 'debug') {\n if (isTelemetryEnabled()) {\n getLogger().debug(message, args);\n } else {\n console.debug(message, args);\n }\n }\n}\n\nexport function logInfo(message: string, args: object) {\n if (['debug', 'info'].includes(getLogLevel())) {\n if (isTelemetryEnabled()) {\n getLogger().info(message, args);\n } else {\n console.info(message, args);\n }\n }\n}\n\nexport function logError(message: string, args: object) {\n if (isTelemetryEnabled()) {\n getLogger().error(message, args);\n } else {\n console.error(message, args);\n }\n}\n\ninterface WrappedTransaction {\n end(result?: string, context?: Record<string, unknown>): void;\n setContext(context: Record<string, unknown>): void;\n}\n\nexport function startTransaction(\n type: 'method' | 'cron' | 'ai' | 'custom' | 'route',\n name: string,\n context?: Record<string, unknown>\n): WrappedTransaction {\n if (!isTelemetryEnabled()) {\n return {\n end: () => {\n // do nothing\n },\n setContext: () => {\n // do nothing\n },\n };\n }\n\n const apm = getApm();\n const transaction = apm.startTransaction(name, type);\n if (context) {\n apm.setCustomContext(context);\n }\n\n return {\n end: (\n result?: string,\n { endTime, context }: { endTime?: number; context?: Record<string, unknown> } = {}\n ) => {\n if (context) {\n apm.setCustomContext(context);\n }\n transaction.end(result, endTime);\n },\n setContext: (context: Record<string, unknown>) => {\n apm.setCustomContext(context);\n },\n };\n}\n\nexport function captureError(error: Error) {\n if (!isTelemetryEnabled()) {\n console.error(error);\n return;\n }\n\n getApm().captureError(error);\n}\n"]}