@xyo-network/module-abstract-mongodb 3.18.2 → 3.18.3

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.
@@ -249,11 +249,16 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
249
249
  * Ensures any indexes specified within the config are created. This method should be idempotent
250
250
  * allowing for multiple calls without causing errors while ensuring the desired state.
251
251
  */
252
- async ensureIndexes() {
253
- const configIndexes = this.config?.storage?.indexes ?? [];
252
+ async ensureCollection() {
253
+ const { max } = this.config;
254
254
  const payloadCollectionName = this.payloadSdkConfig.collection;
255
255
  const payloadStandardIndexes = standardIndexes2.map((ix) => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }));
256
- await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes, ...configIndexes]);
256
+ if (max) {
257
+ await ensureCappedCollection(this.payloads, max);
258
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
259
+ } else {
260
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
261
+ }
257
262
  }
258
263
  };
259
264
  __publicField(MongoModuleBase, "labels", MongoDBStorageClassLabels2);
@@ -262,6 +267,10 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
262
267
  ], MongoModuleBase);
263
268
  return MongoModuleBase;
264
269
  };
270
+ var collectionExists = async (db, name) => {
271
+ const collections = await db.listCollections({ name }).toArray();
272
+ return collections.length > 0;
273
+ };
265
274
  var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
266
275
  await sdk.useCollection(async (collection) => {
267
276
  const collectionName = collection.collectionName.toLowerCase();
@@ -282,6 +291,46 @@ var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
282
291
  }
283
292
  });
284
293
  };
294
+ var ensureCappedCollection = async (sdk, max, docSize = 1024) => {
295
+ await sdk.useCollection(async (collection) => {
296
+ const name = collection.collectionName.toLowerCase();
297
+ await sdk.useMongo(async (client) => {
298
+ const db = client.db(collection.dbName);
299
+ const exists2 = await collectionExists(db, name);
300
+ const size = docSize * max;
301
+ return exists2 ? await ensureExistingCollectionIsCapped(sdk, max, size) : await db.createCollection(name, {
302
+ capped: true,
303
+ size,
304
+ max
305
+ });
306
+ });
307
+ });
308
+ };
309
+ var ensureExistingCollectionIsCapped = async (sdk, max, docSize = 1024) => {
310
+ await sdk.useCollection(async (collection) => {
311
+ const name = collection.collectionName.toLowerCase();
312
+ await sdk.useMongo(async (client) => {
313
+ const db = client.db(collection.dbName);
314
+ const exists2 = await collectionExists(db, name);
315
+ if (!exists2) throw new Error(`Collection '${name}' does not exist`);
316
+ const size = docSize * max;
317
+ const stats = await db.command({ collStats: name });
318
+ if (stats.capped && stats.max === max && stats.maxSize === size) {
319
+ return;
320
+ }
321
+ const tmpName = `${name}_tmp_capped`;
322
+ await db.createCollection(tmpName, {
323
+ capped: true,
324
+ size,
325
+ max
326
+ });
327
+ const docs = await collection.find().sort({ $natural: -1 }).limit(max).toArray();
328
+ if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed());
329
+ await collection.drop();
330
+ await db.collection(tmpName).rename(name);
331
+ });
332
+ });
333
+ };
285
334
 
286
335
  // src/util/dbProperty.ts
287
336
  var escapeChar = "#";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":"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","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements"]}
1
+ {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { Db, MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureCollection(): Promise<void> {\n const { max } = this.config\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n\n if (max) {\n // Create capped collection if it doesn't exist or convert it if it does\n await ensureCappedCollection(this.payloads, max)\n // Recreate indexes after creating/converting a capped collection as\n // capped will remove all indexes on existing collections.\n // https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n } else {\n // Create indexes (creates collection without having to write data to it)\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n }\n }\n }\n return MongoModuleBase\n}\n\nconst collectionExists = async (db: Db, name: string): Promise<boolean> => {\n const collections = await db.listCollections({ name }).toArray()\n return collections.length > 0\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n\n/**\n * Ensures that a collection is capped with a max document count and a reasonable size.\n * If the collection exists and is not capped, it will be converted.\n * If it doesn't exist, it will be created.\n *\n * @param name The name of the collection.\n * @param count The maximum number of documents to retain.\n * @param documentSize Estimated average document size in bytes if collection is empty.\n */\nconst ensureCappedCollection = async (sdk: BaseMongoSdk<PayloadWithMongoMeta>, max: number, docSize = 1024) => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n const size = docSize * max\n return exists\n ? await ensureExistingCollectionIsCapped(sdk, max, size)\n // Create capped collection\n : await db.createCollection(name, {\n capped: true, size, max,\n })\n })\n })\n}\n\n/**\n * Converts an existing collection to a capped collection with a max document count.\n * Since MongoDB doesn't support `max` in `convertToCapped` or `cloneCollectionAsCapped`,\n * this function recreates the collection to work around Mongo's limitations.\n * https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n * https://www.mongodb.com/docs/manual/reference/command/clonecollectionascapped/\n * @param db - The MongoDB database instance\n * @param name - The name of the collection to convert\n * @param max - The maximum number of documents to retain\n * @param docSize - Fallback size (in bytes) to use if no documents exist (default 1KB)\n */\nconst ensureExistingCollectionIsCapped = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n max: number,\n docSize = 1024,\n): Promise<void> => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n if (!exists) throw new Error(`Collection '${name}' does not exist`)\n\n const size = docSize * max\n\n const stats = await db.command({ collStats: name })\n if (stats.capped && stats.max === max && stats.maxSize === size) {\n return\n }\n\n const tmpName = `${name}_tmp_capped`\n\n // Create new capped collection\n await db.createCollection(tmpName, {\n capped: true, size, max,\n })\n\n // Copy most recent documents\n const docs = await collection\n .find()\n .sort({ $natural: -1 })\n .limit(max)\n .toArray()\n\n if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed())\n\n // Replace old collection\n await collection.drop()\n await db.collection(tmpName).rename(name)\n })\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":"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","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements","exists"]}
@@ -249,11 +249,16 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
249
249
  * Ensures any indexes specified within the config are created. This method should be idempotent
250
250
  * allowing for multiple calls without causing errors while ensuring the desired state.
251
251
  */
252
- async ensureIndexes() {
253
- const configIndexes = this.config?.storage?.indexes ?? [];
252
+ async ensureCollection() {
253
+ const { max } = this.config;
254
254
  const payloadCollectionName = this.payloadSdkConfig.collection;
255
255
  const payloadStandardIndexes = standardIndexes2.map((ix) => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }));
256
- await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes, ...configIndexes]);
256
+ if (max) {
257
+ await ensureCappedCollection(this.payloads, max);
258
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
259
+ } else {
260
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
261
+ }
257
262
  }
258
263
  };
259
264
  __publicField(MongoModuleBase, "labels", MongoDBStorageClassLabels2);
@@ -262,6 +267,10 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
262
267
  ], MongoModuleBase);
263
268
  return MongoModuleBase;
264
269
  };
270
+ var collectionExists = async (db, name) => {
271
+ const collections = await db.listCollections({ name }).toArray();
272
+ return collections.length > 0;
273
+ };
265
274
  var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
266
275
  await sdk.useCollection(async (collection) => {
267
276
  const collectionName = collection.collectionName.toLowerCase();
@@ -282,6 +291,46 @@ var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
282
291
  }
283
292
  });
284
293
  };
