@omen.foundation/node-microservice-runtime 0.1.102 → 0.1.104
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/storage.cjs +19 -6
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +21 -6
- package/dist/storage.js.map +1 -1
- package/package.json +1 -1
- package/scripts/publish-service.mjs +53 -5
package/dist/storage.cjs
CHANGED
|
@@ -122,15 +122,28 @@ class StorageService {
|
|
|
122
122
|
}
|
|
123
123
|
async getMongoClient(connectionString) {
|
|
124
124
|
const normalizedConnectionString = this.normalizeConnectionString(connectionString);
|
|
125
|
+
if (normalizedConnectionString !== connectionString) {
|
|
126
|
+
this.logger.debug('MongoDB connection string was normalized (password encoding applied)');
|
|
127
|
+
}
|
|
125
128
|
if (this.clientCache.has(normalizedConnectionString)) {
|
|
126
129
|
return this.clientCache.get(normalizedConnectionString);
|
|
127
130
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
try {
|
|
132
|
+
const client = new mongodb_1.MongoClient(normalizedConnectionString, {
|
|
133
|
+
maxPoolSize: 20,
|
|
134
|
+
});
|
|
135
|
+
await client.connect();
|
|
136
|
+
this.clientCache.set(normalizedConnectionString, client);
|
|
137
|
+
return client;
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
const sanitizedConnectionString = normalizedConnectionString.replace(/:([^:@]+)@/, ':****@');
|
|
141
|
+
this.logger.error({
|
|
142
|
+
error: error instanceof Error ? error.message : String(error),
|
|
143
|
+
connectionString: sanitizedConnectionString
|
|
144
|
+
}, 'Failed to connect to MongoDB');
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
134
147
|
}
|
|
135
148
|
async getConnectionString(storageName) {
|
|
136
149
|
const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;
|
package/dist/storage.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAe,KAAK,EAAE,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAOjE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEhF;AAED,wBAAgB,4BAA4B,IAAI,eAAe,EAAE,CAEhE;AAED,UAAU,0BAA0B;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,iBAAiB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,sBAAsB,CAAC,CAAS;gBAE5B,YAAY,EAAE,0BAA0B;IAO9C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAkBrF,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAUhG,aAAa,CAAC,SAAS,SAAS,QAAQ,EAC5C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAS3B,gBAAgB,CAAC,QAAQ,EAAE,SAAS,SAAS,QAAQ,EACzD,WAAW,EAAE,UAAU,QAAQ,EAC/B,cAAc,EAAE,UAAU,SAAS,EACnC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAYjC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;YAqEnB,cAAc;
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAAe,KAAK,EAAE,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC/E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAElD,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,CAOjE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,GAAG,SAAS,CAEhF;AAED,wBAAgB,4BAA4B,IAAI,eAAe,EAAE,CAEhE;AAED,UAAU,0BAA0B;IAClC,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,iBAAiB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAQD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAoB;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAyB;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,sBAAsB,CAAC,CAAS;gBAE5B,YAAY,EAAE,0BAA0B;IAO9C,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAkBrF,cAAc,CAAC,CAAC,EAAE,WAAW,EAAE,UAAU,CAAC,EAAE,OAAO,GAAE,wBAA6B,GAAG,OAAO,CAAC,EAAE,CAAC;IAUhG,aAAa,CAAC,SAAS,SAAS,QAAQ,EAC5C,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAS3B,gBAAgB,CAAC,QAAQ,EAAE,SAAS,SAAS,QAAQ,EACzD,WAAW,EAAE,UAAU,QAAQ,EAC/B,cAAc,EAAE,UAAU,SAAS,EACnC,OAAO,GAAE,wBAA6B,GACrC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IAYjC;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;YAqEnB,cAAc;YAkCd,mBAAmB;YAmBnB,qBAAqB;IAwBnC,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,oBAAoB;IAQtB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ/B"}
|
package/dist/storage.js
CHANGED
|
@@ -138,15 +138,30 @@ export class StorageService {
|
|
|
138
138
|
async getMongoClient(connectionString) {
|
|
139
139
|
// Normalize the connection string to ensure username/password are properly encoded
|
|
140
140
|
const normalizedConnectionString = this.normalizeConnectionString(connectionString);
|
|
141
|
+
// Log the normalization for debugging (only log if different to avoid spam)
|
|
142
|
+
if (normalizedConnectionString !== connectionString) {
|
|
143
|
+
this.logger.debug('MongoDB connection string was normalized (password encoding applied)');
|
|
144
|
+
}
|
|
141
145
|
if (this.clientCache.has(normalizedConnectionString)) {
|
|
142
146
|
return this.clientCache.get(normalizedConnectionString);
|
|
143
147
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
try {
|
|
149
|
+
const client = new MongoClient(normalizedConnectionString, {
|
|
150
|
+
maxPoolSize: 20,
|
|
151
|
+
});
|
|
152
|
+
await client.connect();
|
|
153
|
+
this.clientCache.set(normalizedConnectionString, client);
|
|
154
|
+
return client;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
// If connection fails, log the error with connection string details (without password)
|
|
158
|
+
const sanitizedConnectionString = normalizedConnectionString.replace(/:([^:@]+)@/, ':****@');
|
|
159
|
+
this.logger.error({
|
|
160
|
+
error: error instanceof Error ? error.message : String(error),
|
|
161
|
+
connectionString: sanitizedConnectionString
|
|
162
|
+
}, 'Failed to connect to MongoDB');
|
|
163
|
+
throw error;
|
|
164
|
+
}
|
|
150
165
|
}
|
|
151
166
|
async getConnectionString(storageName) {
|
|
152
167
|
const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;
|
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Logger } from 'pino';\r\nimport { MongoClient, type Db, type Collection, type Document } from 'mongodb';\r\nimport type { BoundBeamApi } from './services.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport type { HttpRequester } from 'beamable-sdk';\r\n\r\nexport interface StorageConnectionOptions {\r\n useCache?: boolean;\r\n}\r\n\r\nexport interface StorageCollectionOptions extends StorageConnectionOptions {\r\n collectionName?: string;\r\n}\r\n\r\nexport interface StorageMetadata {\r\n storageName: string;\r\n}\r\n\r\nconst STORAGE_OBJECT_METADATA = new Map<Function, StorageMetadata>();\r\n\r\nexport function StorageObject(storageName: string): ClassDecorator {\r\n if (!storageName || !storageName.trim()) {\r\n throw new Error('@StorageObject requires a non-empty storage name.');\r\n }\r\n return (target) => {\r\n STORAGE_OBJECT_METADATA.set(target, { storageName: storageName.trim() });\r\n };\r\n}\r\n\r\nexport function getStorageMetadata(target: Function): StorageMetadata | undefined {\r\n return STORAGE_OBJECT_METADATA.get(target);\r\n}\r\n\r\nexport function listRegisteredStorageObjects(): StorageMetadata[] {\r\n return Array.from(STORAGE_OBJECT_METADATA.values());\r\n}\r\n\r\ninterface StorageServiceDependencies {\r\n requester: HttpRequester;\r\n api: BoundBeamApi;\r\n env: EnvironmentConfig;\r\n logger: Logger;\r\n}\r\n\r\ninterface ConnectionStringResponse {\r\n connectionString: string;\r\n}\r\n\r\nconst CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';\r\n\r\nexport class StorageService {\r\n private readonly requester: HttpRequester;\r\n private readonly api: BoundBeamApi;\r\n private readonly env: EnvironmentConfig;\r\n private readonly logger: Logger;\r\n private readonly databaseCache = new Map<string, Db>();\r\n private readonly clientCache = new Map<string, MongoClient>();\r\n private cachedConnectionString?: string;\r\n\r\n constructor(dependencies: StorageServiceDependencies) {\r\n this.requester = dependencies.requester;\r\n this.api = dependencies.api;\r\n this.env = dependencies.env;\r\n this.logger = dependencies.logger.child({ component: 'StorageService' });\r\n }\r\n\r\n async getDatabase(storageName: string, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const normalized = this.normalizeStorageName(storageName);\r\n if (!options.useCache) {\r\n this.databaseCache.delete(normalized);\r\n }\r\n const cached = this.databaseCache.get(normalized);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n const connectionString = await this.getConnectionString(normalized);\r\n const client = await this.getMongoClient(connectionString);\r\n const databaseName = this.buildDatabaseName(normalized);\r\n const database = client.db(databaseName);\r\n this.databaseCache.set(normalized, database);\r\n return database;\r\n }\r\n\r\n async getDatabaseFor<T>(storageCtor: new () => T, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n return this.getDatabase(metadata.storageName, options);\r\n }\r\n\r\n async getCollection<TDocument extends Document>(\r\n storageName: string,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const database = await this.getDatabase(storageName, options);\r\n const collectionName = options.collectionName?.trim();\r\n if (!collectionName) {\r\n throw new Error('Collection name must be provided when using getCollection with raw storage name.');\r\n }\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n async getCollectionFor<TStorage, TDocument extends Document>(\r\n storageCtor: new () => TStorage,\r\n collectionCtor: new () => TDocument,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n const collectionName = options.collectionName?.trim() ?? collectionCtor.name;\r\n const database = await this.getDatabase(metadata.storageName, options);\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n /**\r\n * Normalizes a MongoDB connection string by ensuring username and password are properly URL-encoded.\r\n * This prevents \"Password contains unescaped characters\" errors when passwords contain special characters.\r\n * \r\n * Handles both mongodb:// and mongodb+srv:// formats.\r\n * Safely handles already-encoded credentials by decoding first, then re-encoding.\r\n */\r\n private normalizeConnectionString(connectionString: string): string {\r\n try {\r\n // Match MongoDB connection string format: mongodb:// or mongodb+srv://\r\n // Format: mongodb[+srv]://[username:password@]host[:port][/database][?options]\r\n const mongoUriRegex = /^(mongodb(?:\\+srv)?:\\/\\/)(?:([^:@]+)(?::([^@]+))?@)?([^\\/?]+)(?:\\/([^?]*))?(?:\\?(.*))?$/;\r\n const match = connectionString.match(mongoUriRegex);\r\n \r\n if (!match) {\r\n // If it doesn't match the expected format, return as-is (might be a different format)\r\n this.logger.warn('Connection string does not match expected MongoDB URI format, using as-is');\r\n return connectionString;\r\n }\r\n\r\n const [, protocol, username, password, host, database, options] = match;\r\n \r\n // If no username/password, return as-is\r\n if (!username) {\r\n return connectionString;\r\n }\r\n\r\n // Decode username and password first (in case they're already encoded)\r\n // Then re-encode them properly to ensure special characters are handled\r\n let decodedUsername: string;\r\n let decodedPassword: string | undefined;\r\n \r\n try {\r\n decodedUsername = decodeURIComponent(username);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedUsername = username;\r\n }\r\n \r\n if (password) {\r\n try {\r\n decodedPassword = decodeURIComponent(password);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedPassword = password;\r\n }\r\n }\r\n\r\n // Now encode them properly (encodeURIComponent handles all special characters)\r\n const encodedUsername = encodeURIComponent(decodedUsername);\r\n const encodedPassword = decodedPassword ? encodeURIComponent(decodedPassword) : undefined;\r\n\r\n // Reconstruct the connection string\r\n let normalized = `${protocol}${encodedUsername}`;\r\n if (encodedPassword) {\r\n normalized += `:${encodedPassword}`;\r\n }\r\n normalized += `@${host}`;\r\n if (database) {\r\n normalized += `/${database}`;\r\n }\r\n if (options) {\r\n normalized += `?${options}`;\r\n }\r\n\r\n return normalized;\r\n } catch (error) {\r\n // If parsing fails, log warning and return original\r\n this.logger.warn(\r\n { error: error instanceof Error ? error.message : String(error) },\r\n 'Failed to normalize MongoDB connection string, using as-is'\r\n );\r\n return connectionString;\r\n }\r\n }\r\n\r\n private async getMongoClient(connectionString: string): Promise<MongoClient> {\r\n // Normalize the connection string to ensure username/password are properly encoded\r\n const normalizedConnectionString = this.normalizeConnectionString(connectionString);\r\n \r\n if (this.clientCache.has(normalizedConnectionString)) {\r\n return this.clientCache.get(normalizedConnectionString) as MongoClient;\r\n }\r\n const client = new MongoClient(normalizedConnectionString, {\r\n maxPoolSize: 20,\r\n });\r\n await client.connect();\r\n this.clientCache.set(normalizedConnectionString, client);\r\n return client;\r\n }\r\n\r\n private async getConnectionString(storageName: string): Promise<string> {\r\n const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;\r\n const envValue = process.env[variableName];\r\n if (envValue && envValue.trim()) {\r\n return envValue.trim();\r\n }\r\n\r\n if (this.cachedConnectionString) {\r\n return this.cachedConnectionString;\r\n }\r\n\r\n const response = await this.fetchConnectionString();\r\n if (!response.connectionString || !response.connectionString.trim()) {\r\n throw new Error(`Connection string for storage \"${storageName}\" is empty.`);\r\n }\r\n this.cachedConnectionString = response.connectionString.trim();\r\n return this.cachedConnectionString;\r\n }\r\n\r\n private async fetchConnectionString(): Promise<ConnectionStringResponse> {\r\n if (typeof this.api.beamoGetStorageConnectionBasic === 'function') {\r\n const result = await this.api.beamoGetStorageConnectionBasic();\r\n if (result && typeof result === 'object' && 'body' in result) {\r\n return (result as { body: ConnectionStringResponse }).body;\r\n }\r\n return result as ConnectionStringResponse;\r\n }\r\n\r\n this.logger.warn(\r\n 'beamable-sdk does not expose beamoGetStorageConnectionBasic; falling back to manual requester call.',\r\n );\r\n const response = await this.requester.request({\r\n method: 'GET',\r\n url: '/basic/beamo/storage/connection',\r\n withAuth: true,\r\n });\r\n const body = response.body as ConnectionStringResponse | undefined;\r\n if (!body || typeof body.connectionString !== 'string') {\r\n throw new Error('Failed to retrieve Beamable storage connection string.');\r\n }\r\n return body;\r\n }\r\n\r\n private buildDatabaseName(storageName: string): string {\r\n const cid = this.sanitize(this.env.cid);\r\n const pid = this.sanitize(this.env.pid);\r\n const storage = this.sanitize(storageName);\r\n return `${cid}${pid}_${storage}`;\r\n }\r\n\r\n private sanitize(value: string): string {\r\n return value.replace(/[^A-Za-z0-9_]/g, '_');\r\n }\r\n\r\n private normalizeStorageName(storageName: string): string {\r\n const normalized = storageName.trim();\r\n if (!normalized) {\r\n throw new Error('Storage name cannot be empty.');\r\n }\r\n return normalized;\r\n }\r\n\r\n async dispose(): Promise<void> {\r\n for (const client of this.clientCache.values()) {\r\n await client.close();\r\n }\r\n this.clientCache.clear();\r\n this.databaseCache.clear();\r\n this.cachedConnectionString = undefined;\r\n }\r\n}\r\n\r\n"]}
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Logger } from 'pino';\r\nimport { MongoClient, type Db, type Collection, type Document } from 'mongodb';\r\nimport type { BoundBeamApi } from './services.js';\r\nimport type { EnvironmentConfig } from './types.js';\r\nimport type { HttpRequester } from 'beamable-sdk';\r\n\r\nexport interface StorageConnectionOptions {\r\n useCache?: boolean;\r\n}\r\n\r\nexport interface StorageCollectionOptions extends StorageConnectionOptions {\r\n collectionName?: string;\r\n}\r\n\r\nexport interface StorageMetadata {\r\n storageName: string;\r\n}\r\n\r\nconst STORAGE_OBJECT_METADATA = new Map<Function, StorageMetadata>();\r\n\r\nexport function StorageObject(storageName: string): ClassDecorator {\r\n if (!storageName || !storageName.trim()) {\r\n throw new Error('@StorageObject requires a non-empty storage name.');\r\n }\r\n return (target) => {\r\n STORAGE_OBJECT_METADATA.set(target, { storageName: storageName.trim() });\r\n };\r\n}\r\n\r\nexport function getStorageMetadata(target: Function): StorageMetadata | undefined {\r\n return STORAGE_OBJECT_METADATA.get(target);\r\n}\r\n\r\nexport function listRegisteredStorageObjects(): StorageMetadata[] {\r\n return Array.from(STORAGE_OBJECT_METADATA.values());\r\n}\r\n\r\ninterface StorageServiceDependencies {\r\n requester: HttpRequester;\r\n api: BoundBeamApi;\r\n env: EnvironmentConfig;\r\n logger: Logger;\r\n}\r\n\r\ninterface ConnectionStringResponse {\r\n connectionString: string;\r\n}\r\n\r\nconst CONNECTION_STRING_ENV_PREFIX = 'STORAGE_CONNSTR_';\r\n\r\nexport class StorageService {\r\n private readonly requester: HttpRequester;\r\n private readonly api: BoundBeamApi;\r\n private readonly env: EnvironmentConfig;\r\n private readonly logger: Logger;\r\n private readonly databaseCache = new Map<string, Db>();\r\n private readonly clientCache = new Map<string, MongoClient>();\r\n private cachedConnectionString?: string;\r\n\r\n constructor(dependencies: StorageServiceDependencies) {\r\n this.requester = dependencies.requester;\r\n this.api = dependencies.api;\r\n this.env = dependencies.env;\r\n this.logger = dependencies.logger.child({ component: 'StorageService' });\r\n }\r\n\r\n async getDatabase(storageName: string, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const normalized = this.normalizeStorageName(storageName);\r\n if (!options.useCache) {\r\n this.databaseCache.delete(normalized);\r\n }\r\n const cached = this.databaseCache.get(normalized);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n const connectionString = await this.getConnectionString(normalized);\r\n const client = await this.getMongoClient(connectionString);\r\n const databaseName = this.buildDatabaseName(normalized);\r\n const database = client.db(databaseName);\r\n this.databaseCache.set(normalized, database);\r\n return database;\r\n }\r\n\r\n async getDatabaseFor<T>(storageCtor: new () => T, options: StorageConnectionOptions = {}): Promise<Db> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n return this.getDatabase(metadata.storageName, options);\r\n }\r\n\r\n async getCollection<TDocument extends Document>(\r\n storageName: string,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const database = await this.getDatabase(storageName, options);\r\n const collectionName = options.collectionName?.trim();\r\n if (!collectionName) {\r\n throw new Error('Collection name must be provided when using getCollection with raw storage name.');\r\n }\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n async getCollectionFor<TStorage, TDocument extends Document>(\r\n storageCtor: new () => TStorage,\r\n collectionCtor: new () => TDocument,\r\n options: StorageCollectionOptions = {},\r\n ): Promise<Collection<TDocument>> {\r\n const metadata = getStorageMetadata(storageCtor);\r\n if (!metadata) {\r\n throw new Error(\r\n `Storage metadata for ${storageCtor.name} not found. Did you decorate the class with @StorageObject('Name')?`,\r\n );\r\n }\r\n const collectionName = options.collectionName?.trim() ?? collectionCtor.name;\r\n const database = await this.getDatabase(metadata.storageName, options);\r\n return database.collection<TDocument>(collectionName);\r\n }\r\n\r\n /**\r\n * Normalizes a MongoDB connection string by ensuring username and password are properly URL-encoded.\r\n * This prevents \"Password contains unescaped characters\" errors when passwords contain special characters.\r\n * \r\n * Handles both mongodb:// and mongodb+srv:// formats.\r\n * Safely handles already-encoded credentials by decoding first, then re-encoding.\r\n */\r\n private normalizeConnectionString(connectionString: string): string {\r\n try {\r\n // Match MongoDB connection string format: mongodb:// or mongodb+srv://\r\n // Format: mongodb[+srv]://[username:password@]host[:port][/database][?options]\r\n const mongoUriRegex = /^(mongodb(?:\\+srv)?:\\/\\/)(?:([^:@]+)(?::([^@]+))?@)?([^\\/?]+)(?:\\/([^?]*))?(?:\\?(.*))?$/;\r\n const match = connectionString.match(mongoUriRegex);\r\n \r\n if (!match) {\r\n // If it doesn't match the expected format, return as-is (might be a different format)\r\n this.logger.warn('Connection string does not match expected MongoDB URI format, using as-is');\r\n return connectionString;\r\n }\r\n\r\n const [, protocol, username, password, host, database, options] = match;\r\n \r\n // If no username/password, return as-is\r\n if (!username) {\r\n return connectionString;\r\n }\r\n\r\n // Decode username and password first (in case they're already encoded)\r\n // Then re-encode them properly to ensure special characters are handled\r\n let decodedUsername: string;\r\n let decodedPassword: string | undefined;\r\n \r\n try {\r\n decodedUsername = decodeURIComponent(username);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedUsername = username;\r\n }\r\n \r\n if (password) {\r\n try {\r\n decodedPassword = decodeURIComponent(password);\r\n } catch {\r\n // If decoding fails, assume it's not encoded and use as-is\r\n decodedPassword = password;\r\n }\r\n }\r\n\r\n // Now encode them properly (encodeURIComponent handles all special characters)\r\n const encodedUsername = encodeURIComponent(decodedUsername);\r\n const encodedPassword = decodedPassword ? encodeURIComponent(decodedPassword) : undefined;\r\n\r\n // Reconstruct the connection string\r\n let normalized = `${protocol}${encodedUsername}`;\r\n if (encodedPassword) {\r\n normalized += `:${encodedPassword}`;\r\n }\r\n normalized += `@${host}`;\r\n if (database) {\r\n normalized += `/${database}`;\r\n }\r\n if (options) {\r\n normalized += `?${options}`;\r\n }\r\n\r\n return normalized;\r\n } catch (error) {\r\n // If parsing fails, log warning and return original\r\n this.logger.warn(\r\n { error: error instanceof Error ? error.message : String(error) },\r\n 'Failed to normalize MongoDB connection string, using as-is'\r\n );\r\n return connectionString;\r\n }\r\n }\r\n\r\n private async getMongoClient(connectionString: string): Promise<MongoClient> {\r\n // Normalize the connection string to ensure username/password are properly encoded\r\n const normalizedConnectionString = this.normalizeConnectionString(connectionString);\r\n \r\n // Log the normalization for debugging (only log if different to avoid spam)\r\n if (normalizedConnectionString !== connectionString) {\r\n this.logger.debug('MongoDB connection string was normalized (password encoding applied)');\r\n }\r\n \r\n if (this.clientCache.has(normalizedConnectionString)) {\r\n return this.clientCache.get(normalizedConnectionString) as MongoClient;\r\n }\r\n \r\n try {\r\n const client = new MongoClient(normalizedConnectionString, {\r\n maxPoolSize: 20,\r\n });\r\n await client.connect();\r\n this.clientCache.set(normalizedConnectionString, client);\r\n return client;\r\n } catch (error) {\r\n // If connection fails, log the error with connection string details (without password)\r\n const sanitizedConnectionString = normalizedConnectionString.replace(/:([^:@]+)@/, ':****@');\r\n this.logger.error(\r\n { \r\n error: error instanceof Error ? error.message : String(error),\r\n connectionString: sanitizedConnectionString \r\n },\r\n 'Failed to connect to MongoDB'\r\n );\r\n throw error;\r\n }\r\n }\r\n\r\n private async getConnectionString(storageName: string): Promise<string> {\r\n const variableName = `${CONNECTION_STRING_ENV_PREFIX}${storageName}`;\r\n const envValue = process.env[variableName];\r\n if (envValue && envValue.trim()) {\r\n return envValue.trim();\r\n }\r\n\r\n if (this.cachedConnectionString) {\r\n return this.cachedConnectionString;\r\n }\r\n\r\n const response = await this.fetchConnectionString();\r\n if (!response.connectionString || !response.connectionString.trim()) {\r\n throw new Error(`Connection string for storage \"${storageName}\" is empty.`);\r\n }\r\n this.cachedConnectionString = response.connectionString.trim();\r\n return this.cachedConnectionString;\r\n }\r\n\r\n private async fetchConnectionString(): Promise<ConnectionStringResponse> {\r\n if (typeof this.api.beamoGetStorageConnectionBasic === 'function') {\r\n const result = await this.api.beamoGetStorageConnectionBasic();\r\n if (result && typeof result === 'object' && 'body' in result) {\r\n return (result as { body: ConnectionStringResponse }).body;\r\n }\r\n return result as ConnectionStringResponse;\r\n }\r\n\r\n this.logger.warn(\r\n 'beamable-sdk does not expose beamoGetStorageConnectionBasic; falling back to manual requester call.',\r\n );\r\n const response = await this.requester.request({\r\n method: 'GET',\r\n url: '/basic/beamo/storage/connection',\r\n withAuth: true,\r\n });\r\n const body = response.body as ConnectionStringResponse | undefined;\r\n if (!body || typeof body.connectionString !== 'string') {\r\n throw new Error('Failed to retrieve Beamable storage connection string.');\r\n }\r\n return body;\r\n }\r\n\r\n private buildDatabaseName(storageName: string): string {\r\n const cid = this.sanitize(this.env.cid);\r\n const pid = this.sanitize(this.env.pid);\r\n const storage = this.sanitize(storageName);\r\n return `${cid}${pid}_${storage}`;\r\n }\r\n\r\n private sanitize(value: string): string {\r\n return value.replace(/[^A-Za-z0-9_]/g, '_');\r\n }\r\n\r\n private normalizeStorageName(storageName: string): string {\r\n const normalized = storageName.trim();\r\n if (!normalized) {\r\n throw new Error('Storage name cannot be empty.');\r\n }\r\n return normalized;\r\n }\r\n\r\n async dispose(): Promise<void> {\r\n for (const client of this.clientCache.values()) {\r\n await client.close();\r\n }\r\n this.clientCache.clear();\r\n this.databaseCache.clear();\r\n this.cachedConnectionString = undefined;\r\n }\r\n}\r\n\r\n"]}
|
package/package.json
CHANGED
|
@@ -747,15 +747,39 @@ async function fetchCurrentManifest(apiHost, token, cid, pid) {
|
|
|
747
747
|
return response.json();
|
|
748
748
|
}
|
|
749
749
|
|
|
750
|
-
async function discoverStorageObjects(srcDir) {
|
|
750
|
+
async function discoverStorageObjects(srcDir, cwd = process.cwd()) {
|
|
751
751
|
const storageObjects = [];
|
|
752
752
|
try {
|
|
753
|
-
|
|
753
|
+
// Resolve src path relative to current working directory (where publish is run from)
|
|
754
|
+
const srcPath = path.isAbsolute(srcDir) ? srcDir : path.resolve(cwd, srcDir || 'src');
|
|
755
|
+
|
|
756
|
+
console.log(`[beamo-node] Searching for @StorageObject decorators in: ${srcPath}`);
|
|
757
|
+
|
|
758
|
+
// Check if directory exists
|
|
759
|
+
try {
|
|
760
|
+
const stats = await fs.stat(srcPath);
|
|
761
|
+
if (!stats.isDirectory()) {
|
|
762
|
+
console.warn(`[beamo-node] Warning: ${srcPath} is not a directory`);
|
|
763
|
+
return storageObjects;
|
|
764
|
+
}
|
|
765
|
+
} catch (error) {
|
|
766
|
+
console.warn(`[beamo-node] Warning: Directory ${srcPath} does not exist`);
|
|
767
|
+
return storageObjects;
|
|
768
|
+
}
|
|
769
|
+
|
|
754
770
|
const files = await getAllTypeScriptFiles(srcPath);
|
|
755
771
|
|
|
772
|
+
if (files.length === 0) {
|
|
773
|
+
console.warn(`[beamo-node] Warning: No TypeScript files found in ${srcPath}`);
|
|
774
|
+
return storageObjects;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
console.log(`[beamo-node] Scanning ${files.length} TypeScript file(s) for @StorageObject decorators...`);
|
|
778
|
+
|
|
756
779
|
for (const file of files) {
|
|
757
780
|
const content = await fs.readFile(file, 'utf-8');
|
|
758
|
-
// Match @StorageObject('StorageName') pattern
|
|
781
|
+
// Match @StorageObject('StorageName') pattern - handle both single and double quotes
|
|
782
|
+
// Also match multiline patterns where decorator might be on a different line
|
|
759
783
|
const storageRegex = /@StorageObject\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
760
784
|
let match;
|
|
761
785
|
while ((match = storageRegex.exec(content)) !== null) {
|
|
@@ -767,12 +791,21 @@ async function discoverStorageObjects(srcDir) {
|
|
|
767
791
|
checksum: null,
|
|
768
792
|
archived: false,
|
|
769
793
|
});
|
|
794
|
+
console.log(`[beamo-node] ✓ Discovered storage object: ${storageName} (from ${path.relative(cwd, file)})`);
|
|
770
795
|
}
|
|
771
796
|
}
|
|
772
797
|
}
|
|
798
|
+
|
|
799
|
+
if (storageObjects.length === 0) {
|
|
800
|
+
console.log(`[beamo-node] No @StorageObject decorators found in ${files.length} file(s) in ${srcPath}`);
|
|
801
|
+
console.log(`[beamo-node] Make sure your storage classes are decorated with @StorageObject('StorageName')`);
|
|
802
|
+
}
|
|
773
803
|
} catch (error) {
|
|
804
|
+
console.warn(`[beamo-node] Error discovering storage objects: ${error instanceof Error ? error.message : String(error)}`);
|
|
805
|
+
if (error instanceof Error && error.stack) {
|
|
806
|
+
console.warn(`[beamo-node] Stack: ${error.stack}`);
|
|
807
|
+
}
|
|
774
808
|
// If we can't discover storage, that's okay - we'll just use existing ones
|
|
775
|
-
// Debug logging only
|
|
776
809
|
}
|
|
777
810
|
return storageObjects;
|
|
778
811
|
}
|
|
@@ -1013,6 +1046,16 @@ async function updateManifest({
|
|
|
1013
1046
|
manifest: mappedServices,
|
|
1014
1047
|
storageReferences,
|
|
1015
1048
|
};
|
|
1049
|
+
|
|
1050
|
+
// Log storage references being sent to backend
|
|
1051
|
+
if (storageReferences.length > 0) {
|
|
1052
|
+
console.log(`[beamo-node] Publishing ${storageReferences.length} storage reference(s) in manifest:`);
|
|
1053
|
+
storageReferences.forEach(s => {
|
|
1054
|
+
console.log(`[beamo-node] - ${s.id} (type: ${s.storageType || 'mongov1'}, enabled: ${s.enabled !== false})`);
|
|
1055
|
+
});
|
|
1056
|
+
} else {
|
|
1057
|
+
console.warn(`[beamo-node] Warning: No storage references in manifest. Database will not be created automatically.`);
|
|
1058
|
+
}
|
|
1016
1059
|
|
|
1017
1060
|
const publishUrl = new URL('/basic/beamo/manifest', apiHost);
|
|
1018
1061
|
const publishHeaders = {
|
|
@@ -1439,7 +1482,7 @@ async function main() {
|
|
|
1439
1482
|
progress.start('Discovering storage objects and components');
|
|
1440
1483
|
// shortImageId already defined above from verification step
|
|
1441
1484
|
const existingManifest = await fetchCurrentManifest(apiHost, token, cid, pid);
|
|
1442
|
-
const discoveredStorage = await discoverStorageObjects('src');
|
|
1485
|
+
const discoveredStorage = await discoverStorageObjects('src', process.cwd());
|
|
1443
1486
|
const discoveredComponents = await discoverFederationComponents('src');
|
|
1444
1487
|
// Dependencies are ServiceDependencyReference objects with id and storageType
|
|
1445
1488
|
// storageType should be "mongov1" for MongoDB storage objects (matching C# microservices)
|
|
@@ -1447,6 +1490,11 @@ async function main() {
|
|
|
1447
1490
|
id: s.id,
|
|
1448
1491
|
storageType: 'mongov1', // MongoDB storage type (matches ServiceStorageReference in backend)
|
|
1449
1492
|
}));
|
|
1493
|
+
|
|
1494
|
+
if (discoveredStorage.length > 0) {
|
|
1495
|
+
console.log(`[beamo-node] Discovered ${discoveredStorage.length} storage object(s): ${discoveredStorage.map(s => s.id).join(', ')}`);
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1450
1498
|
progress.complete('Storage and components discovered');
|
|
1451
1499
|
|
|
1452
1500
|
// Step 8: Publish manifest
|