@heliyos/heliyos-api-core 1.0.13 → 1.0.15

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.
@@ -1,4 +1,4 @@
1
- import mongoose, { Connection, Model, Schema, FilterQuery, UpdateQuery } from "mongoose";
1
+ import mongoose, { Model, Schema, FilterQuery, UpdateQuery } from "mongoose";
2
2
  export * from "mongoose";
3
3
  export interface Pagination<T> {
4
4
  data: T[];
@@ -16,7 +16,7 @@ declare module "mongoose" {
16
16
  }
17
17
  export declare const mongooseConnection: Promise<mongoose.Connection>;
18
18
  export declare const mongooseReplicaConnection: Promise<mongoose.Connection>;
19
- export declare const createModel: <T extends mongoose.Document<unknown, any, any>>(name: string, schema: Schema, connection?: Connection | Promise<Connection>) => mongoose.Model<T, {}, {}, {}, mongoose.IfAny<T, any, mongoose.Document<unknown, {}, T> & mongoose.Default__v<mongoose.Require_id<T>>>, any> | Promise<mongoose.Model<T, {}, {}, {}, mongoose.IfAny<T, any, mongoose.Document<unknown, {}, T> & mongoose.Default__v<mongoose.Require_id<T>>>, any>>;
19
+ export declare const createModel: <T extends mongoose.Document<unknown, any, any>>(name: string, schema: Schema) => mongoose.Model<T, {}, {}, {}, mongoose.IfAny<T, any, mongoose.Document<unknown, {}, T> & mongoose.Default__v<mongoose.Require_id<T>>>, any>;
20
20
  export declare const createBulkOperations: <T>(operations: {
21
21
  updateOne?: {
22
22
  filter: mongoose.FilterQuery<T>;
package/dist/mongoose.js CHANGED
@@ -10,18 +10,6 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
10
10
  if (k2 === undefined) k2 = k;
11
11
  o[k2] = m[k];
12
12
  }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
26
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
27
15
  };
@@ -34,9 +22,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
34
22
  step((generator = generator.apply(thisArg, _arguments || [])).next());
35
23
  });
36
24
  };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
37
28
  Object.defineProperty(exports, "__esModule", { value: true });
38
29
  exports.closeConnections = exports.checkDatabaseHealth = exports.upsertDocument = exports.safeDelete = exports.withTransaction = exports.ensureIndexes = exports.createBulkOperations = exports.createModel = exports.mongooseReplicaConnection = exports.mongooseConnection = void 0;
39
- const mongoose_1 = __importStar(require("mongoose"));
30
+ const mongoose_1 = __importDefault(require("mongoose"));
40
31
  const ssm_1 = require("./ssm");
41
32
  const logger_1 = require("./logger");
42
33
  // Export commonly used mongoose types
@@ -45,36 +36,111 @@ __exportStar(require("mongoose"), exports);
45
36
  let cachedConnection;
46
37
  let cachedReplicaConnection;
47
38
  const connectionOptions = {
48
- serverSelectionTimeoutMS: 5000,
49
- socketTimeoutMS: 45000,
39
+ // Connection timeouts
40
+ serverSelectionTimeoutMS: 60000,
41
+ connectTimeoutMS: 60000,
42
+ socketTimeoutMS: 360000,
43
+ // Connection pool
44
+ maxPoolSize: 50,
45
+ minPoolSize: 5,
46
+ // Reliability settings
50
47
  retryWrites: true,
48
+ retryReads: true,
49
+ // Performance settings
50
+ heartbeatFrequencyMS: 10000,
51
+ // Monitoring
52
+ monitorCommands: true,
51
53
  };