294
+ var ensureCappedCollection = async (sdk, max, docSize = 1024) => {
295
+ await sdk.useCollection(async (collection) => {
296
+ const name = collection.collectionName.toLowerCase();
297
+ await sdk.useMongo(async (client) => {
298
+ const db = client.db(collection.dbName);
299
+ const exists2 = await collectionExists(db, name);
300
+ const size = docSize * max;
301
+ return exists2 ? await ensureExistingCollectionIsCapped(sdk, max, size) : await db.createCollection(name, {
302
+ capped: true,
303
+ size,
304
+ max
305
+ });
306
+ });
307
+ });
308
+ };
309
+ var ensureExistingCollectionIsCapped = async (sdk, max, docSize = 1024) => {
310
+ await sdk.useCollection(async (collection) => {
311
+ const name = collection.collectionName.toLowerCase();
312
+ await sdk.useMongo(async (client) => {
313
+ const db = client.db(collection.dbName);
314
+ const exists2 = await collectionExists(db, name);
315
+ if (!exists2) throw new Error(`Collection '${name}' does not exist`);
316
+ const size = docSize * max;
317
+ const stats = await db.command({ collStats: name });
318
+ if (stats.capped && stats.max === max && stats.maxSize === size) {
319
+ return;
320
+ }
321
+ const tmpName = `${name}_tmp_capped`;
322
+ await db.createCollection(tmpName, {
323
+ capped: true,
324
+ size,
325
+ max
326
+ });
327
+ const docs = await collection.find().sort({ $natural: -1 }).limit(max).toArray();
328
+ if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed());
329
+ await collection.drop();
330
+ await db.collection(tmpName).rename(name);
331
+ });
332
+ });
333
+ };
285
334
 
286
335
  // src/util/dbProperty.ts
287
336
  var escapeChar = "#";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":";;;;;;;;;;;;;;AACO,IAAM,cAAc;AAAA,EACzB,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,OAAO;AACT;;;ACRA,SAAS,gBAAgB;AAEzB,SAAS,oBAAoB;;;ACGtB,IAAM,mBAAmB,MAAgB;AAC9C,QAAM,MAAgB,CAAC;AACvB,MAAI,QAAQ,IAAI,yBAAyB;AACvC,QAAI,0BAA0B,QAAQ,IAAI;AAAA,EAC5C;AACA,MAAI,QAAQ,IAAI,cAAc;AAC5B,QAAI,iBAAiB,QAAQ,IAAI;AACjC,QAAI,eAAe,QAAQ,IAAI;AAC/B,QAAI,iBAAiB,QAAQ,IAAI;AACjC,QAAI,iBAAiB,QAAQ,IAAI;AAAA,EACnC;AACA,SAAO;AACT;;;ADVO,IAAM,+BAA+B,MAAiC;AAC3E,QAAM,MAAM,iBAAiB;AAC7B,SAAO;AAAA,IACL,oBAAoB,IAAI;AAAA,IACxB,UAAU,SAAS,IAAI,cAAc,MAAM,sBAAsB;AAAA,IACjE,QAAQ,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,IACnE,YAAY,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,IACvE,YAAY,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,EACzE;AACF;AAEO,IAAM,kBAAkB,CAAqB,eAAuB;AACzE,SAAO,IAAI,aAAgB,EAAE,GAAG,6BAA6B,GAAG,WAAW,CAAC;AAC9E;;;AEpBA,SAAS,cAAc;AAIhB,IAAM,mBAAmB,MAAe;AAC7C,QAAM,MAAM,iBAAiB;AAC7B,QAAM,iBAAiB,CAAC,IAAI,yBAAyB,IAAI,gBAAgB,IAAI,cAAc,IAAI,gBAAgB,IAAI,cAAc;AACjI,SAAO,eAAe,MAAM,MAAM;AACpC;;;ACRO,IAAM,YAAY,EAAE,WAAW,YAAqB;;;ACApD,IAAM,4BAA4B;AAClC,IAAM,eAAe;AACrB,IAAM,mBAAmB;AACzB,IAAM,eAAe;;;ACH5B,SAAS,YAAAA,iBAAgB;AACzB,SAAS,gBAAAC,qBAAwC;AACjD,SAAS,wBAAwB;AACjC;AAAA,EAC2D;AAAA,OACpD;;;ACJP,SAAS,SAAS,KAA0C;AAC1D,SAAO,QAAQ,QAAQ,OAAO,QAAQ;AACxC;AAEO,SAAS,MAAwB,WAAc,SAAmB;AACvE,MAAI,CAAC,SAAS,MAAM,GAAG;AACrB,UAAM,IAAI,UAAU,0BAA0B;AAAA,EAChD;AAEA,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,SAAS,MAAM,GAAG;AACrB;AAAA,IACF;AAEA,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,YAAM,YAAY,OAAO,GAAG;AAC5B,YAAM,YAAa,OAAe,GAAG;AAErC,UAAI,MAAM,QAAQ,SAAS,GAAG;AAE5B,QAAC,OAAe,GAAG,IAAI,CAAC,GAAG,SAAS;AAAA,MACtC,WAAW,SAAS,SAAS,GAAG;AAC9B,YAAI,CAAC,SAAS,SAAS,GAAG;AACxB,UAAC,OAAe,GAAG,IAAI,CAAC;AAAA,QAC1B;AACA,cAAO,OAAe,GAAG,GAAG,SAAS;AAAA,MACvC,OAAO;AACL,QAAC,OAAe,GAAG,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ADnBA,IAAM,kBAAsC;AAAA,EAC1C;AAAA,IACE,MAAM;AAAA,IAAY,KAAK,EAAE,OAAO,EAAE;AAAA,IAAG,QAAQ;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AACF;AAEO,IAAM,qBAAqB,CAIhC,eACG;AAEH,MAAe,kBAAf,cAAuC,WAAoC;AAAA,IAEzE;AAAA,IACA;AAAA,IAEA,IAAI,wBAA4C;AAC9C,YAAM,SAAS,EAAE,YAAY,YAAY,gBAAgB,GAAG,6BAA6B,EAAE;AAC3F,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,uBAAuB,cAAc,KAAK,OAAO,uBAAuB,cAAc,YAAY,eAAe;AAAA,MAC7I;AAAA,IACF;AAAA,IAEA,IAAI,iBAAiB;AACnB,WAAK,mBAAmB,KAAK,oBAAoB,IAAIC,cAAwC,KAAK,qBAAqB;AACvH,aAAOC,UAAS,KAAK,gBAAgB;AAAA,IACvC;AAAA,IAEA,IAAI,WAAW;AACb,aAAOA,UAAS,KAAK,OAAO,UAAU,MAAM,2EAA2E;AAAA,IACzH;AAAA,IAEA,IAAI,mBAAuC;AACzC,YAAM,SAAS,EAAE,YAAY,YAAY,UAAU,GAAG,6BAA6B,EAAE;AACrF,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,kBAAkB,cAAc,KAAK,OAAO,kBAAkB,cAAc,YAAY,SAAS;AAAA,MAC7H;AAAA,IACF;AAAA,IAEA,IAAI,WAAW;AACb,WAAK,cAAc,KAAK,eAAe,IAAID,cAAmC,KAAK,gBAAgB;AACnG,aAAOC,UAAS,KAAK,WAAW;AAAA,IAClC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,gBAA+B;AACnC,YAAM,gBAAiB,KAAK,QAA2D,SAAS,WAAW,CAAC;AAC5G,YAAM,+BAA+B,KAAK,sBAAsB;AAChE,YAAM,wBAAwB,KAAK,iBAAiB;AAEpD,YAAM,oBAAoB,gBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,4BAA4B,IAAI,GAAG,IAAI,GAAG,EAAE;AACnH,YAAM,+BAA+B,KAAK,gBAAgB,CAAC,GAAG,mBAAmB,GAAG,aAAa,CAAC;AAClG,YAAM,yBAAyB,gBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,qBAAqB,IAAI,GAAG,IAAI,GAAG,EAAE;AACjH,YAAM,+BAA+B,KAAK,UAAU,CAAC,GAAG,wBAAwB,GAAG,aAAa,CAAC;AAAA,IACnG;AAAA,EACF;AApDE,gBADa,iBACG,UAAS;AADZ,oBAAf;AAAA,IADC,iBAAsC;AAAA,KACxB;AAsDf,SAAO;AACT;AAOA,IAAM,iCAAiC,OACrC,KACA,kBACG;AACH,QAAM,IAAI,cAAc,OAAO,eAAe;AAC5C,UAAM,iBAAiB,WAAW,eAAe,YAAY;AAC7D,UAAM,UAAU,cAAc,OAAO,QAAM,IAAI,MAAM,YAAY,EAAE,WAAW,cAAc,CAAC;AAC7F,QAAI,QAAQ,WAAW,EAAG;AAC1B,eAAW,MAAM,SAAS;AACxB,UAAI;AACF,cAAM,WAAW,cAAc,CAAC,EAAE,CAAC;AAAA,MACrC,SAAS,OAAO;AACd,cAAM,mBAAmB;AACzB,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,aAAa,2BAA2B,aAAa,wBAAwB;AAK/E;AAAA,QACF;AACA,gBAAQ,MAAM,wBAAwB,GAAG,IAAI,mBAAmB,cAAc,KAAK,KAAK,EAAE;AAC1F,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AE1HA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,gBAAAC,qBAAwC;AACjD,SAAS,oBAAAC,yBAAwB;AACjC;AAAA,EAC+D,6BAAAC;AAAA,OACxD;AAUP,IAAMC,mBAAsC;AAAA,EAC1C;AAAA,IACE,MAAM;AAAA,IAAY,KAAK,EAAE,OAAO,EAAE;AAAA,IAAG,QAAQ;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AACF;AAEO,IAAM,uBAAuB,CAIlC,eACG;AAEH,MAAe,kBAAf,cAAuC,WAAsC;AAAA,IAE3E;AAAA,IAEA,IAAI,WAAW;AACb,aAAOC,UAAS,KAAK,OAAO,UAAU,MAAM,2EAA2E;AAAA,IACzH;AAAA,IAEA,IAAI,mBAAuC;AACzC,YAAM,SAAS,EAAE,YAAY,YAAY,UAAU,GAAG,6BAA6B,EAAE;AACrF,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,kBAAkB,cAAc,KAAK,OAAO,kBAAkB,cAAc,YAAY,SAAS;AAAA,MAC7H;AAAA,IACF;AAAA,IAEA,IAAI,WAAW;AACb,WAAK,cAAc,KAAK,eAAe,IAAIC,cAAmC,KAAK,gBAAgB;AACnG,aAAOD,UAAS,KAAK,WAAW;AAAA,IAClC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,gBAA+B;AACnC,YAAM,gBAAiB,KAAK,QAA2D,SAAS,WAAW,CAAC;AAC5G,YAAM,wBAAwB,KAAK,iBAAiB;AAEpD,YAAM,yBAAyBD,iBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,qBAAqB,IAAI,GAAG,IAAI,GAAG,EAAE;AACjH,YAAMG,gCAA+B,KAAK,UAAU,CAAC,GAAG,wBAAwB,GAAG,aAAa,CAAC;AAAA,IACnG;AAAA,EACF;AAjCE,gBADa,iBACG,UAASC;AADZ,oBAAf;AAAA,IADCC,kBAAsC;AAAA,KACxB;AAmCf,SAAO;AACT;AAOA,IAAMF,kCAAiC,OACrC,KACA,kBACG;AACH,QAAM,IAAI,cAAc,OAAO,eAAe;AAC5C,UAAM,iBAAiB,WAAW,eAAe,YAAY;AAC7D,UAAM,UAAU,cAAc,OAAO,QAAM,IAAI,MAAM,YAAY,EAAE,WAAW,cAAc,CAAC;AAC7F,QAAI,QAAQ,WAAW,EAAG;AAC1B,eAAW,MAAM,SAAS;AACxB,UAAI;AACF,cAAM,WAAW,cAAc,CAAC,EAAE,CAAC;AAAA,MACrC,SAAS,OAAO;AACd,cAAM,mBAAmB;AACzB,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,aAAa,2BAA2B,aAAa,wBAAwB;AAK/E;AAAA,QACF;AACA,gBAAQ,MAAM,wBAAwB,GAAG,IAAI,mBAAmB,cAAc,KAAK,KAAK,EAAE;AAC1F,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACvGO,IAAM,aAAa;AAEnB,IAAM,eAAe,CAAC,UAAkB,MAAM,WAAW,KAAK,UAAU;AAExE,IAAM,iBAAiB,CAAC,UAAkB,MAAM,WAAW,YAAY,GAAG;","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements"]}
1
+ {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { Db, MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureCollection(): Promise<void> {\n const { max } = this.config\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n\n if (max) {\n // Create capped collection if it doesn't exist or convert it if it does\n await ensureCappedCollection(this.payloads, max)\n // Recreate indexes after creating/converting a capped collection as\n // capped will remove all indexes on existing collections.\n // https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n } else {\n // Create indexes (creates collection without having to write data to it)\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n }\n }\n }\n return MongoModuleBase\n}\n\nconst collectionExists = async (db: Db, name: string): Promise<boolean> => {\n const collections = await db.listCollections({ name }).toArray()\n return collections.length > 0\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n\n/**\n * Ensures that a collection is capped with a max document count and a reasonable size.\n * If the collection exists and is not capped, it will be converted.\n * If it doesn't exist, it will be created.\n *\n * @param name The name of the collection.\n * @param count The maximum number of documents to retain.\n * @param documentSize Estimated average document size in bytes if collection is empty.\n */\nconst ensureCappedCollection = async (sdk: BaseMongoSdk<PayloadWithMongoMeta>, max: number, docSize = 1024) => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n const size = docSize * max\n return exists\n ? await ensureExistingCollectionIsCapped(sdk, max, size)\n // Create capped collection\n : await db.createCollection(name, {\n capped: true, size, max,\n })\n })\n })\n}\n\n/**\n * Converts an existing collection to a capped collection with a max document count.\n * Since MongoDB doesn't support `max` in `convertToCapped` or `cloneCollectionAsCapped`,\n * this function recreates the collection to work around Mongo's limitations.\n * https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n * https://www.mongodb.com/docs/manual/reference/command/clonecollectionascapped/\n * @param db - The MongoDB database instance\n * @param name - The name of the collection to convert\n * @param max - The maximum number of documents to retain\n * @param docSize - Fallback size (in bytes) to use if no documents exist (default 1KB)\n */\nconst ensureExistingCollectionIsCapped = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n max: number,\n docSize = 1024,\n): Promise<void> => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n if (!exists) throw new Error(`Collection '${name}' does not exist`)\n\n const size = docSize * max\n\n const stats = await db.command({ collStats: name })\n if (stats.capped && stats.max === max && stats.maxSize === size) {\n return\n }\n\n const tmpName = `${name}_tmp_capped`\n\n // Create new capped collection\n await db.createCollection(tmpName, {\n capped: true, size, max,\n })\n\n // Copy most recent documents\n const docs = await collection\n .find()\n .sort({ $natural: -1 })\n .limit(max)\n .toArray()\n\n if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed())\n\n // Replace old collection\n await collection.drop()\n await db.collection(tmpName).rename(name)\n })\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":"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","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements","exists"]}
@@ -249,11 +249,16 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
249
249
  * Ensures any indexes specified within the config are created. This method should be idempotent
250
250
  * allowing for multiple calls without causing errors while ensuring the desired state.
251
251
  */
252
- async ensureIndexes() {
253
- const configIndexes = this.config?.storage?.indexes ?? [];
252
+ async ensureCollection() {
253
+ const { max } = this.config;
254
254
  const payloadCollectionName = this.payloadSdkConfig.collection;
255
255
  const payloadStandardIndexes = standardIndexes2.map((ix) => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }));
256
- await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes, ...configIndexes]);
256
+ if (max) {
257
+ await ensureCappedCollection(this.payloads, max);
258
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
259
+ } else {
260
+ await ensureIndexesExistOnCollection2(this.payloads, [...payloadStandardIndexes]);
261
+ }
257
262
  }
258
263
  };