54
+ const MAX_RETRY_ATTEMPTS = 5;
55
+ const INITIAL_RETRY_DELAY = 1000; // 1 second
56
+ const connectWithRetry = (mongoUrl, attempt = 1) => __awaiter(void 0, void 0, void 0, function* () {
57
+ try {
58
+ logger_1.logger.info(`Attempting to connect to MongoDB (attempt ${attempt}/${MAX_RETRY_ATTEMPTS})`);
59
+ const connection = mongoose_1.default.createConnection(mongoUrl, connectionOptions);
60
+ // Wait for connection to be ready
61
+ yield new Promise((resolve, reject) => {
62
+ connection.once("connected", () => resolve());
63
+ connection.once("error", (err) => reject(err));
64
+ });
65
+ return connection;
66
+ }
67
+ catch (error) {
68
+ if (attempt >= MAX_RETRY_ATTEMPTS) {
69
+ logger_1.logger.error("Max retry attempts reached. Failed to connect to MongoDB");
70
+ return null;
71
+ }
72
+ const delay = INITIAL_RETRY_DELAY * Math.pow(2, attempt - 1);
73
+ logger_1.logger.warn(`Connection attempt ${attempt} failed. Retrying in ${delay}ms...`);
74
+ yield new Promise((resolve) => setTimeout(resolve, delay));
75
+ return connectWithRetry(mongoUrl, attempt + 1);
76
+ }
77
+ });
52
78
  const mongooseConnected = () => __awaiter(void 0, void 0, void 0, function* () {
53
79
  if (cachedConnection) {
54
- logger_1.logger.info("Cached MongoDB Connection");
55
- return cachedConnection;
80
+ // Use type assertion to check connection state
81
+ if (cachedConnection.readyState ===
82
+ mongoose_1.default.ConnectionStates.connected) {
83
+ logger_1.logger.info("Using cached MongoDB Connection");
84
+ return cachedConnection;
85
+ }
86
+ // Try to reconnect using existing connection
87
+ try {
88
+ yield cachedConnection.openUri(process.env.MONGO_DATABASE_URL, connectionOptions);
89
+ if (cachedConnection.readyState ===
90
+ mongoose_1.default.ConnectionStates.connected) {
91
+ logger_1.logger.info("Successfully reconnected using cached connection");
92
+ return cachedConnection;
93
+ }
94
+ }
95
+ catch (error) {
96
+ logger_1.logger.warn("Failed to reconnect using cached connection, will create new one");
97
+ }
56
98
  }
57
99
  let mongoUrl;
58
100
  const nodeEnv = process.env.NODE_ENV;
59
- if (nodeEnv === "local") {
101
+ if (nodeEnv === "local" || nodeEnv === "development") {
60
102
  mongoUrl = process.env.MONGO_DATABASE_URL;
103
+ // Add logging to debug URL
104
+ logger_1.logger.info(`Connecting to MongoDB URL: ${mongoUrl.replace(/\/\/[^@]*@/, "//****:****@")}`);
61
105
  }
62
106
  else if (["development", "production"].includes(nodeEnv)) {
63
107
  const secrets = yield (0, ssm_1.getSecretsManagerSecret)(process.env.ENV_SECRET_NAME);
64
108
  mongoUrl = secrets.MONGO_DATABASE_URL;
65
109
  }
66
110
  else {
67
- logger_1.logger.info(`Failed to get mongoose configuration for env:${nodeEnv}`);
111
+ logger_1.logger.error(`Invalid NODE_ENV: ${nodeEnv}`);
68
112
  return null;
69
113
  }
70
114
  if (!mongoUrl) {
71
- logger_1.logger.info("MONGO_DATABASE_URL not set");
115
+ logger_1.logger.error("MONGO_DATABASE_URL not set");
72
116
  return null;
73
117
  }
74
- logger_1.logger.info("New MongoDB Connection");
75
- const connection = mongoose_1.default.createConnection(mongoUrl, connectionOptions);
118
+ logger_1.logger.info("Creating new MongoDB Connection");
119
+ const connection = yield connectWithRetry(mongoUrl);
120
+ if (!connection) {
121
+ logger_1.logger.error("Failed to establish MongoDB connection after retries");
122
+ return null;
123
+ }
124
+ // Enhanced error handling
76
125
  connection.on("error", (err) => {
77
- console.error("MongoDB connection error:", err);
126
+ logger_1.logger.error("MongoDB connection error:", err);
127
+ // Attempt to reconnect after a delay
128
+ setTimeout(() => {
129
+ if (connection.readyState !== mongoose_1.default.ConnectionStates.connected) {
130
+ logger_1.logger.info("Attempting to reconnect...");
131
+ connection.openUri(mongoUrl, connectionOptions);
132
+ }
133
+ }, 5000);
134
+ });
135
+ // Add connection monitoring
136
+ connection.on("disconnected", () => {
137
+ logger_1.logger.warn("MongoDB disconnected. Attempting to reconnect...");
138
+ });
139
+ connection.on("reconnected", () => {
140
+ logger_1.logger.info("MongoDB reconnected successfully");
141
+ });
142
+ connection.on("connected", () => {
143
+ logger_1.logger.info("MongoDB connected successfully");
78
144
  });
79
145
  cachedConnection = connection;
80
146
  return connection;
@@ -102,7 +168,11 @@ const replicaMongooseConnected = () => __awaiter(void 0, void 0, void 0, functio
102
168
  return null;
103
169
  }
104
170
  logger_1.logger.info("New MongoDB Replica Connection");
105
- const connection = mongoose_1.default.createConnection(replicaUrl, connectionOptions);
171
+ const connection = yield connectWithRetry(replicaUrl);
172
+ if (!connection) {
173
+ logger_1.logger.error("Failed to establish MongoDB replica connection after retries");
174
+ return null;
175
+ }
106
176
  connection.on("error", (err) => {
107
177
  console.error("MongoDB replica connection error:", err);
108
178
  });
@@ -127,34 +197,31 @@ mongoose_1.default.Query.prototype.paginate = function (page = 1, limit = 10) {
127
197
  exports.mongooseConnection = mongooseConnected();
128
198
  exports.mongooseReplicaConnection = replicaMongooseConnected();
129
199
  // Helper for creating typed models
130
- const createModel = (name, schema, connection = exports.mongooseConnection) => {
131
- // Add debug logging
200
+ const createModel = (name, schema) => {
132
201
  logger_1.logger.info(`Creating model ${name}`);
133
- // If we already have a cached connection, use it synchronously
134
- if (connection instanceof mongoose_1.Connection) {
135
- try {
136
- logger_1.logger.info(`Using existing connection for model ${name}`);
137
- return connection.model(name, schema);
138
- }
139
- catch (error) {
140
- logger_1.logger.info(`Error creating model ${name}:`, error);
141
- return null;
202
+ try {
203
+ // If no cached connection exists, create one
204
+ if (!cachedConnection) {
205
+ logger_1.logger.info(`No cached connection found, establishing new connection for model ${name}`);
206
+ let mongoUrl = process.env.MONGO_DATABASE_URL;
207
+ if (!mongoUrl) {
208
+ logger_1.logger.error("MONGO_DATABASE_URL not set");
209
+ return null;
210
+ }
211
+ cachedConnection = mongoose_1.default.createConnection(mongoUrl, connectionOptions);
212
+ cachedConnection.on("error", (err) => {
213
+ console.error("MongoDB connection error:", err);
214
+ });
215
+ logger_1.logger.info(`New connection established for model ${name}`);
142
216
  }
217
+ // Use the cached connection
218
+ logger_1.logger.info(`Creating model ${name} with connection`);
219
+ return cachedConnection.model(name, schema);
143
220
  }
144
- // Otherwise, handle it asynchronously
145
- return Promise.resolve(connection)
146
- .then((conn) => {
147
- if (!conn) {
148
- logger_1.logger.info(`Database connection not established for model: ${name}`);
149
- return null;
150
- }
151
- logger_1.logger.info(`Creating model ${name} with async connection`);
152
- return conn.model(name, schema);
153
- })
154
- .catch((error) => {
155
- logger_1.logger.info(`Error creating model ${name}:`, error);
221
+ catch (error) {
222
+ logger_1.logger.error(`Error creating model ${name}:`, error);
156
223
  return null;
157
- });
224
+ }
158
225
  };
159
226
  exports.createModel = createModel;
160
227
  // Helper for bulk operations with better typing
@@ -212,8 +279,8 @@ exports.upsertDocument = upsertDocument;
212
279
  const checkDatabaseHealth = () => __awaiter(void 0, void 0, void 0, function* () {
213
280
  try {
214
281
  const [primary, replica] = yield Promise.all([
215
- exports.mongooseConnection.then((conn) => conn.readyState === 1),
216
- exports.mongooseReplicaConnection.then((conn) => { var _a; return (_a = (conn === null || conn === void 0 ? void 0 : conn.readyState) === 1) !== null && _a !== void 0 ? _a : false; }),
282
+ exports.mongooseConnection.then((conn) => conn.readyState === mongoose_1.default.ConnectionStates.connected),
283
+ exports.mongooseReplicaConnection.then((conn) => { var _a; return (_a = (conn === null || conn === void 0 ? void 0 : conn.readyState) === mongoose_1.default.ConnectionStates.connected) !== null && _a !== void 0 ? _a : false; }),
217
284
  ]);
218
285
  return { primary, replica };
219
286
  }
@@ -240,3 +307,36 @@ const closeConnections = () => __awaiter(void 0, void 0, void 0, function* () {
240
307
  }
241
308
  });
242
309
  exports.closeConnections = closeConnections;
310
+ // Add this new utility function
311
+ const startConnectionMonitoring = (checkInterval = 30000) => {
312
+ let isReconnecting = false;
313
+ setInterval(() => __awaiter(void 0, void 0, void 0, function* () {
314
+ try {
315
+ if (isReconnecting)
316
+ return;
317
+ const conn = yield exports.mongooseConnection;
318
+ if (!conn ||
319
+ conn.readyState !== mongoose_1.default.ConnectionStates.connected) {
320
+ isReconnecting = true;
321
+ logger_1.logger.warn("Database connection is not healthy");
322
+ try {
323
+ if (cachedConnection) {
324
+ yield cachedConnection.openUri(process.env.MONGO_DATABASE_URL, connectionOptions);
325
+ }
326
+ }
327
+ catch (reconnectError) {
328
+ logger_1.logger.error("Failed to reconnect:", reconnectError);
329
+ }
330
+ finally {
331
+ isReconnecting = false;
332
+ }
333
+ }
334
+ }
335
+ catch (error) {
336
+ isReconnecting = false;
337
+ logger_1.logger.error("Connection monitoring error:", error);
338
+ }
339
+ }), checkInterval);
340
+ };
341
+ // Call this when your application starts
342
+ startConnectionMonitoring();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heliyos/heliyos-api-core",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Heliyos's core api functions and middlewares. Its a private package hosted on npm.",
5
5
  "main": "./dist/index.js",
6
6
  "scripts": {