259
264
  __publicField(MongoModuleBase, "labels", MongoDBStorageClassLabels2);
@@ -262,6 +267,10 @@ var MongoDBModuleMixinV2 = (ModuleBase) => {
262
267
  ], MongoModuleBase);
263
268
  return MongoModuleBase;
264
269
  };
270
+ var collectionExists = async (db, name) => {
271
+ const collections = await db.listCollections({ name }).toArray();
272
+ return collections.length > 0;
273
+ };
265
274
  var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
266
275
  await sdk.useCollection(async (collection) => {
267
276
  const collectionName = collection.collectionName.toLowerCase();
@@ -282,6 +291,46 @@ var ensureIndexesExistOnCollection2 = async (sdk, configIndexes) => {
282
291
  }
283
292
  });
284
293
  };
294
+ var ensureCappedCollection = async (sdk, max, docSize = 1024) => {
295
+ await sdk.useCollection(async (collection) => {
296
+ const name = collection.collectionName.toLowerCase();
297
+ await sdk.useMongo(async (client) => {
298
+ const db = client.db(collection.dbName);
299
+ const exists2 = await collectionExists(db, name);
300
+ const size = docSize * max;
301
+ return exists2 ? await ensureExistingCollectionIsCapped(sdk, max, size) : await db.createCollection(name, {
302
+ capped: true,
303
+ size,
304
+ max
305
+ });
306
+ });
307
+ });
308
+ };
309
+ var ensureExistingCollectionIsCapped = async (sdk, max, docSize = 1024) => {
310
+ await sdk.useCollection(async (collection) => {
311
+ const name = collection.collectionName.toLowerCase();
312
+ await sdk.useMongo(async (client) => {
313
+ const db = client.db(collection.dbName);
314
+ const exists2 = await collectionExists(db, name);
315
+ if (!exists2) throw new Error(`Collection '${name}' does not exist`);
316
+ const size = docSize * max;
317
+ const stats = await db.command({ collStats: name });
318
+ if (stats.capped && stats.max === max && stats.maxSize === size) {
319
+ return;
320
+ }
321
+ const tmpName = `${name}_tmp_capped`;
322
+ await db.createCollection(tmpName, {
323
+ capped: true,
324
+ size,
325
+ max
326
+ });
327
+ const docs = await collection.find().sort({ $natural: -1 }).limit(max).toArray();
328
+ if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed());
329
+ await collection.drop();
330
+ await db.collection(tmpName).rename(name);
331
+ });
332
+ });
333
+ };
285
334
 
286
335
  // src/util/dbProperty.ts
287
336
  var escapeChar = "#";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":";;;;;;;;;;;;;;AACO,IAAM,cAAc;AAAA,EACzB,aAAa;AAAA,EACb,gBAAgB;AAAA,EAChB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,YAAY;AAAA,EACZ,OAAO;AACT;;;ACRA,SAAS,gBAAgB;AAEzB,SAAS,oBAAoB;;;ACGtB,IAAM,mBAAmB,MAAgB;AAC9C,QAAM,MAAgB,CAAC;AACvB,MAAI,QAAQ,IAAI,yBAAyB;AACvC,QAAI,0BAA0B,QAAQ,IAAI;AAAA,EAC5C;AACA,MAAI,QAAQ,IAAI,cAAc;AAC5B,QAAI,iBAAiB,QAAQ,IAAI;AACjC,QAAI,eAAe,QAAQ,IAAI;AAC/B,QAAI,iBAAiB,QAAQ,IAAI;AACjC,QAAI,iBAAiB,QAAQ,IAAI;AAAA,EACnC;AACA,SAAO;AACT;;;ADVO,IAAM,+BAA+B,MAAiC;AAC3E,QAAM,MAAM,iBAAiB;AAC7B,SAAO;AAAA,IACL,oBAAoB,IAAI;AAAA,IACxB,UAAU,SAAS,IAAI,cAAc,MAAM,sBAAsB;AAAA,IACjE,QAAQ,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,IACnE,YAAY,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,IACvE,YAAY,SAAS,IAAI,gBAAgB,MAAM,wBAAwB;AAAA,EACzE;AACF;AAEO,IAAM,kBAAkB,CAAqB,eAAuB;AACzE,SAAO,IAAI,aAAgB,EAAE,GAAG,6BAA6B,GAAG,WAAW,CAAC;AAC9E;;;AEpBA,SAAS,cAAc;AAIhB,IAAM,mBAAmB,MAAe;AAC7C,QAAM,MAAM,iBAAiB;AAC7B,QAAM,iBAAiB,CAAC,IAAI,yBAAyB,IAAI,gBAAgB,IAAI,cAAc,IAAI,gBAAgB,IAAI,cAAc;AACjI,SAAO,eAAe,MAAM,MAAM;AACpC;;;ACRO,IAAM,YAAY,EAAE,WAAW,YAAqB;;;ACApD,IAAM,4BAA4B;AAClC,IAAM,eAAe;AACrB,IAAM,mBAAmB;AACzB,IAAM,eAAe;;;ACH5B,SAAS,YAAAA,iBAAgB;AACzB,SAAS,gBAAAC,qBAAwC;AACjD,SAAS,wBAAwB;AACjC;AAAA,EAC2D;AAAA,OACpD;;;ACJP,SAAS,SAAS,KAA0C;AAC1D,SAAO,QAAQ,QAAQ,OAAO,QAAQ;AACxC;AAEO,SAAS,MAAwB,WAAc,SAAmB;AACvE,MAAI,CAAC,SAAS,MAAM,GAAG;AACrB,UAAM,IAAI,UAAU,0BAA0B;AAAA,EAChD;AAEA,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,SAAS,MAAM,GAAG;AACrB;AAAA,IACF;AAEA,eAAW,OAAO,OAAO,KAAK,MAAM,GAAG;AACrC,YAAM,YAAY,OAAO,GAAG;AAC5B,YAAM,YAAa,OAAe,GAAG;AAErC,UAAI,MAAM,QAAQ,SAAS,GAAG;AAE5B,QAAC,OAAe,GAAG,IAAI,CAAC,GAAG,SAAS;AAAA,MACtC,WAAW,SAAS,SAAS,GAAG;AAC9B,YAAI,CAAC,SAAS,SAAS,GAAG;AACxB,UAAC,OAAe,GAAG,IAAI,CAAC;AAAA,QAC1B;AACA,cAAO,OAAe,GAAG,GAAG,SAAS;AAAA,MACvC,OAAO;AACL,QAAC,OAAe,GAAG,IAAI;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;;;ADnBA,IAAM,kBAAsC;AAAA,EAC1C;AAAA,IACE,MAAM;AAAA,IAAY,KAAK,EAAE,OAAO,EAAE;AAAA,IAAG,QAAQ;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AACF;AAEO,IAAM,qBAAqB,CAIhC,eACG;AAEH,MAAe,kBAAf,cAAuC,WAAoC;AAAA,IAEzE;AAAA,IACA;AAAA,IAEA,IAAI,wBAA4C;AAC9C,YAAM,SAAS,EAAE,YAAY,YAAY,gBAAgB,GAAG,6BAA6B,EAAE;AAC3F,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,uBAAuB,cAAc,KAAK,OAAO,uBAAuB,cAAc,YAAY,eAAe;AAAA,MAC7I;AAAA,IACF;AAAA,IAEA,IAAI,iBAAiB;AACnB,WAAK,mBAAmB,KAAK,oBAAoB,IAAIC,cAAwC,KAAK,qBAAqB;AACvH,aAAOC,UAAS,KAAK,gBAAgB;AAAA,IACvC;AAAA,IAEA,IAAI,WAAW;AACb,aAAOA,UAAS,KAAK,OAAO,UAAU,MAAM,2EAA2E;AAAA,IACzH;AAAA,IAEA,IAAI,mBAAuC;AACzC,YAAM,SAAS,EAAE,YAAY,YAAY,UAAU,GAAG,6BAA6B,EAAE;AACrF,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,kBAAkB,cAAc,KAAK,OAAO,kBAAkB,cAAc,YAAY,SAAS;AAAA,MAC7H;AAAA,IACF;AAAA,IAEA,IAAI,WAAW;AACb,WAAK,cAAc,KAAK,eAAe,IAAID,cAAmC,KAAK,gBAAgB;AACnG,aAAOC,UAAS,KAAK,WAAW;AAAA,IAClC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,gBAA+B;AACnC,YAAM,gBAAiB,KAAK,QAA2D,SAAS,WAAW,CAAC;AAC5G,YAAM,+BAA+B,KAAK,sBAAsB;AAChE,YAAM,wBAAwB,KAAK,iBAAiB;AAEpD,YAAM,oBAAoB,gBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,4BAA4B,IAAI,GAAG,IAAI,GAAG,EAAE;AACnH,YAAM,+BAA+B,KAAK,gBAAgB,CAAC,GAAG,mBAAmB,GAAG,aAAa,CAAC;AAClG,YAAM,yBAAyB,gBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,qBAAqB,IAAI,GAAG,IAAI,GAAG,EAAE;AACjH,YAAM,+BAA+B,KAAK,UAAU,CAAC,GAAG,wBAAwB,GAAG,aAAa,CAAC;AAAA,IACnG;AAAA,EACF;AApDE,gBADa,iBACG,UAAS;AADZ,oBAAf;AAAA,IADC,iBAAsC;AAAA,KACxB;AAsDf,SAAO;AACT;AAOA,IAAM,iCAAiC,OACrC,KACA,kBACG;AACH,QAAM,IAAI,cAAc,OAAO,eAAe;AAC5C,UAAM,iBAAiB,WAAW,eAAe,YAAY;AAC7D,UAAM,UAAU,cAAc,OAAO,QAAM,IAAI,MAAM,YAAY,EAAE,WAAW,cAAc,CAAC;AAC7F,QAAI,QAAQ,WAAW,EAAG;AAC1B,eAAW,MAAM,SAAS;AACxB,UAAI;AACF,cAAM,WAAW,cAAc,CAAC,EAAE,CAAC;AAAA,MACrC,SAAS,OAAO;AACd,cAAM,mBAAmB;AACzB,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,aAAa,2BAA2B,aAAa,wBAAwB;AAK/E;AAAA,QACF;AACA,gBAAQ,MAAM,wBAAwB,GAAG,IAAI,mBAAmB,cAAc,KAAK,KAAK,EAAE;AAC1F,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AE1HA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,gBAAAC,qBAAwC;AACjD,SAAS,oBAAAC,yBAAwB;AACjC;AAAA,EAC+D,6BAAAC;AAAA,OACxD;AAUP,IAAMC,mBAAsC;AAAA,EAC1C;AAAA,IACE,MAAM;AAAA,IAAY,KAAK,EAAE,OAAO,EAAE;AAAA,IAAG,QAAQ;AAAA,EAC/C;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IAAgB,KAAK,EAAE,WAAW,EAAE;AAAA,IAAG,QAAQ;AAAA,EACvD;AACF;AAEO,IAAM,uBAAuB,CAIlC,eACG;AAEH,MAAe,kBAAf,cAAuC,WAAsC;AAAA,IAE3E;AAAA,IAEA,IAAI,WAAW;AACb,aAAOC,UAAS,KAAK,OAAO,UAAU,MAAM,2EAA2E;AAAA,IACzH;AAAA,IAEA,IAAI,mBAAuC;AACzC,YAAM,SAAS,EAAE,YAAY,YAAY,UAAU,GAAG,6BAA6B,EAAE;AACrF,aAAO;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,KAAK,OAAO;AAAA,QACZ,EAAE,YAAY,KAAK,OAAO,kBAAkB,cAAc,KAAK,OAAO,kBAAkB,cAAc,YAAY,SAAS;AAAA,MAC7H;AAAA,IACF;AAAA,IAEA,IAAI,WAAW;AACb,WAAK,cAAc,KAAK,eAAe,IAAIC,cAAmC,KAAK,gBAAgB;AACnG,aAAOD,UAAS,KAAK,WAAW;AAAA,IAClC;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,MAAM,gBAA+B;AACnC,YAAM,gBAAiB,KAAK,QAA2D,SAAS,WAAW,CAAC;AAC5G,YAAM,wBAAwB,KAAK,iBAAiB;AAEpD,YAAM,yBAAyBD,iBAAgB,IAAI,SAAO,EAAE,GAAG,IAAI,MAAM,GAAG,qBAAqB,IAAI,GAAG,IAAI,GAAG,EAAE;AACjH,YAAMG,gCAA+B,KAAK,UAAU,CAAC,GAAG,wBAAwB,GAAG,aAAa,CAAC;AAAA,IACnG;AAAA,EACF;AAjCE,gBADa,iBACG,UAASC;AADZ,oBAAf;AAAA,IADCC,kBAAsC;AAAA,KACxB;AAmCf,SAAO;AACT;AAOA,IAAMF,kCAAiC,OACrC,KACA,kBACG;AACH,QAAM,IAAI,cAAc,OAAO,eAAe;AAC5C,UAAM,iBAAiB,WAAW,eAAe,YAAY;AAC7D,UAAM,UAAU,cAAc,OAAO,QAAM,IAAI,MAAM,YAAY,EAAE,WAAW,cAAc,CAAC;AAC7F,QAAI,QAAQ,WAAW,EAAG;AAC1B,eAAW,MAAM,SAAS;AACxB,UAAI;AACF,cAAM,WAAW,cAAc,CAAC,EAAE,CAAC;AAAA,MACrC,SAAS,OAAO;AACd,cAAM,mBAAmB;AACzB,cAAM,EAAE,SAAS,IAAI;AACrB,YAAI,aAAa,2BAA2B,aAAa,wBAAwB;AAK/E;AAAA,QACF;AACA,gBAAQ,MAAM,wBAAwB,GAAG,IAAI,mBAAmB,cAAc,KAAK,KAAK,EAAE;AAC1F,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;ACvGO,IAAM,aAAa;AAEnB,IAAM,eAAe,CAAC,UAAkB,MAAM,WAAW,KAAK,UAAU;AAExE,IAAM,iBAAiB,CAAC,UAAkB,MAAM,WAAW,YAAY,GAAG;","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements"]}
1
+ {"version":3,"sources":["../../src/Collections.ts","../../src/config/getBaseMongoSdk.ts","../../src/config/getMongoDBConfig.ts","../../src/config/hasMongoDBConfig.ts","../../src/Databases.ts","../../src/Defaults.ts","../../src/Module.ts","../../src/merge.ts","../../src/ModuleV2.ts","../../src/util/dbProperty.ts"],"sourcesContent":["// TODO: By DB\nexport const COLLECTIONS = {\n AddressInfo: 'address_info' as const,\n ArchivistStats: 'archivist_stats' as const,\n BoundWitnesses: 'bound_witnesses' as const,\n Payloads: 'payloads' as const,\n Thumbnails: 'thumbnails' as const,\n Users: 'users' as const,\n}\n","import { assertEx } from '@xylabs/assert'\nimport type { BaseMongoSdkPrivateConfig } from '@xylabs/mongo'\nimport { BaseMongoSdk } from '@xylabs/mongo'\nimport type { Document } from 'mongodb'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const getBaseMongoSdkPrivateConfig = (): BaseMongoSdkPrivateConfig => {\n const env = getMongoDBConfig()\n return {\n dbConnectionString: env.MONGO_CONNECTION_STRING,\n dbDomain: assertEx(env.MONGO_DOMAIN, () => 'Missing Mongo Domain'),\n dbName: assertEx(env.MONGO_DATABASE, () => 'Missing Mongo Database'),\n dbPassword: assertEx(env.MONGO_PASSWORD, () => 'Missing Mongo Password'),\n dbUserName: assertEx(env.MONGO_USERNAME, () => 'Missing Mongo Username'),\n }\n}\n\nexport const getBaseMongoSdk = <T extends Document>(collection: string) => {\n return new BaseMongoSdk<T>({ ...getBaseMongoSdkPrivateConfig(), collection })\n}\n","export type MongoDbConnectionStringEnvVar = 'MONGO_CONNECTION_STRING'\nexport type MongoDbEnvVars = 'MONGO_DATABASE' | 'MONGO_DOMAIN' | 'MONGO_PASSWORD' | 'MONGO_USERNAME'\n\nexport type MongoEnv = Record<MongoDbEnvVars | MongoDbConnectionStringEnvVar, string | undefined>\n\nexport const getMongoDBConfig = (): MongoEnv => {\n const env: MongoEnv = {} as MongoEnv\n if (process.env.MONGO_CONNECTION_STRING) {\n env.MONGO_CONNECTION_STRING = process.env.MONGO_CONNECTION_STRING\n }\n if (process.env.MONGO_DOMAIN) {\n env.MONGO_DATABASE = process.env.MONGO_DATABASE\n env.MONGO_DOMAIN = process.env.MONGO_DOMAIN\n env.MONGO_PASSWORD = process.env.MONGO_PASSWORD\n env.MONGO_USERNAME = process.env.MONGO_USERNAME\n }\n return env\n}\n","import { exists } from '@xylabs/exists'\n\nimport { getMongoDBConfig } from './getMongoDBConfig.ts'\n\nexport const hasMongoDBConfig = (): boolean => {\n const env = getMongoDBConfig()\n const requiredValues = [env.MONGO_CONNECTION_STRING, env.MONGO_DATABASE, env.MONGO_DOMAIN, env.MONGO_PASSWORD, env.MONGO_USERNAME]\n return requiredValues.every(exists)\n}\n","export const DATABASES = { Archivist: 'archivist' as const }\n","export const DefaultAggregateTimeoutMs = 10_000\nexport const DefaultLimit = 20\nexport const DefaultMaxTimeMS = 2000\nexport const DefaultOrder = 'desc'\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModule, MongoDBModuleParams, MongoDBModuleStatic, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { BoundWitnessWithMongoMeta, PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'IX__hash', key: { _hash: 1 }, unique: false,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'IX__sequence', key: { _sequence: 1 }, unique: false,\n },\n]\n\nexport const MongoDBModuleMixin = <\n TParams extends MongoDBModuleParams = MongoDBModuleParams,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModule {\n static readonly labels = MongoDBStorageClassLabels\n _boundWitnessSdk: BaseMongoSdk<BoundWitnessWithMongoMeta> | undefined\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get boundWitnessSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.BoundWitnesses, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.boundWitnessSdkConfig,\n this.config.boundWitnessSdkConfig,\n { collection: this.config.boundWitnessSdkConfig?.collection ?? this.params.boundWitnessSdkConfig?.collection ?? COLLECTIONS.BoundWitnesses },\n )\n }\n\n get boundWitnesses() {\n this._boundWitnessSdk = this._boundWitnessSdk ?? new BaseMongoSdk<BoundWitnessWithMongoMeta>(this.boundWitnessSdkConfig)\n return assertEx(this._boundWitnessSdk)\n }\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureIndexes(): Promise<void> {\n const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []\n const boundWitnessesCollectionName = this.boundWitnessSdkConfig.collection\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const bwStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${boundWitnessesCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.boundWitnesses, [...bwStandardIndexes, ...configIndexes])\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])\n }\n }\n return MongoModuleBase\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta> | BaseMongoSdk<BoundWitnessWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nfunction isObject(val: unknown): val is Record<string, any> {\n return val !== null && typeof val === 'object'\n}\n\nexport function merge<T extends object>(target: T, ...sources: any[]): T {\n if (!isObject(target)) {\n throw new TypeError('Target must be an object')\n }\n\n for (const source of sources) {\n if (!isObject(source)) {\n continue\n }\n\n for (const key of Object.keys(source)) {\n const sourceVal = source[key]\n const targetVal = (target as any)[key]\n\n if (Array.isArray(sourceVal)) {\n // Arrays are replaced, not deeply merged (lodash behavior)\n (target as any)[key] = [...sourceVal]\n } else if (isObject(sourceVal)) {\n if (!isObject(targetVal)) {\n (target as any)[key] = {}\n }\n merge((target as any)[key], sourceVal)\n } else {\n (target as any)[key] = sourceVal\n }\n }\n }\n\n return target\n}\n","import { assertEx } from '@xylabs/assert'\nimport { BaseMongoSdk, BaseMongoSdkConfig } from '@xylabs/mongo'\nimport { staticImplements } from '@xylabs/static-implements'\nimport {\n MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,\n} from '@xyo-network/module-model-mongodb'\nimport { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'\nimport { Db, MongoServerError } from 'mongodb'\n\nimport { AnyAbstractModule } from './AnyAbstractModule.ts'\nimport { COLLECTIONS } from './Collections.ts'\nimport { getBaseMongoSdkPrivateConfig } from './config/index.ts'\nimport { IndexDescription } from './IndexDescription.ts'\nimport { merge } from './merge.ts'\n\nconst standardIndexes: IndexDescription[] = [\n {\n name: 'UX__hash', key: { _hash: 1 }, unique: true,\n },\n {\n name: 'IX__dataHash', key: { _dataHash: 1 }, unique: false,\n },\n {\n name: 'UX__sequence', key: { _sequence: 1 }, unique: true,\n },\n]\n\nexport const MongoDBModuleMixinV2 = <\n TParams extends MongoDBModuleParamsV2 = MongoDBModuleParamsV2,\n TModule extends AnyAbstractModule<TParams> = AnyAbstractModule<TParams>,\n>(\n ModuleBase: TModule,\n) => {\n @staticImplements<MongoDBModuleStatic>()\n abstract class MongoModuleBase extends ModuleBase implements MongoDBModuleV2 {\n static readonly labels = MongoDBStorageClassLabels\n _payloadSdk: BaseMongoSdk<PayloadWithMongoMeta> | undefined\n\n get jobQueue() {\n return assertEx(this.params.jobQueue, () => 'MongoDBModule Error: jobQueue required for this module but is not defined')\n }\n\n get payloadSdkConfig(): BaseMongoSdkConfig {\n const config = { collection: COLLECTIONS.Payloads, ...getBaseMongoSdkPrivateConfig() }\n return merge(\n config,\n this.params.payloadSdkConfig,\n this.config.payloadSdkConfig,\n { collection: this.config.payloadSdkConfig?.collection ?? this.params.payloadSdkConfig?.collection ?? COLLECTIONS.Payloads },\n )\n }\n\n get payloads() {\n this._payloadSdk = this._payloadSdk ?? new BaseMongoSdk<PayloadWithMongoMeta>(this.payloadSdkConfig)\n return assertEx(this._payloadSdk)\n }\n\n /**\n * Ensures any indexes specified within the config are created. This method should be idempotent\n * allowing for multiple calls without causing errors while ensuring the desired state.\n */\n async ensureCollection(): Promise<void> {\n const { max } = this.config\n const payloadCollectionName = this.payloadSdkConfig.collection\n\n const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))\n\n if (max) {\n // Create capped collection if it doesn't exist or convert it if it does\n await ensureCappedCollection(this.payloads, max)\n // Recreate indexes after creating/converting a capped collection as\n // capped will remove all indexes on existing collections.\n // https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n } else {\n // Create indexes (creates collection without having to write data to it)\n await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])\n }\n }\n }\n return MongoModuleBase\n}\n\nconst collectionExists = async (db: Db, name: string): Promise<boolean> => {\n const collections = await db.listCollections({ name }).toArray()\n return collections.length > 0\n}\n\n/**\n * Ensures the specified indexes exist on the collection\n * @param sdk The sdk to use for the collection\n * @param configIndexes The indexes to ensure exist on the collection\n */\nconst ensureIndexesExistOnCollection = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n configIndexes: IndexDescription[],\n) => {\n await sdk.useCollection(async (collection) => {\n const collectionName = collection.collectionName.toLowerCase()\n const indexes = configIndexes.filter(ix => ix?.name?.toLowerCase().startsWith(collectionName))\n if (indexes.length === 0) return\n for (const ix of indexes) {\n try {\n await collection.createIndexes([ix])\n } catch (error) {\n const mongoServerError = error as MongoServerError\n const { codeName } = mongoServerError\n if (codeName === 'IndexKeySpecsConflict' || codeName === 'IndexOptionsConflict') {\n // Index already exists which is fine OR index exists with another name which is fine\n // TODO: For the latter case (IndexOptionsConflict) we could get into this case\n // if we change the TTL an existing index. We currently don't support TTLs so\n // we'll need to revisit this assumption if we do.\n continue\n }\n console.error(`Error creating index ${ix.name} for collection ${collectionName}: ${error}`)\n throw error\n }\n }\n })\n}\n\n/**\n * Ensures that a collection is capped with a max document count and a reasonable size.\n * If the collection exists and is not capped, it will be converted.\n * If it doesn't exist, it will be created.\n *\n * @param name The name of the collection.\n * @param count The maximum number of documents to retain.\n * @param documentSize Estimated average document size in bytes if collection is empty.\n */\nconst ensureCappedCollection = async (sdk: BaseMongoSdk<PayloadWithMongoMeta>, max: number, docSize = 1024) => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n const size = docSize * max\n return exists\n ? await ensureExistingCollectionIsCapped(sdk, max, size)\n // Create capped collection\n : await db.createCollection(name, {\n capped: true, size, max,\n })\n })\n })\n}\n\n/**\n * Converts an existing collection to a capped collection with a max document count.\n * Since MongoDB doesn't support `max` in `convertToCapped` or `cloneCollectionAsCapped`,\n * this function recreates the collection to work around Mongo's limitations.\n * https://www.mongodb.com/docs/manual/reference/command/convertToCapped/\n * https://www.mongodb.com/docs/manual/reference/command/clonecollectionascapped/\n * @param db - The MongoDB database instance\n * @param name - The name of the collection to convert\n * @param max - The maximum number of documents to retain\n * @param docSize - Fallback size (in bytes) to use if no documents exist (default 1KB)\n */\nconst ensureExistingCollectionIsCapped = async (\n sdk: BaseMongoSdk<PayloadWithMongoMeta>,\n max: number,\n docSize = 1024,\n): Promise<void> => {\n await sdk.useCollection(async (collection) => {\n const name = collection.collectionName.toLowerCase()\n await sdk.useMongo(async (client) => {\n const db = client.db(collection.dbName)\n const exists = await collectionExists(db, name)\n if (!exists) throw new Error(`Collection '${name}' does not exist`)\n\n const size = docSize * max\n\n const stats = await db.command({ collStats: name })\n if (stats.capped && stats.max === max && stats.maxSize === size) {\n return\n }\n\n const tmpName = `${name}_tmp_capped`\n\n // Create new capped collection\n await db.createCollection(tmpName, {\n capped: true, size, max,\n })\n\n // Copy most recent documents\n const docs = await collection\n .find()\n .sort({ $natural: -1 })\n .limit(max)\n .toArray()\n\n if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed())\n\n // Replace old collection\n await collection.drop()\n await db.collection(tmpName).rename(name)\n })\n })\n}\n","export const escapeChar = '#'\n\nexport const toDbProperty = (value: string) => value.replaceAll('.', escapeChar)\n\nexport const fromDbProperty = (value: string) => value.replaceAll(escapeChar, '.')\n"],"mappings":"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","names":["assertEx","BaseMongoSdk","BaseMongoSdk","assertEx","assertEx","BaseMongoSdk","staticImplements","MongoDBStorageClassLabels","standardIndexes","assertEx","BaseMongoSdk","ensureIndexesExistOnCollection","MongoDBStorageClassLabels","staticImplements","exists"]}
@@ -11,7 +11,7 @@ export declare const MongoDBModuleMixinV2: <TParams extends MongoDBModuleParamsV
11
11
  * Ensures any indexes specified within the config are created. This method should be idempotent
12
12
  * allowing for multiple calls without causing errors while ensuring the desired state.
13
13
  */
14
- ensureIndexes(): Promise<void>;
14
+ ensureCollection(): Promise<void>;
15
15
  address: import("@xylabs/hex").Address;
16
16
  config: TParams["config"];
17
17
  id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ModuleV2.d.ts","sourceRoot":"","sources":["../../src/ModuleV2.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEhE,OAAO,EACL,qBAAqB,EAAwC,yBAAyB,EACvF,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAkB1D,eAAO,MAAM,oBAAoB,GAC/B,OAAO,SAAS,qBAAqB,GAAG,qBAAqB,EAC7D,OAAO,SAAS,iBAAiB,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,EAEvE,YAAY,OAAO;iBAKJ,YAAY,CAAC,oBAAoB,CAAC,GAAG,SAAS;;+BAMnC,kBAAkB;;IAe1C;;;OAGG;qBACoB,OAAO,CAAC,IAAI,CAAC;;;;;;;;sRAtCM,CAAA,6DAI9C,CAAA;4RAEkE,CAAC,6DAErD,CAAA;;;;;;;;;;;;;;;YAuCb,CAAA"}
1
+ {"version":3,"file":"ModuleV2.d.ts","sourceRoot":"","sources":["../../src/ModuleV2.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAEhE,OAAO,EACL,qBAAqB,EAAwC,yBAAyB,EACvF,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAA;AAGnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAkB1D,eAAO,MAAM,oBAAoB,GAC/B,OAAO,SAAS,qBAAqB,GAAG,qBAAqB,EAC7D,OAAO,SAAS,iBAAiB,CAAC,OAAO,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,EAEvE,YAAY,OAAO;iBAKJ,YAAY,CAAC,oBAAoB,CAAC,GAAG,SAAS;;+BAMnC,kBAAkB;;IAe1C;;;OAGG;wBACuB,OAAO,CAAC,IAAI,CAAC;;;;;;;;sRAtCF,CAAC,6DACvC,CAAC;4RAK0D,CAAC,6DAEtD,CAAC;;;;;;;;;;;;;;;YAkDT,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/module-abstract-mongodb",
3
- "version": "3.18.2",
3
+ "version": "3.18.3",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -37,18 +37,18 @@
37
37
  "module": "dist/node/index.mjs",
38
38
  "types": "dist/types/index.d.ts",
39
39
  "dependencies": {
40
- "@xylabs/assert": "^4.11.10",
41
- "@xylabs/exists": "^4.11.10",
42
- "@xylabs/mongo": "^4.11.10",
43
- "@xylabs/static-implements": "^4.11.10",
44
- "@xyo-network/module-model": "^3.18.2",
45
- "@xyo-network/module-model-mongodb": "^3.18.2",
46
- "@xyo-network/payload-mongodb": "^3.18.2",
47
- "mongodb": "~6.16.0"
40
+ "@xylabs/assert": "^4.11.14",
41
+ "@xylabs/exists": "^4.11.14",
42
+ "@xylabs/mongo": "^4.11.14",
43
+ "@xylabs/static-implements": "^4.11.14",
44
+ "@xyo-network/module-model": "^3.18.3",
45
+ "@xyo-network/module-model-mongodb": "^3.18.3",
46
+ "@xyo-network/payload-mongodb": "^3.18.3",
47
+ "mongodb": "~6.17.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@xylabs/ts-scripts-yarn3": "^6.5.7",
51
- "@xylabs/tsconfig": "^6.5.7",
50
+ "@xylabs/ts-scripts-yarn3": "^6.5.8",
51
+ "@xylabs/tsconfig": "^6.5.8",
52
52
  "tslib": "^2.8.1",
53
53
  "typescript": "^5.8.3"
54
54
  },
package/src/ModuleV2.ts CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  MongoDBModuleParamsV2, MongoDBModuleStatic, MongoDBModuleV2, MongoDBStorageClassLabels,
6
6
  } from '@xyo-network/module-model-mongodb'
7
7
  import { PayloadWithMongoMeta } from '@xyo-network/payload-mongodb'
8
- import { MongoServerError } from 'mongodb'
8
+ import { Db, MongoServerError } from 'mongodb'
9
9
 
10
10
  import { AnyAbstractModule } from './AnyAbstractModule.ts'
11
11
  import { COLLECTIONS } from './Collections.ts'
@@ -59,17 +59,33 @@ export const MongoDBModuleMixinV2 = <
59
59
  * Ensures any indexes specified within the config are created. This method should be idempotent
60
60
  * allowing for multiple calls without causing errors while ensuring the desired state.
61
61
  */
62
- async ensureIndexes(): Promise<void> {
63
- const configIndexes = (this.config as { storage?: { indexes?: IndexDescription[] } })?.storage?.indexes ?? []
62
+ async ensureCollection(): Promise<void> {
63
+ const { max } = this.config
64
64
  const payloadCollectionName = this.payloadSdkConfig.collection
65
65
 
66
66
  const payloadStandardIndexes = standardIndexes.map(ix => ({ ...ix, name: `${payloadCollectionName}.${ix.name}` }))
67
- await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes, ...configIndexes])
67
+
68
+ if (max) {
69
+ // Create capped collection if it doesn't exist or convert it if it does
70
+ await ensureCappedCollection(this.payloads, max)
71
+ // Recreate indexes after creating/converting a capped collection as
72
+ // capped will remove all indexes on existing collections.
73
+ // https://www.mongodb.com/docs/manual/reference/command/convertToCapped/
74
+ await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])
75
+ } else {
76
+ // Create indexes (creates collection without having to write data to it)
77
+ await ensureIndexesExistOnCollection(this.payloads, [...payloadStandardIndexes])
78
+ }
68
79
  }
69
80
  }
70
81
  return MongoModuleBase
71
82
  }
72
83
 
84
+ const collectionExists = async (db: Db, name: string): Promise<boolean> => {
85
+ const collections = await db.listCollections({ name }).toArray()
86
+ return collections.length > 0
87
+ }
88
+
73
89
  /**
74
90
  * Ensures the specified indexes exist on the collection
75
91
  * @param sdk The sdk to use for the collection
@@ -102,3 +118,82 @@ const ensureIndexesExistOnCollection = async (
102
118
  }
103
119
  })
104
120
  }
121
+
122
+ /**
123
+ * Ensures that a collection is capped with a max document count and a reasonable size.
124
+ * If the collection exists and is not capped, it will be converted.
125
+ * If it doesn't exist, it will be created.
126
+ *
127
+ * @param name The name of the collection.
128
+ * @param count The maximum number of documents to retain.
129
+ * @param documentSize Estimated average document size in bytes if collection is empty.
130
+ */
131
+ const ensureCappedCollection = async (sdk: BaseMongoSdk<PayloadWithMongoMeta>, max: number, docSize = 1024) => {
132
+ await sdk.useCollection(async (collection) => {
133
+ const name = collection.collectionName.toLowerCase()
134
+ await sdk.useMongo(async (client) => {
135
+ const db = client.db(collection.dbName)
136
+ const exists = await collectionExists(db, name)
137
+ const size = docSize * max
138
+ return exists
139
+ ? await ensureExistingCollectionIsCapped(sdk, max, size)
140
+ // Create capped collection
141
+ : await db.createCollection(name, {
142
+ capped: true, size, max,
143
+ })
144
+ })
145
+ })
146
+ }
147
+
148
+ /**
149
+ * Converts an existing collection to a capped collection with a max document count.
150
+ * Since MongoDB doesn't support `max` in `convertToCapped` or `cloneCollectionAsCapped`,
151
+ * this function recreates the collection to work around Mongo's limitations.
152
+ * https://www.mongodb.com/docs/manual/reference/command/convertToCapped/
153
+ * https://www.mongodb.com/docs/manual/reference/command/clonecollectionascapped/
154
+ * @param db - The MongoDB database instance
155
+ * @param name - The name of the collection to convert
156
+ * @param max - The maximum number of documents to retain
157
+ * @param docSize - Fallback size (in bytes) to use if no documents exist (default 1KB)
158
+ */
159
+ const ensureExistingCollectionIsCapped = async (
160
+ sdk: BaseMongoSdk<PayloadWithMongoMeta>,
161
+ max: number,
162
+ docSize = 1024,
163
+ ): Promise<void> => {
164
+ await sdk.useCollection(async (collection) => {
165
+ const name = collection.collectionName.toLowerCase()
166
+ await sdk.useMongo(async (client) => {
167
+ const db = client.db(collection.dbName)
168
+ const exists = await collectionExists(db, name)
169
+ if (!exists) throw new Error(`Collection '${name}' does not exist`)
170
+
171
+ const size = docSize * max
172
+
173
+ const stats = await db.command({ collStats: name })
174
+ if (stats.capped && stats.max === max && stats.maxSize === size) {
175
+ return
176
+ }
177
+
178
+ const tmpName = `${name}_tmp_capped`
179
+
180
+ // Create new capped collection
181
+ await db.createCollection(tmpName, {
182
+ capped: true, size, max,
183
+ })
184
+
185
+ // Copy most recent documents
186
+ const docs = await collection
187
+ .find()
188
+ .sort({ $natural: -1 })
189
+ .limit(max)
190
+ .toArray()
191
+
192
+ if (docs.length > 0) await db.collection(tmpName).insertMany(docs.toReversed())
193
+
194
+ // Replace old collection
195
+ await collection.drop()
196
+ await db.collection(tmpName).rename(name)
197
+ })
198
+ })
199
+ }