From ce0aa4f789475ec5a5bf8224e917e162ea9afa04 Mon Sep 17 00:00:00 2001 From: Tim Basten Date: Thu, 2 Apr 2026 17:50:10 +0800 Subject: [PATCH] a --- .gitignore | 1 + backend/index.js | 10 +- backend/node_modules/.package-lock.json | 54 + .../fa57d6c4-9e78-4624-9229-f77b87a07481.json | 1 + .../fa57d6c4-9e78-4624-9229-f77b87a07481.json | 1 + .../kareem/.nyc_output/processinfo/index.json | 1 + backend/node_modules/kareem/CHANGELOG.md | 840 +++ backend/node_modules/kareem/LICENSE | 202 + backend/node_modules/kareem/README.md | 385 ++ backend/node_modules/kareem/SECURITY.md | 5 + .../kareem/coverage/lcov-report/base.css | 224 + .../coverage/lcov-report/block-navigation.js | 87 + .../kareem/coverage/lcov-report/favicon.png | Bin 0 -> 445 bytes .../kareem/coverage/lcov-report/index.html | 116 + .../kareem/coverage/lcov-report/index.js.html | 1603 +++++ .../kareem/coverage/lcov-report/prettify.css | 1 + .../kareem/coverage/lcov-report/prettify.js | 2 + .../lcov-report/sort-arrow-sprite.png | Bin 0 -> 138 bytes .../kareem/coverage/lcov-report/sorter.js | 196 + .../node_modules/kareem/coverage/lcov.info | 423 ++ backend/node_modules/kareem/index.d.ts | 31 + backend/node_modules/kareem/index.js | 539 ++ backend/node_modules/kareem/package.json | 28 + backend/node_modules/mongoose/LICENSE.md | 22 + backend/node_modules/mongoose/README.md | 395 ++ backend/node_modules/mongoose/SECURITY.md | 1 + .../node_modules/mongoose/eslint.config.mjs | 198 + backend/node_modules/mongoose/index.js | 64 + .../node_modules/mongoose/lib/aggregate.js | 1226 ++++ backend/node_modules/mongoose/lib/cast.js | 436 ++ .../node_modules/mongoose/lib/cast/bigint.js | 46 + .../node_modules/mongoose/lib/cast/boolean.js | 32 + .../node_modules/mongoose/lib/cast/date.js | 41 + .../mongoose/lib/cast/decimal128.js | 39 + .../node_modules/mongoose/lib/cast/double.js | 50 + .../node_modules/mongoose/lib/cast/int32.js | 36 + .../node_modules/mongoose/lib/cast/number.js | 42 + .../mongoose/lib/cast/objectid.js | 29 + .../node_modules/mongoose/lib/cast/string.js | 37 + .../node_modules/mongoose/lib/cast/uuid.js | 35 + .../node_modules/mongoose/lib/collection.js | 321 + .../node_modules/mongoose/lib/connection.js | 1841 ++++++ .../mongoose/lib/connectionState.js | 26 + .../node_modules/mongoose/lib/constants.js | 73 + .../mongoose/lib/cursor/aggregationCursor.js | 466 ++ .../mongoose/lib/cursor/changeStream.js | 218 + .../mongoose/lib/cursor/queryCursor.js | 625 ++ backend/node_modules/mongoose/lib/document.js | 5599 ++++++++++++++++ backend/node_modules/mongoose/lib/driver.js | 15 + .../node_modules/mongoose/lib/drivers/SPEC.md | 4 + .../node-mongodb-native/bulkWriteResult.js | 5 + .../drivers/node-mongodb-native/collection.js | 393 ++ .../drivers/node-mongodb-native/connection.js | 506 ++ .../lib/drivers/node-mongodb-native/index.js | 10 + .../lib/error/browserMissingSchema.js | 29 + .../lib/error/bulkSaveIncompleteError.js | 44 + .../mongoose/lib/error/bulkWriteError.js | 41 + .../node_modules/mongoose/lib/error/cast.js | 158 + .../lib/error/createCollectionsError.js | 26 + .../mongoose/lib/error/divergentArray.js | 40 + .../mongoose/lib/error/eachAsyncMultiError.js | 41 + .../node_modules/mongoose/lib/error/index.js | 237 + .../mongoose/lib/error/invalidSchemaOption.js | 32 + .../mongoose/lib/error/messages.js | 47 + .../mongoose/lib/error/missingSchema.js | 33 + .../mongoose/lib/error/mongooseError.js | 13 + .../mongoose/lib/error/notFound.js | 47 + .../mongoose/lib/error/objectExpected.js | 31 + .../mongoose/lib/error/objectParameter.js | 31 + .../mongoose/lib/error/overwriteModel.js | 31 + .../mongoose/lib/error/parallelSave.js | 33 + .../mongoose/lib/error/parallelValidate.js | 33 + .../mongoose/lib/error/serverSelection.js | 62 + .../mongoose/lib/error/setOptionError.js | 103 + .../node_modules/mongoose/lib/error/strict.js | 35 + .../mongoose/lib/error/strictPopulate.js | 31 + .../mongoose/lib/error/syncIndexes.js | 30 + .../mongoose/lib/error/validation.js | 97 + .../mongoose/lib/error/validator.js | 100 + .../mongoose/lib/error/version.js | 38 + .../aggregate/prepareDiscriminatorPipeline.js | 39 + .../aggregate/stringifyFunctionOperators.js | 50 + .../mongoose/lib/helpers/arrayDepth.js | 33 + .../lib/helpers/buildMiddlewareFilter.js | 24 + .../mongoose/lib/helpers/clone.js | 236 + .../mongoose/lib/helpers/common.js | 127 + .../helpers/createJSONSchemaTypeDefinition.js | 24 + .../mongoose/lib/helpers/cursor/eachAsync.js | 225 + .../applyEmbeddedDiscriminators.js | 36 + .../areDiscriminatorValuesEqual.js | 16 + ...checkEmbeddedDiscriminatorKeyProjection.js | 12 + .../helpers/discriminator/getConstructor.js | 29 + .../discriminator/getDiscriminatorByValue.js | 28 + .../getSchemaDiscriminatorByValue.js | 27 + .../discriminator/mergeDiscriminatorSchema.js | 96 + .../lib/helpers/document/applyDefaults.js | 132 + .../lib/helpers/document/applyTimestamps.js | 106 + .../lib/helpers/document/applyVirtuals.js | 147 + .../helpers/document/cleanModifiedSubpaths.js | 45 + .../mongoose/lib/helpers/document/compile.js | 241 + .../document/getDeepestSubdocumentForPath.js | 38 + .../document/getEmbeddedDiscriminatorPath.js | 53 + .../lib/helpers/document/handleSpreadDoc.js | 35 + .../lib/helpers/document/isInPathsToSave.js | 24 + .../node_modules/mongoose/lib/helpers/each.js | 25 + .../lib/helpers/error/combinePathErrors.js | 22 + .../mongoose/lib/helpers/firstKey.js | 8 + .../node_modules/mongoose/lib/helpers/get.js | 65 + .../lib/helpers/getConstructorName.js | 16 + .../lib/helpers/getDefaultBulkwriteResult.js | 18 + .../mongoose/lib/helpers/getFunctionName.js | 10 + .../mongoose/lib/helpers/immediate.js | 16 + .../helpers/indexes/applySchemaCollation.js | 13 + .../decorateDiscriminatorIndexOptions.js | 14 + .../lib/helpers/indexes/getRelatedIndexes.js | 63 + .../lib/helpers/indexes/isDefaultIdIndex.js | 18 + .../lib/helpers/indexes/isIndexEqual.js | 95 + .../lib/helpers/indexes/isIndexSpecEqual.js | 32 + .../lib/helpers/indexes/isTextIndex.js | 16 + .../lib/helpers/indexes/isTimeseriesIndex.js | 16 + .../mongoose/lib/helpers/isAsyncFunction.js | 9 + .../mongoose/lib/helpers/isBsonType.js | 15 + .../mongoose/lib/helpers/isMongooseObject.js | 22 + .../mongoose/lib/helpers/isObject.js | 16 + .../mongoose/lib/helpers/isPOJO.js | 12 + .../mongoose/lib/helpers/isPromise.js | 6 + .../mongoose/lib/helpers/isSimpleValidator.js | 22 + .../mongoose/lib/helpers/minimize.js | 41 + .../lib/helpers/model/applyDefaultsToPOJO.js | 52 + .../mongoose/lib/helpers/model/applyHooks.js | 151 + .../lib/helpers/model/applyMethods.js | 70 + .../lib/helpers/model/applyStaticHooks.js | 33 + .../lib/helpers/model/applyStatics.js | 13 + .../lib/helpers/model/castBulkWrite.js | 322 + .../helpers/model/decorateBulkWriteResult.js | 8 + .../lib/helpers/model/discriminator.js | 265 + .../lib/helpers/model/pushNestedArrayPaths.js | 15 + .../mongoose/lib/helpers/omitUndefined.js | 20 + .../node_modules/mongoose/lib/helpers/once.js | 12 + .../mongoose/lib/helpers/parallelLimit.js | 37 + .../mongoose/lib/helpers/path/parentPaths.js | 18 + .../lib/helpers/path/setDottedPath.js | 33 + .../mongoose/lib/helpers/pluralize.js | 95 + .../populate/assignRawDocsToIdStructure.js | 129 + .../lib/helpers/populate/assignVals.js | 360 ++ .../populate/createPopulateQueryFilter.js | 97 + .../populate/getModelsMapForPopulate.js | 894 +++ .../lib/helpers/populate/getSchemaTypes.js | 229 + .../lib/helpers/populate/getVirtual.js | 103 + .../lib/helpers/populate/leanPopulateMap.js | 7 + .../lib/helpers/populate/lookupLocalFields.js | 40 + .../populate/markArraySubdocsPopulated.js | 49 + .../helpers/populate/modelNamesFromRefPath.js | 74 + .../populate/removeDeselectedForeignField.js | 31 + .../populate/setPopulatedVirtualValue.js | 33 + .../lib/helpers/populate/skipPopulateValue.js | 10 + .../lib/helpers/populate/validateRef.js | 19 + .../mongoose/lib/helpers/printJestWarning.js | 21 + .../lib/helpers/processConnectionOptions.js | 65 + .../lib/helpers/projection/applyProjection.js | 83 + .../helpers/projection/hasIncludedChildren.js | 41 + .../projection/isDefiningProjection.js | 18 + .../lib/helpers/projection/isExclusive.js | 37 + .../lib/helpers/projection/isInclusive.js | 39 + .../helpers/projection/isNestedProjection.js | 8 + .../lib/helpers/projection/isPathExcluded.js | 40 + .../projection/isPathSelectedInclusive.js | 28 + .../lib/helpers/projection/isSubpath.js | 14 + .../lib/helpers/projection/parseProjection.js | 38 + .../lib/helpers/query/applyGlobalOption.js | 29 + .../mongoose/lib/helpers/query/cast$expr.js | 287 + .../lib/helpers/query/castFilterPath.js | 54 + .../mongoose/lib/helpers/query/castUpdate.js | 643 ++ .../query/getEmbeddedDiscriminatorPath.js | 103 + .../lib/helpers/query/handleImmutable.js | 44 + .../query/handleReadPreferenceAliases.js | 23 + .../lib/helpers/query/hasDollarKeys.js | 23 + .../mongoose/lib/helpers/query/isOperator.js | 14 + .../lib/helpers/query/sanitizeFilter.js | 41 + .../lib/helpers/query/sanitizeProjection.js | 14 + .../helpers/query/selectPopulatedFields.js | 62 + .../mongoose/lib/helpers/query/trusted.js | 13 + .../mongoose/lib/helpers/query/validOps.js | 3 + .../mongoose/lib/helpers/schema/addAutoId.js | 7 + .../lib/helpers/schema/applyBuiltinPlugins.js | 12 + .../lib/helpers/schema/applyPlugins.js | 55 + .../lib/helpers/schema/applyReadConcern.js | 20 + .../lib/helpers/schema/applyWriteConcern.js | 39 + .../schema/cleanPositionalOperators.js | 12 + .../mongoose/lib/helpers/schema/getIndexes.js | 171 + .../helpers/schema/getKeysInSchemaOrder.js | 28 + .../mongoose/lib/helpers/schema/getPath.js | 43 + .../schema/getSubdocumentStrictValue.js | 32 + .../lib/helpers/schema/handleIdOption.js | 20 + .../helpers/schema/handleTimestampOption.js | 24 + .../mongoose/lib/helpers/schema/idGetter.js | 34 + .../mongoose/lib/helpers/schema/merge.js | 36 + .../lib/helpers/schematype/handleImmutable.js | 50 + .../lib/helpers/setDefaultsOnInsert.js | 174 + .../mongoose/lib/helpers/specialProperties.js | 3 + .../mongoose/lib/helpers/symbols.js | 20 + .../mongoose/lib/helpers/timers.js | 3 + .../timestamps/setDocumentTimestamps.js | 26 + .../lib/helpers/timestamps/setupTimestamps.js | 132 + .../lib/helpers/topology/allServersUnknown.js | 12 + .../mongoose/lib/helpers/topology/isAtlas.js | 31 + .../lib/helpers/topology/isSSLError.js | 16 + .../update/applyTimestampsToChildren.js | 193 + .../helpers/update/applyTimestampsToUpdate.js | 131 + .../lib/helpers/update/castArrayFilters.js | 113 + .../update/decorateUpdateWithVersionKey.js | 35 + .../lib/helpers/update/modifiedPaths.js | 33 + .../helpers/update/moveImmutableProperties.js | 53 + .../update/removeUnusedArrayFilters.js | 37 + .../update/updatedPathsByArrayFilter.js | 27 + .../mongoose/lib/helpers/updateValidators.js | 186 + backend/node_modules/mongoose/lib/index.js | 17 + backend/node_modules/mongoose/lib/internal.js | 46 + backend/node_modules/mongoose/lib/model.js | 5202 +++++++++++++++ .../mongoose/lib/modifiedPathsSnapshot.js | 9 + backend/node_modules/mongoose/lib/mongoose.js | 1435 ++++ backend/node_modules/mongoose/lib/options.js | 18 + .../mongoose/lib/options/populateOptions.js | 36 + .../mongoose/lib/options/propertyOptions.js | 8 + .../mongoose/lib/options/saveOptions.js | 16 + .../lib/options/schemaArrayOptions.js | 78 + .../lib/options/schemaBufferOptions.js | 38 + .../mongoose/lib/options/schemaDateOptions.js | 71 + .../lib/options/schemaDocumentArrayOptions.js | 91 + .../mongoose/lib/options/schemaMapOptions.js | 43 + .../lib/options/schemaNumberOptions.js | 101 + .../lib/options/schemaObjectIdOptions.js | 64 + .../lib/options/schemaStringOptions.js | 138 + .../lib/options/schemaSubdocumentOptions.js | 89 + .../mongoose/lib/options/schemaTypeOptions.js | 244 + .../lib/options/schemaUnionOptions.js | 32 + .../mongoose/lib/options/virtualOptions.js | 164 + .../mongoose/lib/plugins/index.js | 5 + .../mongoose/lib/plugins/saveSubdocs.js | 94 + .../mongoose/lib/plugins/sharding.js | 101 + .../mongoose/lib/plugins/trackTransaction.js | 89 + backend/node_modules/mongoose/lib/query.js | 5760 +++++++++++++++++ .../node_modules/mongoose/lib/queryHelpers.js | 395 ++ backend/node_modules/mongoose/lib/schema.js | 3225 +++++++++ .../node_modules/mongoose/lib/schema/array.js | 702 ++ .../mongoose/lib/schema/bigint.js | 282 + .../mongoose/lib/schema/boolean.js | 332 + .../mongoose/lib/schema/buffer.js | 343 + .../node_modules/mongoose/lib/schema/date.js | 467 ++ .../mongoose/lib/schema/decimal128.js | 263 + .../mongoose/lib/schema/documentArray.js | 666 ++ .../lib/schema/documentArrayElement.js | 136 + .../mongoose/lib/schema/double.js | 246 + .../node_modules/mongoose/lib/schema/index.js | 32 + .../node_modules/mongoose/lib/schema/int32.js | 289 + .../node_modules/mongoose/lib/schema/map.js | 197 + .../node_modules/mongoose/lib/schema/mixed.js | 146 + .../mongoose/lib/schema/number.js | 510 ++ .../mongoose/lib/schema/objectId.js | 333 + .../mongoose/lib/schema/operators/bitwise.js | 38 + .../mongoose/lib/schema/operators/exists.js | 12 + .../lib/schema/operators/geospatial.js | 107 + .../mongoose/lib/schema/operators/helpers.js | 32 + .../mongoose/lib/schema/operators/text.js | 39 + .../mongoose/lib/schema/operators/type.js | 20 + .../mongoose/lib/schema/string.js | 733 +++ .../mongoose/lib/schema/subdocument.js | 436 ++ .../mongoose/lib/schema/symbols.js | 5 + .../node_modules/mongoose/lib/schema/union.js | 113 + .../node_modules/mongoose/lib/schema/uuid.js | 305 + .../node_modules/mongoose/lib/schemaType.js | 1842 ++++++ .../node_modules/mongoose/lib/stateMachine.js | 229 + .../mongoose/lib/types/array/index.js | 119 + .../lib/types/array/isMongooseArray.js | 5 + .../mongoose/lib/types/array/methods/index.js | 1095 ++++ .../mongoose/lib/types/arraySubdocument.js | 208 + .../node_modules/mongoose/lib/types/buffer.js | 294 + .../mongoose/lib/types/decimal128.js | 13 + .../mongoose/lib/types/documentArray/index.js | 113 + .../documentArray/isMongooseDocumentArray.js | 5 + .../lib/types/documentArray/methods/index.js | 415 ++ .../node_modules/mongoose/lib/types/double.js | 13 + .../node_modules/mongoose/lib/types/index.js | 23 + .../node_modules/mongoose/lib/types/map.js | 368 ++ .../mongoose/lib/types/objectid.js | 41 + .../mongoose/lib/types/subdocument.js | 443 ++ .../node_modules/mongoose/lib/types/uuid.js | 13 + backend/node_modules/mongoose/lib/utils.js | 1054 +++ .../node_modules/mongoose/lib/validOptions.js | 43 + .../node_modules/mongoose/lib/virtualType.js | 204 + backend/node_modules/mongoose/package.json | 139 + .../node_modules/mongoose/tstyche.config.json | 6 + .../mongoose/types/aggregate.d.ts | 185 + .../mongoose/types/augmentations.d.ts | 9 + .../node_modules/mongoose/types/callback.d.ts | 8 + .../mongoose/types/collection.d.ts | 49 + .../mongoose/types/connection.d.ts | 297 + .../node_modules/mongoose/types/cursor.d.ts | 67 + .../node_modules/mongoose/types/document.d.ts | 291 + .../node_modules/mongoose/types/error.d.ts | 143 + .../mongoose/types/expressions.d.ts | 3053 +++++++++ .../node_modules/mongoose/types/helpers.d.ts | 32 + .../node_modules/mongoose/types/index.d.ts | 1146 ++++ .../node_modules/mongoose/types/indexes.d.ts | 97 + .../mongoose/types/inferhydrateddoctype.d.ts | 135 + .../mongoose/types/inferrawdoctype.d.ts | 182 + .../mongoose/types/inferschematype.d.ts | 379 ++ .../mongoose/types/middlewares.d.ts | 66 + .../node_modules/mongoose/types/models.d.ts | 1265 ++++ .../mongoose/types/mongooseoptions.d.ts | 232 + .../mongoose/types/pipelinestage.d.ts | 334 + .../node_modules/mongoose/types/populate.d.ts | 53 + .../node_modules/mongoose/types/query.d.ts | 955 +++ .../mongoose/types/schemaoptions.d.ts | 284 + .../mongoose/types/schematypes.d.ts | 657 ++ .../node_modules/mongoose/types/session.d.ts | 32 + .../node_modules/mongoose/types/types.d.ts | 109 + .../node_modules/mongoose/types/utility.d.ts | 175 + .../mongoose/types/validation.d.ts | 39 + .../node_modules/mongoose/types/virtuals.d.ts | 14 + backend/node_modules/mpath/.travis.yml | 9 + backend/node_modules/mpath/History.md | 88 + backend/node_modules/mpath/LICENSE | 22 + backend/node_modules/mpath/README.md | 278 + backend/node_modules/mpath/SECURITY.md | 5 + backend/node_modules/mpath/index.js | 3 + backend/node_modules/mpath/lib/index.js | 336 + .../node_modules/mpath/lib/stringToParts.js | 48 + backend/node_modules/mpath/package.json | 144 + backend/node_modules/mpath/test/.eslintrc.yml | 4 + backend/node_modules/mpath/test/index.js | 1879 ++++++ .../node_modules/mpath/test/stringToParts.js | 30 + .../mquery/.github/ISSUE_TEMPLATE.md | 11 + .../mquery/.github/PULL_REQUEST_TEMPLATE.md | 9 + backend/node_modules/mquery/History.md | 393 ++ backend/node_modules/mquery/LICENSE | 22 + backend/node_modules/mquery/README.md | 1387 ++++ backend/node_modules/mquery/SECURITY.md | 1 + .../mquery/lib/collection/collection.js | 47 + .../mquery/lib/collection/index.js | 13 + .../mquery/lib/collection/node.js | 128 + backend/node_modules/mquery/lib/env.js | 22 + backend/node_modules/mquery/lib/mquery.js | 2748 ++++++++ .../node_modules/mquery/lib/permissions.js | 93 + backend/node_modules/mquery/lib/utils.js | 297 + backend/node_modules/mquery/package.json | 35 + backend/node_modules/sift/MIT-LICENSE.txt | 20 + backend/node_modules/sift/README.md | 465 ++ backend/node_modules/sift/es/index.js | 632 ++ backend/node_modules/sift/es/index.js.map | 1 + backend/node_modules/sift/es5m/index.js | 743 +++ backend/node_modules/sift/es5m/index.js.map | 1 + backend/node_modules/sift/index.d.ts | 4 + backend/node_modules/sift/index.js | 4 + backend/node_modules/sift/lib/core.d.ts | 116 + backend/node_modules/sift/lib/index.d.ts | 6 + backend/node_modules/sift/lib/index.js | 781 +++ backend/node_modules/sift/lib/index.js.map | 1 + backend/node_modules/sift/lib/operations.d.ts | 88 + backend/node_modules/sift/lib/utils.d.ts | 11 + backend/node_modules/sift/package.json | 61 + backend/node_modules/sift/sift.csp.min.js | 778 +++ backend/node_modules/sift/sift.csp.min.js.map | 1 + backend/node_modules/sift/sift.min.js | 2 + backend/node_modules/sift/sift.min.js.map | 1 + backend/node_modules/sift/src/core.ts | 494 ++ backend/node_modules/sift/src/index.ts | 54 + backend/node_modules/sift/src/operations.ts | 422 ++ backend/node_modules/sift/src/utils.ts | 74 + backend/package-lock.json | 57 +- backend/package.json | 3 +- frontend/js/api.js | 19 +- index.js | 60 +- 373 files changed, 82520 insertions(+), 46 deletions(-) create mode 100644 backend/node_modules/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json create mode 100644 backend/node_modules/kareem/.nyc_output/processinfo/fa57d6c4-9e78-4624-9229-f77b87a07481.json create mode 100644 backend/node_modules/kareem/.nyc_output/processinfo/index.json create mode 100644 backend/node_modules/kareem/CHANGELOG.md create mode 100644 backend/node_modules/kareem/LICENSE create mode 100644 backend/node_modules/kareem/README.md create mode 100644 backend/node_modules/kareem/SECURITY.md create mode 100644 backend/node_modules/kareem/coverage/lcov-report/base.css create mode 100644 backend/node_modules/kareem/coverage/lcov-report/block-navigation.js create mode 100644 backend/node_modules/kareem/coverage/lcov-report/favicon.png create mode 100644 backend/node_modules/kareem/coverage/lcov-report/index.html create mode 100644 backend/node_modules/kareem/coverage/lcov-report/index.js.html create mode 100644 backend/node_modules/kareem/coverage/lcov-report/prettify.css create mode 100644 backend/node_modules/kareem/coverage/lcov-report/prettify.js create mode 100644 backend/node_modules/kareem/coverage/lcov-report/sort-arrow-sprite.png create mode 100644 backend/node_modules/kareem/coverage/lcov-report/sorter.js create mode 100644 backend/node_modules/kareem/coverage/lcov.info create mode 100644 backend/node_modules/kareem/index.d.ts create mode 100644 backend/node_modules/kareem/index.js create mode 100644 backend/node_modules/kareem/package.json create mode 100644 backend/node_modules/mongoose/LICENSE.md create mode 100644 backend/node_modules/mongoose/README.md create mode 100644 backend/node_modules/mongoose/SECURITY.md create mode 100644 backend/node_modules/mongoose/eslint.config.mjs create mode 100644 backend/node_modules/mongoose/index.js create mode 100644 backend/node_modules/mongoose/lib/aggregate.js create mode 100644 backend/node_modules/mongoose/lib/cast.js create mode 100644 backend/node_modules/mongoose/lib/cast/bigint.js create mode 100644 backend/node_modules/mongoose/lib/cast/boolean.js create mode 100644 backend/node_modules/mongoose/lib/cast/date.js create mode 100644 backend/node_modules/mongoose/lib/cast/decimal128.js create mode 100644 backend/node_modules/mongoose/lib/cast/double.js create mode 100644 backend/node_modules/mongoose/lib/cast/int32.js create mode 100644 backend/node_modules/mongoose/lib/cast/number.js create mode 100644 backend/node_modules/mongoose/lib/cast/objectid.js create mode 100644 backend/node_modules/mongoose/lib/cast/string.js create mode 100644 backend/node_modules/mongoose/lib/cast/uuid.js create mode 100644 backend/node_modules/mongoose/lib/collection.js create mode 100644 backend/node_modules/mongoose/lib/connection.js create mode 100644 backend/node_modules/mongoose/lib/connectionState.js create mode 100644 backend/node_modules/mongoose/lib/constants.js create mode 100644 backend/node_modules/mongoose/lib/cursor/aggregationCursor.js create mode 100644 backend/node_modules/mongoose/lib/cursor/changeStream.js create mode 100644 backend/node_modules/mongoose/lib/cursor/queryCursor.js create mode 100644 backend/node_modules/mongoose/lib/document.js create mode 100644 backend/node_modules/mongoose/lib/driver.js create mode 100644 backend/node_modules/mongoose/lib/drivers/SPEC.md create mode 100644 backend/node_modules/mongoose/lib/drivers/node-mongodb-native/bulkWriteResult.js create mode 100644 backend/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js create mode 100644 backend/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js create mode 100644 backend/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js create mode 100644 backend/node_modules/mongoose/lib/error/browserMissingSchema.js create mode 100644 backend/node_modules/mongoose/lib/error/bulkSaveIncompleteError.js create mode 100644 backend/node_modules/mongoose/lib/error/bulkWriteError.js create mode 100644 backend/node_modules/mongoose/lib/error/cast.js create mode 100644 backend/node_modules/mongoose/lib/error/createCollectionsError.js create mode 100644 backend/node_modules/mongoose/lib/error/divergentArray.js create mode 100644 backend/node_modules/mongoose/lib/error/eachAsyncMultiError.js create mode 100644 backend/node_modules/mongoose/lib/error/index.js create mode 100644 backend/node_modules/mongoose/lib/error/invalidSchemaOption.js create mode 100644 backend/node_modules/mongoose/lib/error/messages.js create mode 100644 backend/node_modules/mongoose/lib/error/missingSchema.js create mode 100644 backend/node_modules/mongoose/lib/error/mongooseError.js create mode 100644 backend/node_modules/mongoose/lib/error/notFound.js create mode 100644 backend/node_modules/mongoose/lib/error/objectExpected.js create mode 100644 backend/node_modules/mongoose/lib/error/objectParameter.js create mode 100644 backend/node_modules/mongoose/lib/error/overwriteModel.js create mode 100644 backend/node_modules/mongoose/lib/error/parallelSave.js create mode 100644 backend/node_modules/mongoose/lib/error/parallelValidate.js create mode 100644 backend/node_modules/mongoose/lib/error/serverSelection.js create mode 100644 backend/node_modules/mongoose/lib/error/setOptionError.js create mode 100644 backend/node_modules/mongoose/lib/error/strict.js create mode 100644 backend/node_modules/mongoose/lib/error/strictPopulate.js create mode 100644 backend/node_modules/mongoose/lib/error/syncIndexes.js create mode 100644 backend/node_modules/mongoose/lib/error/validation.js create mode 100644 backend/node_modules/mongoose/lib/error/validator.js create mode 100644 backend/node_modules/mongoose/lib/error/version.js create mode 100644 backend/node_modules/mongoose/lib/helpers/aggregate/prepareDiscriminatorPipeline.js create mode 100644 backend/node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js create mode 100644 backend/node_modules/mongoose/lib/helpers/arrayDepth.js create mode 100644 backend/node_modules/mongoose/lib/helpers/buildMiddlewareFilter.js create mode 100644 backend/node_modules/mongoose/lib/helpers/clone.js create mode 100644 backend/node_modules/mongoose/lib/helpers/common.js create mode 100644 backend/node_modules/mongoose/lib/helpers/createJSONSchemaTypeDefinition.js create mode 100644 backend/node_modules/mongoose/lib/helpers/cursor/eachAsync.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/applyEmbeddedDiscriminators.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/getConstructor.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js create mode 100644 backend/node_modules/mongoose/lib/helpers/discriminator/mergeDiscriminatorSchema.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/applyDefaults.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/applyTimestamps.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/applyVirtuals.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/compile.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/getDeepestSubdocumentForPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js create mode 100644 backend/node_modules/mongoose/lib/helpers/document/isInPathsToSave.js create mode 100644 backend/node_modules/mongoose/lib/helpers/each.js create mode 100644 backend/node_modules/mongoose/lib/helpers/error/combinePathErrors.js create mode 100644 backend/node_modules/mongoose/lib/helpers/firstKey.js create mode 100644 backend/node_modules/mongoose/lib/helpers/get.js create mode 100644 backend/node_modules/mongoose/lib/helpers/getConstructorName.js create mode 100644 backend/node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js create mode 100644 backend/node_modules/mongoose/lib/helpers/getFunctionName.js create mode 100644 backend/node_modules/mongoose/lib/helpers/immediate.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/applySchemaCollation.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/getRelatedIndexes.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/isIndexSpecEqual.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/isTextIndex.js create mode 100644 backend/node_modules/mongoose/lib/helpers/indexes/isTimeseriesIndex.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isAsyncFunction.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isBsonType.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isMongooseObject.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isObject.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isPOJO.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isPromise.js create mode 100644 backend/node_modules/mongoose/lib/helpers/isSimpleValidator.js create mode 100644 backend/node_modules/mongoose/lib/helpers/minimize.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/applyDefaultsToPOJO.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/applyHooks.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/applyMethods.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/applyStaticHooks.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/applyStatics.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/castBulkWrite.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/decorateBulkWriteResult.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/discriminator.js create mode 100644 backend/node_modules/mongoose/lib/helpers/model/pushNestedArrayPaths.js create mode 100644 backend/node_modules/mongoose/lib/helpers/omitUndefined.js create mode 100644 backend/node_modules/mongoose/lib/helpers/once.js create mode 100644 backend/node_modules/mongoose/lib/helpers/parallelLimit.js create mode 100644 backend/node_modules/mongoose/lib/helpers/path/parentPaths.js create mode 100644 backend/node_modules/mongoose/lib/helpers/path/setDottedPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/pluralize.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/assignVals.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/getVirtual.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/setPopulatedVirtualValue.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/skipPopulateValue.js create mode 100644 backend/node_modules/mongoose/lib/helpers/populate/validateRef.js create mode 100644 backend/node_modules/mongoose/lib/helpers/printJestWarning.js create mode 100644 backend/node_modules/mongoose/lib/helpers/processConnectionOptions.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/applyProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/hasIncludedChildren.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isExclusive.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isInclusive.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isNestedProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/isSubpath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/projection/parseProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/applyGlobalOption.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/cast$expr.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/castFilterPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/castUpdate.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/handleImmutable.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/handleReadPreferenceAliases.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/isOperator.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/sanitizeFilter.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/sanitizeProjection.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/trusted.js create mode 100644 backend/node_modules/mongoose/lib/helpers/query/validOps.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/addAutoId.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/applyBuiltinPlugins.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/applyPlugins.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/applyReadConcern.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/getIndexes.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/getKeysInSchemaOrder.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/getPath.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/getSubdocumentStrictValue.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/handleIdOption.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/idGetter.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schema/merge.js create mode 100644 backend/node_modules/mongoose/lib/helpers/schematype/handleImmutable.js create mode 100644 backend/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js create mode 100644 backend/node_modules/mongoose/lib/helpers/specialProperties.js create mode 100644 backend/node_modules/mongoose/lib/helpers/symbols.js create mode 100644 backend/node_modules/mongoose/lib/helpers/timers.js create mode 100644 backend/node_modules/mongoose/lib/helpers/timestamps/setDocumentTimestamps.js create mode 100644 backend/node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js create mode 100644 backend/node_modules/mongoose/lib/helpers/topology/allServersUnknown.js create mode 100644 backend/node_modules/mongoose/lib/helpers/topology/isAtlas.js create mode 100644 backend/node_modules/mongoose/lib/helpers/topology/isSSLError.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/castArrayFilters.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/decorateUpdateWithVersionKey.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/modifiedPaths.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js create mode 100644 backend/node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js create mode 100644 backend/node_modules/mongoose/lib/helpers/updateValidators.js create mode 100644 backend/node_modules/mongoose/lib/index.js create mode 100644 backend/node_modules/mongoose/lib/internal.js create mode 100644 backend/node_modules/mongoose/lib/model.js create mode 100644 backend/node_modules/mongoose/lib/modifiedPathsSnapshot.js create mode 100644 backend/node_modules/mongoose/lib/mongoose.js create mode 100644 backend/node_modules/mongoose/lib/options.js create mode 100644 backend/node_modules/mongoose/lib/options/populateOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/propertyOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/saveOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaArrayOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaBufferOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaDateOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaDocumentArrayOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaMapOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaNumberOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaObjectIdOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaStringOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaSubdocumentOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaTypeOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/schemaUnionOptions.js create mode 100644 backend/node_modules/mongoose/lib/options/virtualOptions.js create mode 100644 backend/node_modules/mongoose/lib/plugins/index.js create mode 100644 backend/node_modules/mongoose/lib/plugins/saveSubdocs.js create mode 100644 backend/node_modules/mongoose/lib/plugins/sharding.js create mode 100644 backend/node_modules/mongoose/lib/plugins/trackTransaction.js create mode 100644 backend/node_modules/mongoose/lib/query.js create mode 100644 backend/node_modules/mongoose/lib/queryHelpers.js create mode 100644 backend/node_modules/mongoose/lib/schema.js create mode 100644 backend/node_modules/mongoose/lib/schema/array.js create mode 100644 backend/node_modules/mongoose/lib/schema/bigint.js create mode 100644 backend/node_modules/mongoose/lib/schema/boolean.js create mode 100644 backend/node_modules/mongoose/lib/schema/buffer.js create mode 100644 backend/node_modules/mongoose/lib/schema/date.js create mode 100644 backend/node_modules/mongoose/lib/schema/decimal128.js create mode 100644 backend/node_modules/mongoose/lib/schema/documentArray.js create mode 100644 backend/node_modules/mongoose/lib/schema/documentArrayElement.js create mode 100644 backend/node_modules/mongoose/lib/schema/double.js create mode 100644 backend/node_modules/mongoose/lib/schema/index.js create mode 100644 backend/node_modules/mongoose/lib/schema/int32.js create mode 100644 backend/node_modules/mongoose/lib/schema/map.js create mode 100644 backend/node_modules/mongoose/lib/schema/mixed.js create mode 100644 backend/node_modules/mongoose/lib/schema/number.js create mode 100644 backend/node_modules/mongoose/lib/schema/objectId.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/bitwise.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/exists.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/geospatial.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/helpers.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/text.js create mode 100644 backend/node_modules/mongoose/lib/schema/operators/type.js create mode 100644 backend/node_modules/mongoose/lib/schema/string.js create mode 100644 backend/node_modules/mongoose/lib/schema/subdocument.js create mode 100644 backend/node_modules/mongoose/lib/schema/symbols.js create mode 100644 backend/node_modules/mongoose/lib/schema/union.js create mode 100644 backend/node_modules/mongoose/lib/schema/uuid.js create mode 100644 backend/node_modules/mongoose/lib/schemaType.js create mode 100644 backend/node_modules/mongoose/lib/stateMachine.js create mode 100644 backend/node_modules/mongoose/lib/types/array/index.js create mode 100644 backend/node_modules/mongoose/lib/types/array/isMongooseArray.js create mode 100644 backend/node_modules/mongoose/lib/types/array/methods/index.js create mode 100644 backend/node_modules/mongoose/lib/types/arraySubdocument.js create mode 100644 backend/node_modules/mongoose/lib/types/buffer.js create mode 100644 backend/node_modules/mongoose/lib/types/decimal128.js create mode 100644 backend/node_modules/mongoose/lib/types/documentArray/index.js create mode 100644 backend/node_modules/mongoose/lib/types/documentArray/isMongooseDocumentArray.js create mode 100644 backend/node_modules/mongoose/lib/types/documentArray/methods/index.js create mode 100644 backend/node_modules/mongoose/lib/types/double.js create mode 100644 backend/node_modules/mongoose/lib/types/index.js create mode 100644 backend/node_modules/mongoose/lib/types/map.js create mode 100644 backend/node_modules/mongoose/lib/types/objectid.js create mode 100644 backend/node_modules/mongoose/lib/types/subdocument.js create mode 100644 backend/node_modules/mongoose/lib/types/uuid.js create mode 100644 backend/node_modules/mongoose/lib/utils.js create mode 100644 backend/node_modules/mongoose/lib/validOptions.js create mode 100644 backend/node_modules/mongoose/lib/virtualType.js create mode 100644 backend/node_modules/mongoose/package.json create mode 100644 backend/node_modules/mongoose/tstyche.config.json create mode 100644 backend/node_modules/mongoose/types/aggregate.d.ts create mode 100644 backend/node_modules/mongoose/types/augmentations.d.ts create mode 100644 backend/node_modules/mongoose/types/callback.d.ts create mode 100644 backend/node_modules/mongoose/types/collection.d.ts create mode 100644 backend/node_modules/mongoose/types/connection.d.ts create mode 100644 backend/node_modules/mongoose/types/cursor.d.ts create mode 100644 backend/node_modules/mongoose/types/document.d.ts create mode 100644 backend/node_modules/mongoose/types/error.d.ts create mode 100644 backend/node_modules/mongoose/types/expressions.d.ts create mode 100644 backend/node_modules/mongoose/types/helpers.d.ts create mode 100644 backend/node_modules/mongoose/types/index.d.ts create mode 100644 backend/node_modules/mongoose/types/indexes.d.ts create mode 100644 backend/node_modules/mongoose/types/inferhydrateddoctype.d.ts create mode 100644 backend/node_modules/mongoose/types/inferrawdoctype.d.ts create mode 100644 backend/node_modules/mongoose/types/inferschematype.d.ts create mode 100644 backend/node_modules/mongoose/types/middlewares.d.ts create mode 100644 backend/node_modules/mongoose/types/models.d.ts create mode 100644 backend/node_modules/mongoose/types/mongooseoptions.d.ts create mode 100644 backend/node_modules/mongoose/types/pipelinestage.d.ts create mode 100644 backend/node_modules/mongoose/types/populate.d.ts create mode 100644 backend/node_modules/mongoose/types/query.d.ts create mode 100644 backend/node_modules/mongoose/types/schemaoptions.d.ts create mode 100644 backend/node_modules/mongoose/types/schematypes.d.ts create mode 100644 backend/node_modules/mongoose/types/session.d.ts create mode 100644 backend/node_modules/mongoose/types/types.d.ts create mode 100644 backend/node_modules/mongoose/types/utility.d.ts create mode 100644 backend/node_modules/mongoose/types/validation.d.ts create mode 100644 backend/node_modules/mongoose/types/virtuals.d.ts create mode 100644 backend/node_modules/mpath/.travis.yml create mode 100644 backend/node_modules/mpath/History.md create mode 100644 backend/node_modules/mpath/LICENSE create mode 100644 backend/node_modules/mpath/README.md create mode 100644 backend/node_modules/mpath/SECURITY.md create mode 100644 backend/node_modules/mpath/index.js create mode 100644 backend/node_modules/mpath/lib/index.js create mode 100644 backend/node_modules/mpath/lib/stringToParts.js create mode 100644 backend/node_modules/mpath/package.json create mode 100644 backend/node_modules/mpath/test/.eslintrc.yml create mode 100644 backend/node_modules/mpath/test/index.js create mode 100644 backend/node_modules/mpath/test/stringToParts.js create mode 100644 backend/node_modules/mquery/.github/ISSUE_TEMPLATE.md create mode 100644 backend/node_modules/mquery/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 backend/node_modules/mquery/History.md create mode 100644 backend/node_modules/mquery/LICENSE create mode 100644 backend/node_modules/mquery/README.md create mode 100644 backend/node_modules/mquery/SECURITY.md create mode 100644 backend/node_modules/mquery/lib/collection/collection.js create mode 100644 backend/node_modules/mquery/lib/collection/index.js create mode 100644 backend/node_modules/mquery/lib/collection/node.js create mode 100644 backend/node_modules/mquery/lib/env.js create mode 100644 backend/node_modules/mquery/lib/mquery.js create mode 100644 backend/node_modules/mquery/lib/permissions.js create mode 100644 backend/node_modules/mquery/lib/utils.js create mode 100644 backend/node_modules/mquery/package.json create mode 100644 backend/node_modules/sift/MIT-LICENSE.txt create mode 100755 backend/node_modules/sift/README.md create mode 100644 backend/node_modules/sift/es/index.js create mode 100644 backend/node_modules/sift/es/index.js.map create mode 100644 backend/node_modules/sift/es5m/index.js create mode 100644 backend/node_modules/sift/es5m/index.js.map create mode 100644 backend/node_modules/sift/index.d.ts create mode 100644 backend/node_modules/sift/index.js create mode 100644 backend/node_modules/sift/lib/core.d.ts create mode 100644 backend/node_modules/sift/lib/index.d.ts create mode 100644 backend/node_modules/sift/lib/index.js create mode 100644 backend/node_modules/sift/lib/index.js.map create mode 100644 backend/node_modules/sift/lib/operations.d.ts create mode 100644 backend/node_modules/sift/lib/utils.d.ts create mode 100644 backend/node_modules/sift/package.json create mode 100644 backend/node_modules/sift/sift.csp.min.js create mode 100644 backend/node_modules/sift/sift.csp.min.js.map create mode 100644 backend/node_modules/sift/sift.min.js create mode 100644 backend/node_modules/sift/sift.min.js.map create mode 100644 backend/node_modules/sift/src/core.ts create mode 100644 backend/node_modules/sift/src/index.ts create mode 100644 backend/node_modules/sift/src/operations.ts create mode 100644 backend/node_modules/sift/src/utils.ts diff --git a/.gitignore b/.gitignore index 0ff7529..6a9d7bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ backend\node_modules +backend\.env diff --git a/backend/index.js b/backend/index.js index 46f56eb..811964a 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,4 +1,6 @@ +require("dotenv").config(); const express = require('express'); +const mongoose = require("mongoose") const cors = require('cors'); const port = 3001; @@ -8,9 +10,9 @@ app.use(express.json()); app.use(cors("*")); -app.get("/test", async (req, res) => { - res.send("Hello World!"); -}); +// app.get("/test", async (req, res) => { +// res.send("Hello World!"); +// }); const tasks = [ { @@ -85,4 +87,4 @@ app.post("/api/tasks/todo", async (req, res) => { app.listen(port, () => { console.log(`To Do App listening on port ${port}`); -}); \ No newline at end of file +}); diff --git a/backend/node_modules/.package-lock.json b/backend/node_modules/.package-lock.json index b26c4bb..c3bac54 100644 --- a/backend/node_modules/.package-lock.json +++ b/backend/node_modules/.package-lock.json @@ -502,6 +502,15 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/kareem": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.2.0.tgz", + "integrity": "sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -622,6 +631,45 @@ "node": ">=20.19.0" } }, + "node_modules/mongoose": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.3.3.tgz", + "integrity": "sha512-sfv5LOIPWeN5o/281kp4Rx9ZnuXb0g8CtvBTi7trYQs2PYYx8LWXegXxG3ar7VEns1o+d4h9LI/Dtc7dTTyYmA==", + "license": "MIT", + "dependencies": { + "kareem": "3.2.0", + "mongodb": "~7.1", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -904,6 +952,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", diff --git a/backend/node_modules/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json b/backend/node_modules/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json new file mode 100644 index 0000000..8e84c99 --- /dev/null +++ b/backend/node_modules/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json @@ -0,0 +1 @@ +{"/home/v/Desktop/MongoDB/kareem/index.js":{"path":"/home/v/Desktop/MongoDB/kareem/index.js","statementMap":{"0":{"start":{"line":7,"column":2},"end":{"line":7,"column":25}},"1":{"start":{"line":8,"column":2},"end":{"line":8,"column":26}},"2":{"start":{"line":11,"column":0},"end":{"line":17,"column":2}},"3":{"start":{"line":12,"column":2},"end":{"line":14,"column":3}},"4":{"start":{"line":13,"column":4},"end":{"line":13,"column":56}},"5":{"start":{"line":16,"column":2},"end":{"line":16,"column":29}},"6":{"start":{"line":19,"column":0},"end":{"line":25,"column":2}},"7":{"start":{"line":20,"column":2},"end":{"line":22,"column":3}},"8":{"start":{"line":21,"column":4},"end":{"line":21,"column":52}},"9":{"start":{"line":24,"column":2},"end":{"line":24,"column":29}},"10":{"start":{"line":34,"column":0},"end":{"line":94,"column":2}},"11":{"start":{"line":35,"column":15},"end":{"line":35,"column":41}},"12":{"start":{"line":36,"column":18},"end":{"line":36,"column":29}},"13":{"start":{"line":37,"column":16},"end":{"line":37,"column":20}},"14":{"start":{"line":38,"column":28},"end":{"line":38,"column":32}},"15":{"start":{"line":40,"column":2},"end":{"line":42,"column":3}},"16":{"start":{"line":41,"column":4},"end":{"line":41,"column":11}},"17":{"start":{"line":44,"column":27},"end":{"line":44,"column":29}},"18":{"start":{"line":45,"column":2},"end":{"line":87,"column":3}},"19":{"start":{"line":46,"column":4},"end":{"line":86,"column":5}},"20":{"start":{"line":47,"column":20},"end":{"line":47,"column":24}},"21":{"start":{"line":48,"column":19},"end":{"line":48,"column":23}},"22":{"start":{"line":49,"column":24},"end":{"line":52,"column":8}},"23":{"start":{"line":50,"column":8},"end":{"line":50,"column":27}},"24":{"start":{"line":51,"column":8},"end":{"line":51,"column":25}},"25":{"start":{"line":53,"column":19},"end":{"line":53,"column":59}},"26":{"start":{"line":53,"column":29},"end":{"line":53,"column":58}},"27":{"start":{"line":54,"column":20},"end":{"line":54,"column":40}},"28":{"start":{"line":55,"column":6},"end":{"line":60,"column":7}},"29":{"start":{"line":55,"column":19},"end":{"line":55,"column":20}},"30":{"start":{"line":56,"column":8},"end":{"line":58,"column":9}},"31":{"start":{"line":57,"column":10},"end":{"line":57,"column":19}},"32":{"start":{"line":59,"column":8},"end":{"line":59,"column":28}},"33":{"start":{"line":62,"column":6},"end":{"line":75,"column":7}},"34":{"start":{"line":63,"column":33},"end":{"line":63,"column":60}},"35":{"start":{"line":64,"column":8},"end":{"line":68,"column":9}},"36":{"start":{"line":65,"column":10},"end":{"line":65,"column":33}},"37":{"start":{"line":67,"column":10},"end":{"line":67,"column":26}},"38":{"start":{"line":70,"column":8},"end":{"line":73,"column":9}},"39":{"start":{"line":71,"column":10},"end":{"line":71,"column":38}},"40":{"start":{"line":72,"column":10},"end":{"line":72,"column":19}},"41":{"start":{"line":74,"column":8},"end":{"line":74,"column":20}},"42":{"start":{"line":77,"column":6},"end":{"line":85,"column":7}},"43":{"start":{"line":78,"column":8},"end":{"line":78,"column":35}},"44":{"start":{"line":80,"column":8},"end":{"line":83,"column":9}},"45":{"start":{"line":81,"column":10},"end":{"line":81,"column":38}},"46":{"start":{"line":82,"column":10},"end":{"line":82,"column":19}},"47":{"start":{"line":84,"column":8},"end":{"line":84,"column":20}},"48":{"start":{"line":89,"column":2},"end":{"line":89,"column":38}},"49":{"start":{"line":91,"column":2},"end":{"line":93,"column":3}},"50":{"start":{"line":92,"column":4},"end":{"line":92,"column":30}},"51":{"start":{"line":103,"column":0},"end":{"line":110,"column":2}},"52":{"start":{"line":104,"column":15},"end":{"line":104,"column":41}},"53":{"start":{"line":105,"column":18},"end":{"line":105,"column":29}},"54":{"start":{"line":107,"column":2},"end":{"line":109,"column":3}},"55":{"start":{"line":107,"column":15},"end":{"line":107,"column":16}},"56":{"start":{"line":108,"column":4},"end":{"line":108,"column":42}},"57":{"start":{"line":120,"column":0},"end":{"line":225,"column":2}},"58":{"start":{"line":121,"column":16},"end":{"line":121,"column":43}},"59":{"start":{"line":122,"column":19},"end":{"line":122,"column":31}},"60":{"start":{"line":124,"column":19},"end":{"line":124,"column":23}},"61":{"start":{"line":125,"column":2},"end":{"line":127,"column":3}},"62":{"start":{"line":126,"column":4},"end":{"line":126,"column":31}},"63":{"start":{"line":129,"column":2},"end":{"line":134,"column":3}},"64":{"start":{"line":130,"column":4},"end":{"line":132,"column":5}},"65":{"start":{"line":131,"column":6},"end":{"line":131,"column":23}},"66":{"start":{"line":133,"column":4},"end":{"line":133,"column":16}},"67":{"start":{"line":136,"column":2},"end":{"line":218,"column":3}},"68":{"start":{"line":137,"column":17},"end":{"line":137,"column":31}},"69":{"start":{"line":138,"column":18},"end":{"line":138,"column":19}},"70":{"start":{"line":139,"column":20},"end":{"line":139,"column":22}},"71":{"start":{"line":140,"column":22},"end":{"line":140,"column":33}},"72":{"start":{"line":141,"column":4},"end":{"line":146,"column":5}},"73":{"start":{"line":141,"column":17},"end":{"line":141,"column":18}},"74":{"start":{"line":142,"column":6},"end":{"line":145,"column":7}},"75":{"start":{"line":143,"column":8},"end":{"line":143,"column":21}},"76":{"start":{"line":144,"column":8},"end":{"line":144,"column":30}},"77":{"start":{"line":148,"column":4},"end":{"line":153,"column":5}},"78":{"start":{"line":149,"column":6},"end":{"line":149,"column":42}},"79":{"start":{"line":150,"column":6},"end":{"line":152,"column":7}},"80":{"start":{"line":150,"column":19},"end":{"line":150,"column":33}},"81":{"start":{"line":151,"column":8},"end":{"line":151,"column":27}},"82":{"start":{"line":157,"column":22},"end":{"line":160,"column":6}},"83":{"start":{"line":158,"column":6},"end":{"line":158,"column":25}},"84":{"start":{"line":159,"column":6},"end":{"line":159,"column":23}},"85":{"start":{"line":161,"column":4},"end":{"line":167,"column":7}},"86":{"start":{"line":162,"column":6},"end":{"line":166,"column":7}},"87":{"start":{"line":163,"column":8},"end":{"line":163,"column":20}},"88":{"start":{"line":165,"column":8},"end":{"line":165,"column":18}},"89":{"start":{"line":169,"column":4},"end":{"line":217,"column":5}},"90":{"start":{"line":170,"column":6},"end":{"line":188,"column":7}},"91":{"start":{"line":171,"column":8},"end":{"line":185,"column":9}},"92":{"start":{"line":172,"column":22},"end":{"line":172,"column":71}},"93":{"start":{"line":173,"column":10},"end":{"line":178,"column":11}},"94":{"start":{"line":174,"column":12},"end":{"line":174,"column":22}},"95":{"start":{"line":175,"column":17},"end":{"line":178,"column":11}},"96":{"start":{"line":177,"column":12},"end":{"line":177,"column":28}},"97":{"start":{"line":180,"column":10},"end":{"line":183,"column":11}},"98":{"start":{"line":181,"column":12},"end":{"line":181,"column":30}},"99":{"start":{"line":182,"column":12},"end":{"line":182,"column":21}},"100":{"start":{"line":184,"column":10},"end":{"line":184,"column":29}},"101":{"start":{"line":187,"column":8},"end":{"line":187,"column":17}},"102":{"start":{"line":190,"column":6},"end":{"line":216,"column":7}},"103":{"start":{"line":192,"column":8},"end":{"line":192,"column":17}},"104":{"start":{"line":194,"column":18},"end":{"line":194,"column":22}},"105":{"start":{"line":195,"column":8},"end":{"line":210,"column":9}},"106":{"start":{"line":196,"column":10},"end":{"line":196,"column":45}},"107":{"start":{"line":197,"column":10},"end":{"line":202,"column":11}},"108":{"start":{"line":198,"column":12},"end":{"line":198,"column":28}},"109":{"start":{"line":199,"column":17},"end":{"line":202,"column":11}},"110":{"start":{"line":201,"column":12},"end":{"line":201,"column":34}},"111":{"start":{"line":204,"column":10},"end":{"line":207,"column":11}},"112":{"start":{"line":205,"column":12},"end":{"line":205,"column":30}},"113":{"start":{"line":206,"column":12},"end":{"line":206,"column":21}},"114":{"start":{"line":208,"column":10},"end":{"line":208,"column":29}},"115":{"start":{"line":209,"column":10},"end":{"line":209,"column":19}},"116":{"start":{"line":212,"column":8},"end":{"line":215,"column":9}},"117":{"start":{"line":213,"column":10},"end":{"line":213,"column":26}},"118":{"start":{"line":214,"column":10},"end":{"line":214,"column":19}},"119":{"start":{"line":220,"column":2},"end":{"line":222,"column":3}},"120":{"start":{"line":221,"column":4},"end":{"line":221,"column":21}},"121":{"start":{"line":224,"column":2},"end":{"line":224,"column":14}},"122":{"start":{"line":234,"column":0},"end":{"line":246,"column":2}},"123":{"start":{"line":235,"column":16},"end":{"line":235,"column":43}},"124":{"start":{"line":236,"column":19},"end":{"line":236,"column":31}},"125":{"start":{"line":238,"column":2},"end":{"line":243,"column":3}},"126":{"start":{"line":238,"column":15},"end":{"line":238,"column":16}},"127":{"start":{"line":239,"column":16},"end":{"line":239,"column":54}},"128":{"start":{"line":240,"column":4},"end":{"line":242,"column":5}},"129":{"start":{"line":241,"column":6},"end":{"line":241,"column":22}},"130":{"start":{"line":245,"column":2},"end":{"line":245,"column":14}},"131":{"start":{"line":254,"column":0},"end":{"line":265,"column":2}},"132":{"start":{"line":255,"column":16},"end":{"line":255,"column":20}},"133":{"start":{"line":256,"column":2},"end":{"line":264,"column":4}},"134":{"start":{"line":257,"column":4},"end":{"line":257,"column":45}},"135":{"start":{"line":259,"column":21},"end":{"line":259,"column":46}},"136":{"start":{"line":261,"column":19},"end":{"line":261,"column":61}},"137":{"start":{"line":263,"column":4},"end":{"line":263,"column":21}},"138":{"start":{"line":276,"column":0},"end":{"line":297,"column":2}},"139":{"start":{"line":278,"column":28},"end":{"line":278,"column":33}},"140":{"start":{"line":279,"column":2},"end":{"line":288,"column":3}},"141":{"start":{"line":280,"column":4},"end":{"line":280,"column":44}},"142":{"start":{"line":282,"column":4},"end":{"line":287,"column":5}},"143":{"start":{"line":283,"column":6},"end":{"line":283,"column":23}},"144":{"start":{"line":284,"column":6},"end":{"line":284,"column":33}},"145":{"start":{"line":286,"column":6},"end":{"line":286,"column":70}},"146":{"start":{"line":290,"column":2},"end":{"line":292,"column":3}},"147":{"start":{"line":291,"column":4},"end":{"line":291,"column":40}},"148":{"start":{"line":294,"column":2},"end":{"line":294,"column":59}},"149":{"start":{"line":296,"column":2},"end":{"line":296,"column":16}},"150":{"start":{"line":304,"column":0},"end":{"line":336,"column":2}},"151":{"start":{"line":305,"column":16},"end":{"line":305,"column":28}},"152":{"start":{"line":307,"column":15},"end":{"line":307,"column":45}},"153":{"start":{"line":308,"column":2},"end":{"line":319,"column":3}},"154":{"start":{"line":309,"column":18},"end":{"line":311,"column":16}},"155":{"start":{"line":310,"column":15},"end":{"line":310,"column":51}},"156":{"start":{"line":313,"column":4},"end":{"line":316,"column":5}},"157":{"start":{"line":314,"column":6},"end":{"line":314,"column":31}},"158":{"start":{"line":315,"column":6},"end":{"line":315,"column":15}},"159":{"start":{"line":318,"column":4},"end":{"line":318,"column":33}},"160":{"start":{"line":321,"column":16},"end":{"line":321,"column":47}},"161":{"start":{"line":322,"column":2},"end":{"line":333,"column":3}},"162":{"start":{"line":323,"column":18},"end":{"line":325,"column":16}},"163":{"start":{"line":324,"column":15},"end":{"line":324,"column":51}},"164":{"start":{"line":327,"column":4},"end":{"line":330,"column":5}},"165":{"start":{"line":328,"column":6},"end":{"line":328,"column":32}},"166":{"start":{"line":329,"column":6},"end":{"line":329,"column":15}},"167":{"start":{"line":332,"column":4},"end":{"line":332,"column":34}},"168":{"start":{"line":335,"column":2},"end":{"line":335,"column":15}},"169":{"start":{"line":343,"column":0},"end":{"line":345,"column":2}},"170":{"start":{"line":344,"column":2},"end":{"line":344,"column":55}},"171":{"start":{"line":355,"column":0},"end":{"line":365,"column":2}},"172":{"start":{"line":356,"column":16},"end":{"line":356,"column":20}},"173":{"start":{"line":357,"column":2},"end":{"line":360,"column":3}},"174":{"start":{"line":359,"column":4},"end":{"line":359,"column":14}},"175":{"start":{"line":361,"column":2},"end":{"line":364,"column":4}},"176":{"start":{"line":362,"column":21},"end":{"line":362,"column":36}},"177":{"start":{"line":363,"column":4},"end":{"line":363,"column":74}},"178":{"start":{"line":376,"column":0},"end":{"line":398,"column":2}},"179":{"start":{"line":377,"column":2},"end":{"line":382,"column":3}},"180":{"start":{"line":378,"column":4},"end":{"line":378,"column":17}},"181":{"start":{"line":379,"column":4},"end":{"line":379,"column":17}},"182":{"start":{"line":380,"column":9},"end":{"line":382,"column":3}},"183":{"start":{"line":381,"column":4},"end":{"line":381,"column":17}},"184":{"start":{"line":384,"column":15},"end":{"line":384,"column":41}},"185":{"start":{"line":385,"column":2},"end":{"line":385,"column":29}},"186":{"start":{"line":387,"column":2},"end":{"line":389,"column":3}},"187":{"start":{"line":388,"column":4},"end":{"line":388,"column":74}},"188":{"start":{"line":391,"column":2},"end":{"line":395,"column":3}},"189":{"start":{"line":392,"column":4},"end":{"line":392,"column":57}},"190":{"start":{"line":394,"column":4},"end":{"line":394,"column":54}},"191":{"start":{"line":397,"column":2},"end":{"line":397,"column":14}},"192":{"start":{"line":409,"column":0},"end":{"line":429,"column":2}},"193":{"start":{"line":410,"column":16},"end":{"line":410,"column":43}},"194":{"start":{"line":412,"column":2},"end":{"line":416,"column":3}},"195":{"start":{"line":413,"column":4},"end":{"line":413,"column":19}},"196":{"start":{"line":414,"column":4},"end":{"line":414,"column":17}},"197":{"start":{"line":415,"column":4},"end":{"line":415,"column":17}},"198":{"start":{"line":418,"column":2},"end":{"line":420,"column":3}},"199":{"start":{"line":419,"column":4},"end":{"line":419,"column":75}},"200":{"start":{"line":422,"column":2},"end":{"line":426,"column":3}},"201":{"start":{"line":423,"column":4},"end":{"line":423,"column":58}},"202":{"start":{"line":425,"column":4},"end":{"line":425,"column":55}},"203":{"start":{"line":427,"column":2},"end":{"line":427,"column":31}},"204":{"start":{"line":428,"column":2},"end":{"line":428,"column":14}},"205":{"start":{"line":440,"column":0},"end":{"line":447,"column":2}},"206":{"start":{"line":441,"column":2},"end":{"line":445,"column":3}},"207":{"start":{"line":442,"column":4},"end":{"line":442,"column":19}},"208":{"start":{"line":443,"column":4},"end":{"line":443,"column":17}},"209":{"start":{"line":444,"column":4},"end":{"line":444,"column":17}},"210":{"start":{"line":446,"column":2},"end":{"line":446,"column":74}},"211":{"start":{"line":453,"column":0},"end":{"line":465,"column":2}},"212":{"start":{"line":454,"column":12},"end":{"line":454,"column":24}},"213":{"start":{"line":456,"column":2},"end":{"line":459,"column":3}},"214":{"start":{"line":457,"column":18},"end":{"line":457,"column":45}},"215":{"start":{"line":458,"column":4},"end":{"line":458,"column":28}},"216":{"start":{"line":460,"column":2},"end":{"line":462,"column":3}},"217":{"start":{"line":461,"column":4},"end":{"line":461,"column":52}},"218":{"start":{"line":464,"column":2},"end":{"line":464,"column":11}},"219":{"start":{"line":473,"column":0},"end":{"line":493,"column":2}},"220":{"start":{"line":474,"column":2},"end":{"line":474,"column":48}},"221":{"start":{"line":475,"column":14},"end":{"line":475,"column":41}},"222":{"start":{"line":477,"column":2},"end":{"line":484,"column":3}},"223":{"start":{"line":478,"column":23},"end":{"line":478,"column":47}},"224":{"start":{"line":479,"column":25},"end":{"line":481,"column":67}},"225":{"start":{"line":481,"column":18},"end":{"line":481,"column":66}},"226":{"start":{"line":481,"column":39},"end":{"line":481,"column":44}},"227":{"start":{"line":482,"column":21},"end":{"line":482,"column":52}},"228":{"start":{"line":483,"column":4},"end":{"line":483,"column":33}},"229":{"start":{"line":485,"column":2},"end":{"line":490,"column":3}},"230":{"start":{"line":486,"column":24},"end":{"line":486,"column":49}},"231":{"start":{"line":487,"column":25},"end":{"line":488,"column":48}},"232":{"start":{"line":488,"column":18},"end":{"line":488,"column":47}},"233":{"start":{"line":489,"column":4},"end":{"line":489,"column":58}},"234":{"start":{"line":492,"column":2},"end":{"line":492,"column":13}},"235":{"start":{"line":496,"column":2},"end":{"line":496,"column":79}},"236":{"start":{"line":500,"column":2},"end":{"line":502,"column":3}},"237":{"start":{"line":501,"column":4},"end":{"line":501,"column":16}},"238":{"start":{"line":503,"column":2},"end":{"line":503,"column":40}},"239":{"start":{"line":506,"column":0},"end":{"line":506,"column":24}}},"fnMap":{"0":{"name":"Kareem","decl":{"start":{"line":6,"column":9},"end":{"line":6,"column":15}},"loc":{"start":{"line":6,"column":18},"end":{"line":9,"column":1}},"line":6},"1":{"name":"skipWrappedFunction","decl":{"start":{"line":11,"column":38},"end":{"line":11,"column":57}},"loc":{"start":{"line":11,"column":60},"end":{"line":17,"column":1}},"line":11},"2":{"name":"overwriteResult","decl":{"start":{"line":19,"column":34},"end":{"line":19,"column":49}},"loc":{"start":{"line":19,"column":52},"end":{"line":25,"column":1}},"line":19},"3":{"name":"execPre","decl":{"start":{"line":34,"column":42},"end":{"line":34,"column":49}},"loc":{"start":{"line":34,"column":71},"end":{"line":94,"column":1}},"line":34},"4":{"name":"(anonymous_4)","decl":{"start":{"line":49,"column":36},"end":{"line":49,"column":37}},"loc":{"start":{"line":49,"column":59},"end":{"line":52,"column":7}},"line":49},"5":{"name":"(anonymous_5)","decl":{"start":{"line":53,"column":20},"end":{"line":53,"column":21}},"loc":{"start":{"line":53,"column":29},"end":{"line":53,"column":58}},"line":53},"6":{"name":"(anonymous_6)","decl":{"start":{"line":103,"column":31},"end":{"line":103,"column":32}},"loc":{"start":{"line":103,"column":61},"end":{"line":110,"column":1}},"line":103},"7":{"name":"execPost","decl":{"start":{"line":120,"column":43},"end":{"line":120,"column":51}},"loc":{"start":{"line":120,"column":82},"end":{"line":225,"column":1}},"line":120},"8":{"name":"(anonymous_8)","decl":{"start":{"line":157,"column":34},"end":{"line":157,"column":35}},"loc":{"start":{"line":157,"column":57},"end":{"line":160,"column":5}},"line":157},"9":{"name":"nextCallback","decl":{"start":{"line":161,"column":26},"end":{"line":161,"column":38}},"loc":{"start":{"line":161,"column":44},"end":{"line":167,"column":5}},"line":161},"10":{"name":"(anonymous_10)","decl":{"start":{"line":234,"column":32},"end":{"line":234,"column":33}},"loc":{"start":{"line":234,"column":62},"end":{"line":246,"column":1}},"line":234},"11":{"name":"(anonymous_11)","decl":{"start":{"line":254,"column":37},"end":{"line":254,"column":38}},"loc":{"start":{"line":254,"column":56},"end":{"line":265,"column":1}},"line":254},"12":{"name":"syncWrapper","decl":{"start":{"line":256,"column":18},"end":{"line":256,"column":29}},"loc":{"start":{"line":256,"column":32},"end":{"line":264,"column":3}},"line":256},"13":{"name":"wrap","decl":{"start":{"line":276,"column":39},"end":{"line":276,"column":43}},"loc":{"start":{"line":276,"column":78},"end":{"line":297,"column":1}},"line":276},"14":{"name":"(anonymous_14)","decl":{"start":{"line":304,"column":26},"end":{"line":304,"column":27}},"loc":{"start":{"line":304,"column":39},"end":{"line":336,"column":1}},"line":304},"15":{"name":"(anonymous_15)","decl":{"start":{"line":310,"column":10},"end":{"line":310,"column":11}},"loc":{"start":{"line":310,"column":15},"end":{"line":310,"column":51}},"line":310},"16":{"name":"(anonymous_16)","decl":{"start":{"line":324,"column":10},"end":{"line":324,"column":11}},"loc":{"start":{"line":324,"column":15},"end":{"line":324,"column":51}},"line":324},"17":{"name":"(anonymous_17)","decl":{"start":{"line":343,"column":28},"end":{"line":343,"column":29}},"loc":{"start":{"line":343,"column":43},"end":{"line":345,"column":1}},"line":343},"18":{"name":"(anonymous_18)","decl":{"start":{"line":355,"column":33},"end":{"line":355,"column":34}},"loc":{"start":{"line":355,"column":70},"end":{"line":365,"column":1}},"line":355},"19":{"name":"kareemWrappedFunction","decl":{"start":{"line":361,"column":18},"end":{"line":361,"column":39}},"loc":{"start":{"line":361,"column":42},"end":{"line":364,"column":3}},"line":361},"20":{"name":"(anonymous_20)","decl":{"start":{"line":376,"column":23},"end":{"line":376,"column":24}},"loc":{"start":{"line":376,"column":67},"end":{"line":398,"column":1}},"line":376},"21":{"name":"(anonymous_21)","decl":{"start":{"line":409,"column":24},"end":{"line":409,"column":25}},"loc":{"start":{"line":409,"column":61},"end":{"line":429,"column":1}},"line":409},"22":{"name":"postError","decl":{"start":{"line":440,"column":38},"end":{"line":440,"column":47}},"loc":{"start":{"line":440,"column":76},"end":{"line":447,"column":1}},"line":440},"23":{"name":"(anonymous_23)","decl":{"start":{"line":453,"column":25},"end":{"line":453,"column":26}},"loc":{"start":{"line":453,"column":36},"end":{"line":465,"column":1}},"line":453},"24":{"name":"(anonymous_24)","decl":{"start":{"line":473,"column":25},"end":{"line":473,"column":26}},"loc":{"start":{"line":473,"column":48},"end":{"line":493,"column":1}},"line":473},"25":{"name":"(anonymous_25)","decl":{"start":{"line":481,"column":13},"end":{"line":481,"column":14}},"loc":{"start":{"line":481,"column":18},"end":{"line":481,"column":66}},"line":481},"26":{"name":"(anonymous_26)","decl":{"start":{"line":481,"column":33},"end":{"line":481,"column":34}},"loc":{"start":{"line":481,"column":39},"end":{"line":481,"column":44}},"line":481},"27":{"name":"(anonymous_27)","decl":{"start":{"line":488,"column":13},"end":{"line":488,"column":14}},"loc":{"start":{"line":488,"column":18},"end":{"line":488,"column":47}},"line":488},"28":{"name":"isPromiseLike","decl":{"start":{"line":495,"column":9},"end":{"line":495,"column":22}},"loc":{"start":{"line":495,"column":26},"end":{"line":497,"column":1}},"line":495},"29":{"name":"isErrorHandlingMiddleware","decl":{"start":{"line":499,"column":9},"end":{"line":499,"column":34}},"loc":{"start":{"line":499,"column":50},"end":{"line":504,"column":1}},"line":499}},"branchMap":{"0":{"loc":{"start":{"line":12,"column":2},"end":{"line":14,"column":3}},"type":"if","locations":[{"start":{"line":12,"column":2},"end":{"line":14,"column":3}},{"start":{"line":12,"column":2},"end":{"line":14,"column":3}}],"line":12},"1":{"loc":{"start":{"line":20,"column":2},"end":{"line":22,"column":3}},"type":"if","locations":[{"start":{"line":20,"column":2},"end":{"line":22,"column":3}},{"start":{"line":20,"column":2},"end":{"line":22,"column":3}}],"line":20},"2":{"loc":{"start":{"line":35,"column":15},"end":{"line":35,"column":41}},"type":"binary-expr","locations":[{"start":{"line":35,"column":15},"end":{"line":35,"column":35}},{"start":{"line":35,"column":39},"end":{"line":35,"column":41}}],"line":35},"3":{"loc":{"start":{"line":40,"column":2},"end":{"line":42,"column":3}},"type":"if","locations":[{"start":{"line":40,"column":2},"end":{"line":42,"column":3}},{"start":{"line":40,"column":2},"end":{"line":42,"column":3}}],"line":40},"4":{"loc":{"start":{"line":46,"column":4},"end":{"line":86,"column":5}},"type":"if","locations":[{"start":{"line":46,"column":4},"end":{"line":86,"column":5}},{"start":{"line":46,"column":4},"end":{"line":86,"column":5}}],"line":46},"5":{"loc":{"start":{"line":53,"column":29},"end":{"line":53,"column":58}},"type":"cond-expr","locations":[{"start":{"line":53,"column":35},"end":{"line":53,"column":46}},{"start":{"line":53,"column":49},"end":{"line":53,"column":58}}],"line":53},"6":{"loc":{"start":{"line":56,"column":8},"end":{"line":58,"column":9}},"type":"if","locations":[{"start":{"line":56,"column":8},"end":{"line":58,"column":9}},{"start":{"line":56,"column":8},"end":{"line":58,"column":9}}],"line":56},"7":{"loc":{"start":{"line":56,"column":12},"end":{"line":56,"column":68}},"type":"binary-expr","locations":[{"start":{"line":56,"column":12},"end":{"line":56,"column":34}},{"start":{"line":56,"column":38},"end":{"line":56,"column":68}}],"line":56},"8":{"loc":{"start":{"line":64,"column":8},"end":{"line":68,"column":9}},"type":"if","locations":[{"start":{"line":64,"column":8},"end":{"line":68,"column":9}},{"start":{"line":64,"column":8},"end":{"line":68,"column":9}}],"line":64},"9":{"loc":{"start":{"line":70,"column":8},"end":{"line":73,"column":9}},"type":"if","locations":[{"start":{"line":70,"column":8},"end":{"line":73,"column":9}},{"start":{"line":70,"column":8},"end":{"line":73,"column":9}}],"line":70},"10":{"loc":{"start":{"line":80,"column":8},"end":{"line":83,"column":9}},"type":"if","locations":[{"start":{"line":80,"column":8},"end":{"line":83,"column":9}},{"start":{"line":80,"column":8},"end":{"line":83,"column":9}}],"line":80},"11":{"loc":{"start":{"line":91,"column":2},"end":{"line":93,"column":3}},"type":"if","locations":[{"start":{"line":91,"column":2},"end":{"line":93,"column":3}},{"start":{"line":91,"column":2},"end":{"line":93,"column":3}}],"line":91},"12":{"loc":{"start":{"line":104,"column":15},"end":{"line":104,"column":41}},"type":"binary-expr","locations":[{"start":{"line":104,"column":15},"end":{"line":104,"column":35}},{"start":{"line":104,"column":39},"end":{"line":104,"column":41}}],"line":104},"13":{"loc":{"start":{"line":108,"column":30},"end":{"line":108,"column":40}},"type":"binary-expr","locations":[{"start":{"line":108,"column":30},"end":{"line":108,"column":34}},{"start":{"line":108,"column":38},"end":{"line":108,"column":40}}],"line":108},"14":{"loc":{"start":{"line":121,"column":16},"end":{"line":121,"column":43}},"type":"binary-expr","locations":[{"start":{"line":121,"column":16},"end":{"line":121,"column":37}},{"start":{"line":121,"column":41},"end":{"line":121,"column":43}}],"line":121},"15":{"loc":{"start":{"line":125,"column":2},"end":{"line":127,"column":3}},"type":"if","locations":[{"start":{"line":125,"column":2},"end":{"line":127,"column":3}},{"start":{"line":125,"column":2},"end":{"line":127,"column":3}}],"line":125},"16":{"loc":{"start":{"line":125,"column":6},"end":{"line":125,"column":30}},"type":"binary-expr","locations":[{"start":{"line":125,"column":6},"end":{"line":125,"column":13}},{"start":{"line":125,"column":17},"end":{"line":125,"column":30}}],"line":125},"17":{"loc":{"start":{"line":129,"column":2},"end":{"line":134,"column":3}},"type":"if","locations":[{"start":{"line":129,"column":2},"end":{"line":134,"column":3}},{"start":{"line":129,"column":2},"end":{"line":134,"column":3}}],"line":129},"18":{"loc":{"start":{"line":130,"column":4},"end":{"line":132,"column":5}},"type":"if","locations":[{"start":{"line":130,"column":4},"end":{"line":132,"column":5}},{"start":{"line":130,"column":4},"end":{"line":132,"column":5}}],"line":130},"19":{"loc":{"start":{"line":142,"column":6},"end":{"line":145,"column":7}},"type":"if","locations":[{"start":{"line":142,"column":6},"end":{"line":145,"column":7}},{"start":{"line":142,"column":6},"end":{"line":145,"column":7}}],"line":142},"20":{"loc":{"start":{"line":142,"column":10},"end":{"line":142,"column":44}},"type":"binary-expr","locations":[{"start":{"line":142,"column":10},"end":{"line":142,"column":18}},{"start":{"line":142,"column":22},"end":{"line":142,"column":44}}],"line":142},"21":{"loc":{"start":{"line":148,"column":4},"end":{"line":153,"column":5}},"type":"if","locations":[{"start":{"line":148,"column":4},"end":{"line":153,"column":5}},{"start":{"line":148,"column":4},"end":{"line":153,"column":5}}],"line":148},"22":{"loc":{"start":{"line":162,"column":6},"end":{"line":166,"column":7}},"type":"if","locations":[{"start":{"line":162,"column":6},"end":{"line":166,"column":7}},{"start":{"line":162,"column":6},"end":{"line":166,"column":7}}],"line":162},"23":{"loc":{"start":{"line":169,"column":4},"end":{"line":217,"column":5}},"type":"if","locations":[{"start":{"line":169,"column":4},"end":{"line":217,"column":5}},{"start":{"line":169,"column":4},"end":{"line":217,"column":5}}],"line":169},"24":{"loc":{"start":{"line":170,"column":6},"end":{"line":188,"column":7}},"type":"if","locations":[{"start":{"line":170,"column":6},"end":{"line":188,"column":7}},{"start":{"line":170,"column":6},"end":{"line":188,"column":7}}],"line":170},"25":{"loc":{"start":{"line":173,"column":10},"end":{"line":178,"column":11}},"type":"if","locations":[{"start":{"line":173,"column":10},"end":{"line":178,"column":11}},{"start":{"line":173,"column":10},"end":{"line":178,"column":11}}],"line":173},"26":{"loc":{"start":{"line":175,"column":17},"end":{"line":178,"column":11}},"type":"if","locations":[{"start":{"line":175,"column":17},"end":{"line":178,"column":11}},{"start":{"line":175,"column":17},"end":{"line":178,"column":11}}],"line":175},"27":{"loc":{"start":{"line":180,"column":10},"end":{"line":183,"column":11}},"type":"if","locations":[{"start":{"line":180,"column":10},"end":{"line":183,"column":11}},{"start":{"line":180,"column":10},"end":{"line":183,"column":11}}],"line":180},"28":{"loc":{"start":{"line":190,"column":6},"end":{"line":216,"column":7}},"type":"if","locations":[{"start":{"line":190,"column":6},"end":{"line":216,"column":7}},{"start":{"line":190,"column":6},"end":{"line":216,"column":7}}],"line":190},"29":{"loc":{"start":{"line":197,"column":10},"end":{"line":202,"column":11}},"type":"if","locations":[{"start":{"line":197,"column":10},"end":{"line":202,"column":11}},{"start":{"line":197,"column":10},"end":{"line":202,"column":11}}],"line":197},"30":{"loc":{"start":{"line":199,"column":17},"end":{"line":202,"column":11}},"type":"if","locations":[{"start":{"line":199,"column":17},"end":{"line":202,"column":11}},{"start":{"line":199,"column":17},"end":{"line":202,"column":11}}],"line":199},"31":{"loc":{"start":{"line":204,"column":10},"end":{"line":207,"column":11}},"type":"if","locations":[{"start":{"line":204,"column":10},"end":{"line":207,"column":11}},{"start":{"line":204,"column":10},"end":{"line":207,"column":11}}],"line":204},"32":{"loc":{"start":{"line":212,"column":8},"end":{"line":215,"column":9}},"type":"if","locations":[{"start":{"line":212,"column":8},"end":{"line":215,"column":9}},{"start":{"line":212,"column":8},"end":{"line":215,"column":9}}],"line":212},"33":{"loc":{"start":{"line":220,"column":2},"end":{"line":222,"column":3}},"type":"if","locations":[{"start":{"line":220,"column":2},"end":{"line":222,"column":3}},{"start":{"line":220,"column":2},"end":{"line":222,"column":3}}],"line":220},"34":{"loc":{"start":{"line":235,"column":16},"end":{"line":235,"column":43}},"type":"binary-expr","locations":[{"start":{"line":235,"column":16},"end":{"line":235,"column":37}},{"start":{"line":235,"column":41},"end":{"line":235,"column":43}}],"line":235},"35":{"loc":{"start":{"line":239,"column":43},"end":{"line":239,"column":53}},"type":"binary-expr","locations":[{"start":{"line":239,"column":43},"end":{"line":239,"column":47}},{"start":{"line":239,"column":51},"end":{"line":239,"column":53}}],"line":239},"36":{"loc":{"start":{"line":240,"column":4},"end":{"line":242,"column":5}},"type":"if","locations":[{"start":{"line":240,"column":4},"end":{"line":242,"column":5}},{"start":{"line":240,"column":4},"end":{"line":242,"column":5}}],"line":240},"37":{"loc":{"start":{"line":282,"column":4},"end":{"line":287,"column":5}},"type":"if","locations":[{"start":{"line":282,"column":4},"end":{"line":287,"column":5}},{"start":{"line":282,"column":4},"end":{"line":287,"column":5}}],"line":282},"38":{"loc":{"start":{"line":290,"column":2},"end":{"line":292,"column":3}},"type":"if","locations":[{"start":{"line":290,"column":2},"end":{"line":292,"column":3}},{"start":{"line":290,"column":2},"end":{"line":292,"column":3}}],"line":290},"39":{"loc":{"start":{"line":313,"column":4},"end":{"line":316,"column":5}},"type":"if","locations":[{"start":{"line":313,"column":4},"end":{"line":316,"column":5}},{"start":{"line":313,"column":4},"end":{"line":316,"column":5}}],"line":313},"40":{"loc":{"start":{"line":327,"column":4},"end":{"line":330,"column":5}},"type":"if","locations":[{"start":{"line":327,"column":4},"end":{"line":330,"column":5}},{"start":{"line":327,"column":4},"end":{"line":330,"column":5}}],"line":327},"41":{"loc":{"start":{"line":344,"column":9},"end":{"line":344,"column":54}},"type":"binary-expr","locations":[{"start":{"line":344,"column":9},"end":{"line":344,"column":29}},{"start":{"line":344,"column":33},"end":{"line":344,"column":54}}],"line":344},"42":{"loc":{"start":{"line":357,"column":2},"end":{"line":360,"column":3}},"type":"if","locations":[{"start":{"line":357,"column":2},"end":{"line":360,"column":3}},{"start":{"line":357,"column":2},"end":{"line":360,"column":3}}],"line":357},"43":{"loc":{"start":{"line":362,"column":21},"end":{"line":362,"column":36}},"type":"binary-expr","locations":[{"start":{"line":362,"column":21},"end":{"line":362,"column":28}},{"start":{"line":362,"column":32},"end":{"line":362,"column":36}}],"line":362},"44":{"loc":{"start":{"line":377,"column":2},"end":{"line":382,"column":3}},"type":"if","locations":[{"start":{"line":377,"column":2},"end":{"line":382,"column":3}},{"start":{"line":377,"column":2},"end":{"line":382,"column":3}}],"line":377},"45":{"loc":{"start":{"line":380,"column":9},"end":{"line":382,"column":3}},"type":"if","locations":[{"start":{"line":380,"column":9},"end":{"line":382,"column":3}},{"start":{"line":380,"column":9},"end":{"line":382,"column":3}}],"line":380},"46":{"loc":{"start":{"line":384,"column":15},"end":{"line":384,"column":41}},"type":"binary-expr","locations":[{"start":{"line":384,"column":15},"end":{"line":384,"column":35}},{"start":{"line":384,"column":39},"end":{"line":384,"column":41}}],"line":384},"47":{"loc":{"start":{"line":387,"column":2},"end":{"line":389,"column":3}},"type":"if","locations":[{"start":{"line":387,"column":2},"end":{"line":389,"column":3}},{"start":{"line":387,"column":2},"end":{"line":389,"column":3}}],"line":387},"48":{"loc":{"start":{"line":391,"column":2},"end":{"line":395,"column":3}},"type":"if","locations":[{"start":{"line":391,"column":2},"end":{"line":395,"column":3}},{"start":{"line":391,"column":2},"end":{"line":395,"column":3}}],"line":391},"49":{"loc":{"start":{"line":410,"column":16},"end":{"line":410,"column":43}},"type":"binary-expr","locations":[{"start":{"line":410,"column":16},"end":{"line":410,"column":37}},{"start":{"line":410,"column":41},"end":{"line":410,"column":43}}],"line":410},"50":{"loc":{"start":{"line":412,"column":2},"end":{"line":416,"column":3}},"type":"if","locations":[{"start":{"line":412,"column":2},"end":{"line":416,"column":3}},{"start":{"line":412,"column":2},"end":{"line":416,"column":3}}],"line":412},"51":{"loc":{"start":{"line":418,"column":2},"end":{"line":420,"column":3}},"type":"if","locations":[{"start":{"line":418,"column":2},"end":{"line":420,"column":3}},{"start":{"line":418,"column":2},"end":{"line":420,"column":3}}],"line":418},"52":{"loc":{"start":{"line":422,"column":2},"end":{"line":426,"column":3}},"type":"if","locations":[{"start":{"line":422,"column":2},"end":{"line":426,"column":3}},{"start":{"line":422,"column":2},"end":{"line":426,"column":3}}],"line":422},"53":{"loc":{"start":{"line":441,"column":2},"end":{"line":445,"column":3}},"type":"if","locations":[{"start":{"line":441,"column":2},"end":{"line":445,"column":3}},{"start":{"line":441,"column":2},"end":{"line":445,"column":3}}],"line":441},"54":{"loc":{"start":{"line":474,"column":10},"end":{"line":474,"column":47}},"type":"cond-expr","locations":[{"start":{"line":474,"column":35},"end":{"line":474,"column":39}},{"start":{"line":474,"column":42},"end":{"line":474,"column":47}}],"line":474},"55":{"loc":{"start":{"line":475,"column":14},"end":{"line":475,"column":41}},"type":"cond-expr","locations":[{"start":{"line":475,"column":22},"end":{"line":475,"column":34}},{"start":{"line":475,"column":37},"end":{"line":475,"column":41}}],"line":475},"56":{"loc":{"start":{"line":478,"column":23},"end":{"line":478,"column":47}},"type":"binary-expr","locations":[{"start":{"line":478,"column":23},"end":{"line":478,"column":41}},{"start":{"line":478,"column":45},"end":{"line":478,"column":47}}],"line":478},"57":{"loc":{"start":{"line":486,"column":24},"end":{"line":486,"column":49}},"type":"binary-expr","locations":[{"start":{"line":486,"column":24},"end":{"line":486,"column":43}},{"start":{"line":486,"column":47},"end":{"line":486,"column":49}}],"line":486},"58":{"loc":{"start":{"line":496,"column":10},"end":{"line":496,"column":77}},"type":"binary-expr","locations":[{"start":{"line":496,"column":10},"end":{"line":496,"column":31}},{"start":{"line":496,"column":35},"end":{"line":496,"column":45}},{"start":{"line":496,"column":49},"end":{"line":496,"column":77}}],"line":496},"59":{"loc":{"start":{"line":500,"column":2},"end":{"line":502,"column":3}},"type":"if","locations":[{"start":{"line":500,"column":2},"end":{"line":502,"column":3}},{"start":{"line":500,"column":2},"end":{"line":502,"column":3}}],"line":500}},"s":{"0":62,"1":62,"2":1,"3":6,"4":3,"5":3,"6":1,"7":12,"8":6,"9":6,"10":1,"11":27,"12":27,"13":27,"14":27,"15":27,"16":3,"17":24,"18":24,"19":33,"20":27,"21":27,"22":27,"23":27,"24":27,"25":27,"26":27,"27":27,"28":27,"29":27,"30":19,"31":0,"32":19,"33":27,"34":27,"35":27,"36":0,"37":27,"38":9,"39":3,"40":3,"41":6,"42":6,"43":6,"44":2,"45":0,"46":0,"47":2,"48":16,"49":16,"50":3,"51":1,"52":4,"53":4,"54":4,"55":4,"56":4,"57":1,"58":27,"59":27,"60":27,"61":27,"62":6,"63":27,"64":1,"65":0,"66":1,"67":26,"68":41,"69":41,"70":41,"71":41,"72":41,"73":41,"74":40,"75":40,"76":40,"77":41,"78":1,"79":1,"80":1,"81":1,"82":41,"83":41,"84":41,"85":41,"86":24,"87":12,"88":12,"89":41,"90":14,"91":8,"92":8,"93":8,"94":1,"95":7,"96":6,"97":5,"98":0,"99":0,"100":5,"101":6,"102":27,"103":1,"104":26,"105":26,"106":26,"107":26,"108":3,"109":23,"110":18,"111":7,"112":2,"113":2,"114":5,"115":5,"116":19,"117":2,"118":2,"119":26,"120":11,"121":15,"122":1,"123":5,"124":5,"125":5,"126":5,"127":6,"128":6,"129":2,"130":5,"131":1,"132":2,"133":2,"134":2,"135":2,"136":2,"137":2,"138":1,"139":17,"140":17,"141":17,"142":7,"143":2,"144":2,"145":5,"146":12,"147":10,"148":9,"149":7,"150":1,"151":2,"152":2,"153":2,"154":4,"155":6,"156":4,"157":1,"158":1,"159":3,"160":2,"161":2,"162":4,"163":6,"164":4,"165":1,"166":1,"167":3,"168":2,"169":1,"170":2,"171":1,"172":1,"173":1,"174":0,"175":1,"176":1,"177":1,"178":1,"179":51,"180":43,"181":43,"182":8,"183":1,"184":51,"185":51,"186":51,"187":1,"188":50,"189":2,"190":48,"191":50,"192":1,"193":60,"194":60,"195":52,"196":52,"197":52,"198":60,"199":1,"200":59,"201":2,"202":57,"203":59,"204":59,"205":1,"206":1,"207":1,"208":1,"209":1,"210":1,"211":1,"212":4,"213":4,"214":6,"215":6,"216":4,"217":5,"218":4,"219":1,"220":1,"221":1,"222":1,"223":1,"224":1,"225":1,"226":1,"227":1,"228":1,"229":1,"230":1,"231":1,"232":1,"233":1,"234":1,"235":61,"236":41,"237":2,"238":39,"239":1},"f":{"0":62,"1":6,"2":12,"3":27,"4":27,"5":27,"6":4,"7":27,"8":41,"9":24,"10":5,"11":2,"12":2,"13":17,"14":2,"15":6,"16":6,"17":2,"18":1,"19":1,"20":51,"21":60,"22":1,"23":4,"24":1,"25":1,"26":1,"27":1,"28":61,"29":41},"b":{"0":[3,3],"1":[6,6],"2":[27,3],"3":[3,24],"4":[27,6],"5":[9,18],"6":[0,19],"7":[19,19],"8":[0,27],"9":[3,6],"10":[0,2],"11":[3,13],"12":[4,1],"13":[4,2],"14":[27,1],"15":[6,21],"16":[27,10],"17":[1,26],"18":[0,1],"19":[40,0],"20":[40,37],"21":[1,40],"22":[12,12],"23":[14,27],"24":[8,6],"25":[1,7],"26":[6,1],"27":[0,5],"28":[1,26],"29":[3,23],"30":[18,5],"31":[2,5],"32":[2,17],"33":[11,15],"34":[5,1],"35":[6,2],"36":[2,4],"37":[2,5],"38":[10,2],"39":[1,3],"40":[1,3],"41":[2,1],"42":[0,1],"43":[1,0],"44":[43,8],"45":[1,7],"46":[51,35],"47":[1,50],"48":[2,48],"49":[60,40],"50":[52,8],"51":[1,59],"52":[2,57],"53":[1,0],"54":[1,0],"55":[1,0],"56":[1,0],"57":[1,1],"58":[61,5,5],"59":[2,39]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"9aa8c8a5aa5ed39ed05b444adf39a3f6ce508b9f","contentHash":"919c95bfd55a08217a62792188dd82a1cc98a911fe4121331b010d95c0f36506"}} \ No newline at end of file diff --git a/backend/node_modules/kareem/.nyc_output/processinfo/fa57d6c4-9e78-4624-9229-f77b87a07481.json b/backend/node_modules/kareem/.nyc_output/processinfo/fa57d6c4-9e78-4624-9229-f77b87a07481.json new file mode 100644 index 0000000..c8858dc --- /dev/null +++ b/backend/node_modules/kareem/.nyc_output/processinfo/fa57d6c4-9e78-4624-9229-f77b87a07481.json @@ -0,0 +1 @@ +{"parent":null,"pid":2678345,"argv":["/home/v/.nvm/versions/node/v20.18.1/bin/node","/home/v/Desktop/MongoDB/kareem/node_modules/.bin/mocha","./test/examples.test.js","./test/misc.test.js","./test/post.test.js","./test/pre.test.js","./test/wrap.test.js"],"execArgv":[],"cwd":"/home/v/Desktop/MongoDB/kareem","time":1756153005026,"ppid":2678334,"coverageFilename":"/home/v/Desktop/MongoDB/kareem/.nyc_output/fa57d6c4-9e78-4624-9229-f77b87a07481.json","externalId":"","uuid":"fa57d6c4-9e78-4624-9229-f77b87a07481","files":["/home/v/Desktop/MongoDB/kareem/index.js"]} \ No newline at end of file diff --git a/backend/node_modules/kareem/.nyc_output/processinfo/index.json b/backend/node_modules/kareem/.nyc_output/processinfo/index.json new file mode 100644 index 0000000..9f2f2cd --- /dev/null +++ b/backend/node_modules/kareem/.nyc_output/processinfo/index.json @@ -0,0 +1 @@ +{"processes":{"fa57d6c4-9e78-4624-9229-f77b87a07481":{"parent":null,"children":[]}},"files":{"/home/v/Desktop/MongoDB/kareem/index.js":["fa57d6c4-9e78-4624-9229-f77b87a07481"]},"externalIds":{}} \ No newline at end of file diff --git a/backend/node_modules/kareem/CHANGELOG.md b/backend/node_modules/kareem/CHANGELOG.md new file mode 100644 index 0000000..2b4d033 --- /dev/null +++ b/backend/node_modules/kareem/CHANGELOG.md @@ -0,0 +1,840 @@ +# Changelog + + +## 3.2.0 (2026-01-29) + +* feat(exec): add filter option to execPreSync and execPostSync #44 + + +## 3.1.0 (2026-01-12) + +* feat(exec): add filter option to allow executing hooks based on a filter function #43 + + +## 3.0.0 (2025-11-18) + +* BREAKING CHANGE: make execPre async and drop callback support #39 +* BREAKING CHANGE: require Node 18 +* feat: overwriteArguments support #42 + + +## 2.6.0 (2024-03-04) + +* feat: add TypeScript types + + +## 2.5.1 (2023-01-06) + +* fix: avoid passing final callback to pre hook, because calling the callback can mess up hook execution #36 Automattic/mongoose#12836 + + +## 2.5.0 (2022-12-01) + +* feat: add errorHandler option to `post()` #34 + + +## 2.4.0 (2022-06-13) + +* feat: add `overwriteResult()` and `skipWrappedFunction()` for more advanced control flow + + +## 2.3.4 (2022-02-10) + +* perf: various performance improvements #27 #24 #23 #22 #21 #20 + + +## 2.3.3 (2021-12-26) + +* fix: handle sync errors in `wrap()` + + +## 2.3.2 (2020-12-08) + +* fix: handle sync errors in pre hooks if there are multiple hooks + + +## 2.3.0 (2018-09-24) + +* chore(release): 2.2.3 ([c8f2695](https://github.com/vkarpov15/kareem/commit/c8f2695)) +* chore(release): 2.2.4 ([a377a4f](https://github.com/vkarpov15/kareem/commit/a377a4f)) +* chore(release): 2.2.5 ([5a495e3](https://github.com/vkarpov15/kareem/commit/5a495e3)) +* fix(filter): copy async pres correctly with `filter()` ([1b1ed8a](https://github.com/vkarpov15/kareem/commit/1b1ed8a)), closes [Automattic/mongoose#3054](https://github.com/Automattic/mongoose/issues/3054) +* feat: add filter() function ([1f641f4](https://github.com/vkarpov15/kareem/commit/1f641f4)) +* feat: support storing options on pre and post hooks ([59220b9](https://github.com/vkarpov15/kareem/commit/59220b9)) + + + + +## 2.2.3 (2018-09-10) + +* chore: release 2.2.3 ([af653a3](https://github.com/vkarpov15/kareem/commit/af653a3)) + + + + +## 2.2.2 (2018-09-10) + +* chore: release 2.2.2 ([3f0144d](https://github.com/vkarpov15/kareem/commit/3f0144d)) +* fix: allow merge() to not clone ([e628d65](https://github.com/vkarpov15/kareem/commit/e628d65)) + + + + +## 2.2.1 (2018-06-05) + +* chore: release 2.2.1 ([4625a64](https://github.com/vkarpov15/kareem/commit/4625a64)) +* chore: remove lockfile from git ([7f3e4e6](https://github.com/vkarpov15/kareem/commit/7f3e4e6)) +* fix: handle numAsync correctly when merging ([fef8e7e](https://github.com/vkarpov15/kareem/commit/fef8e7e)) +* test: repro issue with not copying numAsync ([952d9db](https://github.com/vkarpov15/kareem/commit/952d9db)) + + + + +## 2.2.0 (2018-06-05) + +* chore: release 2.2.0 ([ff9ad03](https://github.com/vkarpov15/kareem/commit/ff9ad03)) +* fix: use maps instead of objects for _pres and _posts so `toString()` doesn't get reported as having ([55df303](https://github.com/vkarpov15/kareem/commit/55df303)), closes [Automattic/mongoose#6538](https://github.com/Automattic/mongoose/issues/6538) + + + + +## 2.1.0 (2018-05-16) + +* chore: release 2.1.0 ([ba5f1bc](https://github.com/vkarpov15/kareem/commit/ba5f1bc)) +* feat: add option to check wrapped function return value for promises ([c9d7dd1](https://github.com/vkarpov15/kareem/commit/c9d7dd1)) +* refactor: use const in wrap() ([0fc21f9](https://github.com/vkarpov15/kareem/commit/0fc21f9)) + + + + +## 2.0.7 (2018-04-28) + +* chore: release 2.0.7 ([0bf91e6](https://github.com/vkarpov15/kareem/commit/0bf91e6)) +* feat: add `hasHooks()` ([225f18d](https://github.com/vkarpov15/kareem/commit/225f18d)), closes [Automattic/mongoose#6385](https://github.com/Automattic/mongoose/issues/6385) + + + + +## 2.0.6 (2018-03-22) + +* chore: release 2.0.6 ([f3d406b](https://github.com/vkarpov15/kareem/commit/f3d406b)) +* fix(wrap): ensure fast path still wraps function in `nextTick()` for chaining ([7000494](https://github.com/vkarpov15/kareem/commit/7000494)), closes [Automattic/mongoose#6250](https://github.com/Automattic/mongoose/issues/6250) [dsanel/mongoose-delete#36](https://github.com/dsanel/mongoose-delete/issues/36) + + + + +## 2.0.5 (2018-02-22) + +* chore: release 2.0.5 ([3286612](https://github.com/vkarpov15/kareem/commit/3286612)) +* perf(createWrapper): don't create wrapper if there are no hooks ([5afc5b9](https://github.com/vkarpov15/kareem/commit/5afc5b9)), closes [Automattic/mongoose#6126](https://github.com/Automattic/mongoose/issues/6126) + + + + +## 2.0.4 (2018-02-08) + +* chore: release 2.0.4 ([2ab0293](https://github.com/vkarpov15/kareem/commit/2ab0293)) + + + + +## 2.0.3 (2018-02-01) + +* chore: release 2.0.3 ([3c1abe5](https://github.com/vkarpov15/kareem/commit/3c1abe5)) +* fix: use process.nextTick() re: Automattic/mongoose#6074 ([e5bfe33](https://github.com/vkarpov15/kareem/commit/e5bfe33)), closes [Automattic/mongoose#6074](https://github.com/Automattic/mongoose/issues/6074) + + + + +## 2.0.2 (2018-01-24) + +* chore: fix license ([a9d755c](https://github.com/vkarpov15/kareem/commit/a9d755c)), closes [#10](https://github.com/vkarpov15/kareem/issues/10) +* chore: release 2.0.2 ([fe87ab6](https://github.com/vkarpov15/kareem/commit/fe87ab6)) + + + + +## 2.0.1 (2018-01-09) + +* chore: release 2.0.1 with lockfile bump ([09c44fb](https://github.com/vkarpov15/kareem/commit/09c44fb)) + + + + +## 2.0.0 (2018-01-09) + +* chore: bump marked re: security ([cc564a9](https://github.com/vkarpov15/kareem/commit/cc564a9)) +* chore: release 2.0.0 ([f511d1c](https://github.com/vkarpov15/kareem/commit/f511d1c)) + + + + +## 2.0.0-rc5 (2017-12-23) + +* chore: fix build on node 4+5 ([6dac5a4](https://github.com/vkarpov15/kareem/commit/6dac5a4)) +* chore: fix built on node 4 + 5 again ([434ef0a](https://github.com/vkarpov15/kareem/commit/434ef0a)) +* chore: release 2.0.0-rc5 ([25a32ee](https://github.com/vkarpov15/kareem/commit/25a32ee)) + + + + +## 2.0.0-rc4 (2017-12-22) + +* chore: release 2.0.0-rc4 ([49fc083](https://github.com/vkarpov15/kareem/commit/49fc083)) +* BREAKING CHANGE: deduplicate when merging hooks re: Automattic/mongoose#2945 ([d458573](https://github.com/vkarpov15/kareem/commit/d458573)), closes [Automattic/mongoose#2945](https://github.com/Automattic/mongoose/issues/2945) + + + + +## 2.0.0-rc3 (2017-12-22) + +* chore: release 2.0.0-rc3 ([adaaa00](https://github.com/vkarpov15/kareem/commit/adaaa00)) +* feat: support returning promises from middleware functions ([05b4480](https://github.com/vkarpov15/kareem/commit/05b4480)), closes [Automattic/mongoose#3779](https://github.com/Automattic/mongoose/issues/3779) + + + + +## 2.0.0-rc2 (2017-12-21) + +* chore: release 2.0.0-rc2 ([76325fa](https://github.com/vkarpov15/kareem/commit/76325fa)) +* fix: ensure next() and done() run in next tick ([6c20684](https://github.com/vkarpov15/kareem/commit/6c20684)) + + + + +## 2.0.0-rc1 (2017-12-21) + +* chore: improve test coverage re: Automattic/mongoose#3232 ([7b45cf0](https://github.com/vkarpov15/kareem/commit/7b45cf0)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232) +* chore: release 2.0.0-rc1 ([9b83f52](https://github.com/vkarpov15/kareem/commit/9b83f52)) +* BREAKING CHANGE: report sync exceptions as errors, only allow calling next() and done() once ([674adcc](https://github.com/vkarpov15/kareem/commit/674adcc)), closes [Automattic/mongoose#3483](https://github.com/Automattic/mongoose/issues/3483) + + + + +## 2.0.0-rc0 (2017-12-17) + +* chore: release 2.0.0-rc0 ([16b44b5](https://github.com/vkarpov15/kareem/commit/16b44b5)) +* BREAKING CHANGE: drop support for node < 4 ([9cbb8c7](https://github.com/vkarpov15/kareem/commit/9cbb8c7)) +* BREAKING CHANGE: remove useLegacyPost and add several new features ([6dd8531](https://github.com/vkarpov15/kareem/commit/6dd8531)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232) + + + + +## 1.5.0 (2017-07-20) + +* chore: release 1.5.0 ([9c491a0](https://github.com/vkarpov15/kareem/commit/9c491a0)) +* fix: improve post error handlers results ([9928dd5](https://github.com/vkarpov15/kareem/commit/9928dd5)), closes [Automattic/mongoose#5466](https://github.com/Automattic/mongoose/issues/5466) + + + + +## 1.4.2 (2017-07-06) + +* chore: release 1.4.2 ([8d14ac5](https://github.com/vkarpov15/kareem/commit/8d14ac5)) +* fix: correct args re: Automattic/mongoose#5405 ([3f28ae6](https://github.com/vkarpov15/kareem/commit/3f28ae6)), closes [Automattic/mongoose#5405](https://github.com/Automattic/mongoose/issues/5405) + + + + +## 1.4.1 (2017-04-25) + +* chore: release 1.4.1 ([5ecf0c2](https://github.com/vkarpov15/kareem/commit/5ecf0c2)) +* fix: handle numAsyncPres with clone() ([c72e857](https://github.com/vkarpov15/kareem/commit/c72e857)), closes [#8](https://github.com/vkarpov15/kareem/issues/8) +* test: repro #8 ([9b4d6b2](https://github.com/vkarpov15/kareem/commit/9b4d6b2)), closes [#8](https://github.com/vkarpov15/kareem/issues/8) + + + + +## 1.4.0 (2017-04-19) + +* chore: release 1.4.0 ([101c5f5](https://github.com/vkarpov15/kareem/commit/101c5f5)) +* feat: add merge() function ([285325e](https://github.com/vkarpov15/kareem/commit/285325e)) + + + + +## 1.3.0 (2017-03-26) + +* chore: release 1.3.0 ([f3a9e50](https://github.com/vkarpov15/kareem/commit/f3a9e50)) +* feat: pass function args to execPre ([4dd466d](https://github.com/vkarpov15/kareem/commit/4dd466d)) + + + + +## 1.2.1 (2017-02-03) + +* chore: release 1.2.1 ([d97081f](https://github.com/vkarpov15/kareem/commit/d97081f)) +* fix: filter out _kareemIgnored args for error handlers re: Automattic/mongoose#4925 ([ddc7aeb](https://github.com/vkarpov15/kareem/commit/ddc7aeb)), closes [Automattic/mongoose#4925](https://github.com/Automattic/mongoose/issues/4925) +* fix: make error handlers handle errors in pre hooks ([af38033](https://github.com/vkarpov15/kareem/commit/af38033)), closes [Automattic/mongoose#4927](https://github.com/Automattic/mongoose/issues/4927) + + + + +## 1.2.0 (2017-01-02) + +* chore: release 1.2.0 ([033225c](https://github.com/vkarpov15/kareem/commit/033225c)) +* chore: upgrade deps ([f9e9a09](https://github.com/vkarpov15/kareem/commit/f9e9a09)) +* feat: add _kareemIgnore re: Automattic/mongoose#4836 ([7957771](https://github.com/vkarpov15/kareem/commit/7957771)), closes [Automattic/mongoose#4836](https://github.com/Automattic/mongoose/issues/4836) + + + + +## 1.1.5 (2016-12-13) + +* chore: release 1.1.5 ([1a9f684](https://github.com/vkarpov15/kareem/commit/1a9f684)) +* fix: correct field name ([04a0e9d](https://github.com/vkarpov15/kareem/commit/04a0e9d)) + + + + +## 1.1.4 (2016-12-09) + +* chore: release 1.1.4 ([ece401c](https://github.com/vkarpov15/kareem/commit/ece401c)) +* chore: run tests on node 6 ([e0cb1cb](https://github.com/vkarpov15/kareem/commit/e0cb1cb)) +* fix: only copy own properties in clone() ([dfe28ce](https://github.com/vkarpov15/kareem/commit/dfe28ce)), closes [#7](https://github.com/vkarpov15/kareem/issues/7) + + + + +## 1.1.3 (2016-06-27) + +* chore: release 1.1.3 ([87171c8](https://github.com/vkarpov15/kareem/commit/87171c8)) +* fix: couple more issues with arg processing ([c65f523](https://github.com/vkarpov15/kareem/commit/c65f523)) + + + + +## 1.1.2 (2016-06-27) + +* chore: release 1.1.2 ([8e102b6](https://github.com/vkarpov15/kareem/commit/8e102b6)) +* fix: add early return ([4feda4e](https://github.com/vkarpov15/kareem/commit/4feda4e)) + + + + +## 1.1.1 (2016-06-27) + +* chore: release 1.1.1 ([8bb3050](https://github.com/vkarpov15/kareem/commit/8bb3050)) +* fix: skip error handlers if no error ([0eb3a44](https://github.com/vkarpov15/kareem/commit/0eb3a44)) + + + + +## 1.1.0 (2016-05-11) + +* chore: release 1.1.0 ([85332d9](https://github.com/vkarpov15/kareem/commit/85332d9)) +* chore: test on node 4 and node 5 ([1faefa1](https://github.com/vkarpov15/kareem/commit/1faefa1)) +* 100% coverage again ([c9aee4e](https://github.com/vkarpov15/kareem/commit/c9aee4e)) +* add support for error post hooks ([d378113](https://github.com/vkarpov15/kareem/commit/d378113)) +* basic setup for sync hooks #4 ([55aa081](https://github.com/vkarpov15/kareem/commit/55aa081)), closes [#4](https://github.com/vkarpov15/kareem/issues/4) +* proof of concept for error handlers ([e4a07d9](https://github.com/vkarpov15/kareem/commit/e4a07d9)) +* refactor out handleWrapError helper ([b19af38](https://github.com/vkarpov15/kareem/commit/b19af38)) + + + + +## 1.0.1 (2015-05-10) + +* Fix #1 ([de60dc6](https://github.com/vkarpov15/kareem/commit/de60dc6)), closes [#1](https://github.com/vkarpov15/kareem/issues/1) +* release 1.0.1 ([6971088](https://github.com/vkarpov15/kareem/commit/6971088)) +* Run tests on iojs in travis ([adcd201](https://github.com/vkarpov15/kareem/commit/adcd201)) +* support legacy post hook behavior in wrap() ([23fa74c](https://github.com/vkarpov15/kareem/commit/23fa74c)) +* Use node 0.12 in travis ([834689d](https://github.com/vkarpov15/kareem/commit/834689d)) + + + + +## 1.0.0 (2015-01-28) + +* Tag 1.0.0 ([4c5a35a](https://github.com/vkarpov15/kareem/commit/4c5a35a)) + + + + +## 0.0.8 (2015-01-27) + +* Add clone function ([688bba7](https://github.com/vkarpov15/kareem/commit/688bba7)) +* Add jscs for style checking ([5c93149](https://github.com/vkarpov15/kareem/commit/5c93149)) +* Bump 0.0.8 ([03c0d2f](https://github.com/vkarpov15/kareem/commit/03c0d2f)) +* Fix jscs config, add gulp rules ([9989abf](https://github.com/vkarpov15/kareem/commit/9989abf)) +* fix Makefile typo ([1f7e61a](https://github.com/vkarpov15/kareem/commit/1f7e61a)) + + + + +## 0.0.7 (2015-01-04) + +* Bump 0.0.7 ([98ef173](https://github.com/vkarpov15/kareem/commit/98ef173)) +* fix LearnBoost/mongoose#2553 - use null instead of undefined for err ([9157b48](https://github.com/vkarpov15/kareem/commit/9157b48)), closes [LearnBoost/mongoose#2553](https://github.com/LearnBoost/mongoose/issues/2553) +* Regenerate docs ([2331cdf](https://github.com/vkarpov15/kareem/commit/2331cdf)) + + + + +## 0.0.6 (2015-01-01) + +* Update docs and bump 0.0.6 ([92c12a7](https://github.com/vkarpov15/kareem/commit/92c12a7)) + + + + +## 0.0.5 (2015-01-01) + +* Add coverage rule to Makefile ([825a91c](https://github.com/vkarpov15/kareem/commit/825a91c)) +* Add coveralls to README ([fb52369](https://github.com/vkarpov15/kareem/commit/fb52369)) +* Add coveralls to travis ([93f6f15](https://github.com/vkarpov15/kareem/commit/93f6f15)) +* Add createWrapper() function ([ea77741](https://github.com/vkarpov15/kareem/commit/ea77741)) +* Add istanbul code coverage ([6eceeef](https://github.com/vkarpov15/kareem/commit/6eceeef)) +* Add some more comments for examples ([c5b0c6f](https://github.com/vkarpov15/kareem/commit/c5b0c6f)) +* Add travis ([e6dcb06](https://github.com/vkarpov15/kareem/commit/e6dcb06)) +* Add travis badge to docs ([ad8c9b3](https://github.com/vkarpov15/kareem/commit/ad8c9b3)) +* Add wrap() tests, 100% coverage ([6945be4](https://github.com/vkarpov15/kareem/commit/6945be4)) +* Better test coverage for execPost ([d9ad539](https://github.com/vkarpov15/kareem/commit/d9ad539)) +* Bump 0.0.5 ([69875b1](https://github.com/vkarpov15/kareem/commit/69875b1)) +* Docs fix ([15b7098](https://github.com/vkarpov15/kareem/commit/15b7098)) +* Fix silly mistake in docs generation ([50373eb](https://github.com/vkarpov15/kareem/commit/50373eb)) +* Fix typo in readme ([fec4925](https://github.com/vkarpov15/kareem/commit/fec4925)) +* Linkify travis badge ([92b25fe](https://github.com/vkarpov15/kareem/commit/92b25fe)) +* Make travis run coverage ([747157b](https://github.com/vkarpov15/kareem/commit/747157b)) +* Move travis status badge ([d52e89b](https://github.com/vkarpov15/kareem/commit/d52e89b)) +* Quick fix for coverage ([50bbddb](https://github.com/vkarpov15/kareem/commit/50bbddb)) +* Typo fix ([adea794](https://github.com/vkarpov15/kareem/commit/adea794)) + + + + +## 0.0.4 (2014-12-13) + +* Bump 0.0.4, run docs generation ([51a15fe](https://github.com/vkarpov15/kareem/commit/51a15fe)) +* Use correct post parameters in wrap() ([9bb5da3](https://github.com/vkarpov15/kareem/commit/9bb5da3)) + + + + +## 0.0.3 (2014-12-12) + +* Add npm test script, fix small bug with args not getting passed through post ([49e3e68](https://github.com/vkarpov15/kareem/commit/49e3e68)) +* Bump 0.0.3 ([65621d8](https://github.com/vkarpov15/kareem/commit/65621d8)) +* Update readme ([901388b](https://github.com/vkarpov15/kareem/commit/901388b)) + + + + +## 0.0.2 (2014-12-12) + +* Add github repo and bump 0.0.2 ([59db8be](https://github.com/vkarpov15/kareem/commit/59db8be)) + + + + +## 0.0.1 (2014-12-12) + +* Add basic docs ([ad29ea4](https://github.com/vkarpov15/kareem/commit/ad29ea4)) +* Add pre hooks ([2ffc356](https://github.com/vkarpov15/kareem/commit/2ffc356)) +* Add wrap function ([68c540c](https://github.com/vkarpov15/kareem/commit/68c540c)) +* Bump to version 0.0.1 ([a4bfd68](https://github.com/vkarpov15/kareem/commit/a4bfd68)) +* Initial commit ([4002458](https://github.com/vkarpov15/kareem/commit/4002458)) +* Initial deposit ([98fc489](https://github.com/vkarpov15/kareem/commit/98fc489)) +* Post hooks ([395b67c](https://github.com/vkarpov15/kareem/commit/395b67c)) +* Some basic setup work ([82df75e](https://github.com/vkarpov15/kareem/commit/82df75e)) +* Support sync pre hooks ([1cc1b9f](https://github.com/vkarpov15/kareem/commit/1cc1b9f)) +* Update package.json description ([978da18](https://github.com/vkarpov15/kareem/commit/978da18)) + + + + +## 2.2.5 (2018-09-24) + + + + + +## 2.2.4 (2018-09-24) + + + + + +## 2.2.3 (2018-09-24) + +* fix(filter): copy async pres correctly with `filter()` ([1b1ed8a](https://github.com/vkarpov15/kareem/commit/1b1ed8a)), closes [Automattic/mongoose#3054](https://github.com/Automattic/mongoose/issues/3054) +* feat: add filter() function ([1f641f4](https://github.com/vkarpov15/kareem/commit/1f641f4)) +* feat: support storing options on pre and post hooks ([59220b9](https://github.com/vkarpov15/kareem/commit/59220b9)) + + + + +## 2.2.3 (2018-09-10) + +* chore: release 2.2.3 ([af653a3](https://github.com/vkarpov15/kareem/commit/af653a3)) + + + + +## 2.2.2 (2018-09-10) + +* chore: release 2.2.2 ([3f0144d](https://github.com/vkarpov15/kareem/commit/3f0144d)) +* fix: allow merge() to not clone ([e628d65](https://github.com/vkarpov15/kareem/commit/e628d65)) + + + + +## 2.2.1 (2018-06-05) + +* chore: release 2.2.1 ([4625a64](https://github.com/vkarpov15/kareem/commit/4625a64)) +* chore: remove lockfile from git ([7f3e4e6](https://github.com/vkarpov15/kareem/commit/7f3e4e6)) +* fix: handle numAsync correctly when merging ([fef8e7e](https://github.com/vkarpov15/kareem/commit/fef8e7e)) +* test: repro issue with not copying numAsync ([952d9db](https://github.com/vkarpov15/kareem/commit/952d9db)) + + + + +## 2.2.0 (2018-06-05) + +* chore: release 2.2.0 ([ff9ad03](https://github.com/vkarpov15/kareem/commit/ff9ad03)) +* fix: use maps instead of objects for _pres and _posts so `toString()` doesn't get reported as having ([55df303](https://github.com/vkarpov15/kareem/commit/55df303)), closes [Automattic/mongoose#6538](https://github.com/Automattic/mongoose/issues/6538) + + + + +## 2.1.0 (2018-05-16) + +* chore: release 2.1.0 ([ba5f1bc](https://github.com/vkarpov15/kareem/commit/ba5f1bc)) +* feat: add option to check wrapped function return value for promises ([c9d7dd1](https://github.com/vkarpov15/kareem/commit/c9d7dd1)) +* refactor: use const in wrap() ([0fc21f9](https://github.com/vkarpov15/kareem/commit/0fc21f9)) + + + + +## 2.0.7 (2018-04-28) + +* chore: release 2.0.7 ([0bf91e6](https://github.com/vkarpov15/kareem/commit/0bf91e6)) +* feat: add `hasHooks()` ([225f18d](https://github.com/vkarpov15/kareem/commit/225f18d)), closes [Automattic/mongoose#6385](https://github.com/Automattic/mongoose/issues/6385) + + + + +## 2.0.6 (2018-03-22) + +* chore: release 2.0.6 ([f3d406b](https://github.com/vkarpov15/kareem/commit/f3d406b)) +* fix(wrap): ensure fast path still wraps function in `nextTick()` for chaining ([7000494](https://github.com/vkarpov15/kareem/commit/7000494)), closes [Automattic/mongoose#6250](https://github.com/Automattic/mongoose/issues/6250) [dsanel/mongoose-delete#36](https://github.com/dsanel/mongoose-delete/issues/36) + + + + +## 2.0.5 (2018-02-22) + +* chore: release 2.0.5 ([3286612](https://github.com/vkarpov15/kareem/commit/3286612)) +* perf(createWrapper): don't create wrapper if there are no hooks ([5afc5b9](https://github.com/vkarpov15/kareem/commit/5afc5b9)), closes [Automattic/mongoose#6126](https://github.com/Automattic/mongoose/issues/6126) + + + + +## 2.0.4 (2018-02-08) + +* chore: release 2.0.4 ([2ab0293](https://github.com/vkarpov15/kareem/commit/2ab0293)) + + + + +## 2.0.3 (2018-02-01) + +* chore: release 2.0.3 ([3c1abe5](https://github.com/vkarpov15/kareem/commit/3c1abe5)) +* fix: use process.nextTick() re: Automattic/mongoose#6074 ([e5bfe33](https://github.com/vkarpov15/kareem/commit/e5bfe33)), closes [Automattic/mongoose#6074](https://github.com/Automattic/mongoose/issues/6074) + + + + +## 2.0.2 (2018-01-24) + +* chore: fix license ([a9d755c](https://github.com/vkarpov15/kareem/commit/a9d755c)), closes [#10](https://github.com/vkarpov15/kareem/issues/10) +* chore: release 2.0.2 ([fe87ab6](https://github.com/vkarpov15/kareem/commit/fe87ab6)) + + + + +## 2.0.1 (2018-01-09) + +* chore: release 2.0.1 with lockfile bump ([09c44fb](https://github.com/vkarpov15/kareem/commit/09c44fb)) + + + + +## 2.0.0 (2018-01-09) + +* chore: bump marked re: security ([cc564a9](https://github.com/vkarpov15/kareem/commit/cc564a9)) +* chore: release 2.0.0 ([f511d1c](https://github.com/vkarpov15/kareem/commit/f511d1c)) + + + + +## 2.0.0-rc5 (2017-12-23) + +* chore: fix build on node 4+5 ([6dac5a4](https://github.com/vkarpov15/kareem/commit/6dac5a4)) +* chore: fix built on node 4 + 5 again ([434ef0a](https://github.com/vkarpov15/kareem/commit/434ef0a)) +* chore: release 2.0.0-rc5 ([25a32ee](https://github.com/vkarpov15/kareem/commit/25a32ee)) + + + + +## 2.0.0-rc4 (2017-12-22) + +* chore: release 2.0.0-rc4 ([49fc083](https://github.com/vkarpov15/kareem/commit/49fc083)) +* BREAKING CHANGE: deduplicate when merging hooks re: Automattic/mongoose#2945 ([d458573](https://github.com/vkarpov15/kareem/commit/d458573)), closes [Automattic/mongoose#2945](https://github.com/Automattic/mongoose/issues/2945) + + + + +## 2.0.0-rc3 (2017-12-22) + +* chore: release 2.0.0-rc3 ([adaaa00](https://github.com/vkarpov15/kareem/commit/adaaa00)) +* feat: support returning promises from middleware functions ([05b4480](https://github.com/vkarpov15/kareem/commit/05b4480)), closes [Automattic/mongoose#3779](https://github.com/Automattic/mongoose/issues/3779) + + + + +## 2.0.0-rc2 (2017-12-21) + +* chore: release 2.0.0-rc2 ([76325fa](https://github.com/vkarpov15/kareem/commit/76325fa)) +* fix: ensure next() and done() run in next tick ([6c20684](https://github.com/vkarpov15/kareem/commit/6c20684)) + + + + +## 2.0.0-rc1 (2017-12-21) + +* chore: improve test coverage re: Automattic/mongoose#3232 ([7b45cf0](https://github.com/vkarpov15/kareem/commit/7b45cf0)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232) +* chore: release 2.0.0-rc1 ([9b83f52](https://github.com/vkarpov15/kareem/commit/9b83f52)) +* BREAKING CHANGE: report sync exceptions as errors, only allow calling next() and done() once ([674adcc](https://github.com/vkarpov15/kareem/commit/674adcc)), closes [Automattic/mongoose#3483](https://github.com/Automattic/mongoose/issues/3483) + + + + +## 2.0.0-rc0 (2017-12-17) + +* chore: release 2.0.0-rc0 ([16b44b5](https://github.com/vkarpov15/kareem/commit/16b44b5)) +* BREAKING CHANGE: drop support for node < 4 ([9cbb8c7](https://github.com/vkarpov15/kareem/commit/9cbb8c7)) +* BREAKING CHANGE: remove useLegacyPost and add several new features ([6dd8531](https://github.com/vkarpov15/kareem/commit/6dd8531)), closes [Automattic/mongoose#3232](https://github.com/Automattic/mongoose/issues/3232) + + + + +## 1.5.0 (2017-07-20) + +* chore: release 1.5.0 ([9c491a0](https://github.com/vkarpov15/kareem/commit/9c491a0)) +* fix: improve post error handlers results ([9928dd5](https://github.com/vkarpov15/kareem/commit/9928dd5)), closes [Automattic/mongoose#5466](https://github.com/Automattic/mongoose/issues/5466) + + + + +## 1.4.2 (2017-07-06) + +* chore: release 1.4.2 ([8d14ac5](https://github.com/vkarpov15/kareem/commit/8d14ac5)) +* fix: correct args re: Automattic/mongoose#5405 ([3f28ae6](https://github.com/vkarpov15/kareem/commit/3f28ae6)), closes [Automattic/mongoose#5405](https://github.com/Automattic/mongoose/issues/5405) + + + + +## 1.4.1 (2017-04-25) + +* chore: release 1.4.1 ([5ecf0c2](https://github.com/vkarpov15/kareem/commit/5ecf0c2)) +* fix: handle numAsyncPres with clone() ([c72e857](https://github.com/vkarpov15/kareem/commit/c72e857)), closes [#8](https://github.com/vkarpov15/kareem/issues/8) +* test: repro #8 ([9b4d6b2](https://github.com/vkarpov15/kareem/commit/9b4d6b2)), closes [#8](https://github.com/vkarpov15/kareem/issues/8) + + + + +## 1.4.0 (2017-04-19) + +* chore: release 1.4.0 ([101c5f5](https://github.com/vkarpov15/kareem/commit/101c5f5)) +* feat: add merge() function ([285325e](https://github.com/vkarpov15/kareem/commit/285325e)) + + + + +## 1.3.0 (2017-03-26) + +* chore: release 1.3.0 ([f3a9e50](https://github.com/vkarpov15/kareem/commit/f3a9e50)) +* feat: pass function args to execPre ([4dd466d](https://github.com/vkarpov15/kareem/commit/4dd466d)) + + + + +## 1.2.1 (2017-02-03) + +* chore: release 1.2.1 ([d97081f](https://github.com/vkarpov15/kareem/commit/d97081f)) +* fix: filter out _kareemIgnored args for error handlers re: Automattic/mongoose#4925 ([ddc7aeb](https://github.com/vkarpov15/kareem/commit/ddc7aeb)), closes [Automattic/mongoose#4925](https://github.com/Automattic/mongoose/issues/4925) +* fix: make error handlers handle errors in pre hooks ([af38033](https://github.com/vkarpov15/kareem/commit/af38033)), closes [Automattic/mongoose#4927](https://github.com/Automattic/mongoose/issues/4927) + + + + +## 1.2.0 (2017-01-02) + +* chore: release 1.2.0 ([033225c](https://github.com/vkarpov15/kareem/commit/033225c)) +* chore: upgrade deps ([f9e9a09](https://github.com/vkarpov15/kareem/commit/f9e9a09)) +* feat: add _kareemIgnore re: Automattic/mongoose#4836 ([7957771](https://github.com/vkarpov15/kareem/commit/7957771)), closes [Automattic/mongoose#4836](https://github.com/Automattic/mongoose/issues/4836) + + + + +## 1.1.5 (2016-12-13) + +* chore: release 1.1.5 ([1a9f684](https://github.com/vkarpov15/kareem/commit/1a9f684)) +* fix: correct field name ([04a0e9d](https://github.com/vkarpov15/kareem/commit/04a0e9d)) + + + + +## 1.1.4 (2016-12-09) + +* chore: release 1.1.4 ([ece401c](https://github.com/vkarpov15/kareem/commit/ece401c)) +* chore: run tests on node 6 ([e0cb1cb](https://github.com/vkarpov15/kareem/commit/e0cb1cb)) +* fix: only copy own properties in clone() ([dfe28ce](https://github.com/vkarpov15/kareem/commit/dfe28ce)), closes [#7](https://github.com/vkarpov15/kareem/issues/7) + + + + +## 1.1.3 (2016-06-27) + +* chore: release 1.1.3 ([87171c8](https://github.com/vkarpov15/kareem/commit/87171c8)) +* fix: couple more issues with arg processing ([c65f523](https://github.com/vkarpov15/kareem/commit/c65f523)) + + + + +## 1.1.2 (2016-06-27) + +* chore: release 1.1.2 ([8e102b6](https://github.com/vkarpov15/kareem/commit/8e102b6)) +* fix: add early return ([4feda4e](https://github.com/vkarpov15/kareem/commit/4feda4e)) + + + + +## 1.1.1 (2016-06-27) + +* chore: release 1.1.1 ([8bb3050](https://github.com/vkarpov15/kareem/commit/8bb3050)) +* fix: skip error handlers if no error ([0eb3a44](https://github.com/vkarpov15/kareem/commit/0eb3a44)) + + + + +## 1.1.0 (2016-05-11) + +* chore: release 1.1.0 ([85332d9](https://github.com/vkarpov15/kareem/commit/85332d9)) +* chore: test on node 4 and node 5 ([1faefa1](https://github.com/vkarpov15/kareem/commit/1faefa1)) +* 100% coverage again ([c9aee4e](https://github.com/vkarpov15/kareem/commit/c9aee4e)) +* add support for error post hooks ([d378113](https://github.com/vkarpov15/kareem/commit/d378113)) +* basic setup for sync hooks #4 ([55aa081](https://github.com/vkarpov15/kareem/commit/55aa081)), closes [#4](https://github.com/vkarpov15/kareem/issues/4) +* proof of concept for error handlers ([e4a07d9](https://github.com/vkarpov15/kareem/commit/e4a07d9)) +* refactor out handleWrapError helper ([b19af38](https://github.com/vkarpov15/kareem/commit/b19af38)) + + + + +## 1.0.1 (2015-05-10) + +* Fix #1 ([de60dc6](https://github.com/vkarpov15/kareem/commit/de60dc6)), closes [#1](https://github.com/vkarpov15/kareem/issues/1) +* release 1.0.1 ([6971088](https://github.com/vkarpov15/kareem/commit/6971088)) +* Run tests on iojs in travis ([adcd201](https://github.com/vkarpov15/kareem/commit/adcd201)) +* support legacy post hook behavior in wrap() ([23fa74c](https://github.com/vkarpov15/kareem/commit/23fa74c)) +* Use node 0.12 in travis ([834689d](https://github.com/vkarpov15/kareem/commit/834689d)) + + + + +## 1.0.0 (2015-01-28) + +* Tag 1.0.0 ([4c5a35a](https://github.com/vkarpov15/kareem/commit/4c5a35a)) + + + + +## 0.0.8 (2015-01-27) + +* Add clone function ([688bba7](https://github.com/vkarpov15/kareem/commit/688bba7)) +* Add jscs for style checking ([5c93149](https://github.com/vkarpov15/kareem/commit/5c93149)) +* Bump 0.0.8 ([03c0d2f](https://github.com/vkarpov15/kareem/commit/03c0d2f)) +* Fix jscs config, add gulp rules ([9989abf](https://github.com/vkarpov15/kareem/commit/9989abf)) +* fix Makefile typo ([1f7e61a](https://github.com/vkarpov15/kareem/commit/1f7e61a)) + + + + +## 0.0.7 (2015-01-04) + +* Bump 0.0.7 ([98ef173](https://github.com/vkarpov15/kareem/commit/98ef173)) +* fix LearnBoost/mongoose#2553 - use null instead of undefined for err ([9157b48](https://github.com/vkarpov15/kareem/commit/9157b48)), closes [LearnBoost/mongoose#2553](https://github.com/LearnBoost/mongoose/issues/2553) +* Regenerate docs ([2331cdf](https://github.com/vkarpov15/kareem/commit/2331cdf)) + + + + +## 0.0.6 (2015-01-01) + +* Update docs and bump 0.0.6 ([92c12a7](https://github.com/vkarpov15/kareem/commit/92c12a7)) + + + + +## 0.0.5 (2015-01-01) + +* Add coverage rule to Makefile ([825a91c](https://github.com/vkarpov15/kareem/commit/825a91c)) +* Add coveralls to README ([fb52369](https://github.com/vkarpov15/kareem/commit/fb52369)) +* Add coveralls to travis ([93f6f15](https://github.com/vkarpov15/kareem/commit/93f6f15)) +* Add createWrapper() function ([ea77741](https://github.com/vkarpov15/kareem/commit/ea77741)) +* Add istanbul code coverage ([6eceeef](https://github.com/vkarpov15/kareem/commit/6eceeef)) +* Add some more comments for examples ([c5b0c6f](https://github.com/vkarpov15/kareem/commit/c5b0c6f)) +* Add travis ([e6dcb06](https://github.com/vkarpov15/kareem/commit/e6dcb06)) +* Add travis badge to docs ([ad8c9b3](https://github.com/vkarpov15/kareem/commit/ad8c9b3)) +* Add wrap() tests, 100% coverage ([6945be4](https://github.com/vkarpov15/kareem/commit/6945be4)) +* Better test coverage for execPost ([d9ad539](https://github.com/vkarpov15/kareem/commit/d9ad539)) +* Bump 0.0.5 ([69875b1](https://github.com/vkarpov15/kareem/commit/69875b1)) +* Docs fix ([15b7098](https://github.com/vkarpov15/kareem/commit/15b7098)) +* Fix silly mistake in docs generation ([50373eb](https://github.com/vkarpov15/kareem/commit/50373eb)) +* Fix typo in readme ([fec4925](https://github.com/vkarpov15/kareem/commit/fec4925)) +* Linkify travis badge ([92b25fe](https://github.com/vkarpov15/kareem/commit/92b25fe)) +* Make travis run coverage ([747157b](https://github.com/vkarpov15/kareem/commit/747157b)) +* Move travis status badge ([d52e89b](https://github.com/vkarpov15/kareem/commit/d52e89b)) +* Quick fix for coverage ([50bbddb](https://github.com/vkarpov15/kareem/commit/50bbddb)) +* Typo fix ([adea794](https://github.com/vkarpov15/kareem/commit/adea794)) + + + + +## 0.0.4 (2014-12-13) + +* Bump 0.0.4, run docs generation ([51a15fe](https://github.com/vkarpov15/kareem/commit/51a15fe)) +* Use correct post parameters in wrap() ([9bb5da3](https://github.com/vkarpov15/kareem/commit/9bb5da3)) + + + + +## 0.0.3 (2014-12-12) + +* Add npm test script, fix small bug with args not getting passed through post ([49e3e68](https://github.com/vkarpov15/kareem/commit/49e3e68)) +* Bump 0.0.3 ([65621d8](https://github.com/vkarpov15/kareem/commit/65621d8)) +* Update readme ([901388b](https://github.com/vkarpov15/kareem/commit/901388b)) + + + + +## 0.0.2 (2014-12-12) + +* Add github repo and bump 0.0.2 ([59db8be](https://github.com/vkarpov15/kareem/commit/59db8be)) + + + + +## 0.0.1 (2014-12-12) + +* Add basic docs ([ad29ea4](https://github.com/vkarpov15/kareem/commit/ad29ea4)) +* Add pre hooks ([2ffc356](https://github.com/vkarpov15/kareem/commit/2ffc356)) +* Add wrap function ([68c540c](https://github.com/vkarpov15/kareem/commit/68c540c)) +* Bump to version 0.0.1 ([a4bfd68](https://github.com/vkarpov15/kareem/commit/a4bfd68)) +* Initial commit ([4002458](https://github.com/vkarpov15/kareem/commit/4002458)) +* Initial deposit ([98fc489](https://github.com/vkarpov15/kareem/commit/98fc489)) +* Post hooks ([395b67c](https://github.com/vkarpov15/kareem/commit/395b67c)) +* Some basic setup work ([82df75e](https://github.com/vkarpov15/kareem/commit/82df75e)) +* Support sync pre hooks ([1cc1b9f](https://github.com/vkarpov15/kareem/commit/1cc1b9f)) +* Update package.json description ([978da18](https://github.com/vkarpov15/kareem/commit/978da18)) diff --git a/backend/node_modules/kareem/LICENSE b/backend/node_modules/kareem/LICENSE new file mode 100644 index 0000000..b0d46d3 --- /dev/null +++ b/backend/node_modules/kareem/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014-2022 mongoosejs + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/backend/node_modules/kareem/README.md b/backend/node_modules/kareem/README.md new file mode 100644 index 0000000..92b01c9 --- /dev/null +++ b/backend/node_modules/kareem/README.md @@ -0,0 +1,385 @@ +# kareem + + [![Build Status](https://github.com/mongoosejs/kareem/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/mongoosejs/kareem/actions/workflows/test.yml) + + +Re-imagined take on the [hooks](http://npmjs.org/package/hooks) module, meant to offer additional flexibility in allowing you to execute hooks whenever necessary, as opposed to simply wrapping a single function. + +Named for the NBA's 2nd all-time leading scorer Kareem Abdul-Jabbar, known for his mastery of the [hook shot](http://en.wikipedia.org/wiki/Kareem_Abdul-Jabbar#Skyhook) + + + + + +# API + +## pre hooks + +Much like [hooks](https://npmjs.org/package/hooks), kareem lets you define +pre and post hooks: pre hooks are called before a given function executes. +Unlike hooks, kareem stores hooks and other internal state in a separate +object, rather than relying on inheritance. Furthermore, kareem exposes +an `execPre()` function that allows you to execute your pre hooks when +appropriate, giving you more fine-grained control over your function hooks. + +### It runs without any hooks specified + +```javascript +await hooks.execPre('cook', null); +``` + +### It runs basic serial pre hooks + +pre hook functions can return a promise that resolves when finished. + +```javascript +let count = 0; + +hooks.pre('cook', function() { + ++count; + return Promise.resolve(); +}); + +await hooks.execPre('cook', null); +assert.equal(1, count); +``` + +### It can run multiple pre hooks + +```javascript +let count1 = 0; +let count2 = 0; + +hooks.pre('cook', function() { + ++count1; + return Promise.resolve(); +}); + +hooks.pre('cook', function() { + ++count2; + return Promise.resolve(); +}); + +await hooks.execPre('cook', null); +assert.equal(1, count1); +assert.equal(1, count2); +``` + +### It can run fully synchronous pre hooks + +If your pre hook function takes no parameters, its assumed to be +fully synchronous. + +```javascript +let count1 = 0; +let count2 = 0; + +hooks.pre('cook', function() { + ++count1; +}); + +hooks.pre('cook', function() { + ++count2; +}); + +await hooks.execPre('cook', null); +assert.equal(1, count1); +assert.equal(1, count2); +``` + +### It properly attaches context to pre hooks + +Pre save hook functions are bound to the second parameter to `execPre()` + +```javascript +hooks.pre('cook', function() { + this.bacon = 3; +}); + +hooks.pre('cook', function() { + this.eggs = 4; +}); + +const obj = { bacon: 0, eggs: 0 }; + +// In the pre hooks, `this` will refer to `obj` +await hooks.execPre('cook', obj); +assert.equal(3, obj.bacon); +assert.equal(4, obj.eggs); +``` + +### It supports returning a promise + +You can also return a promise from your pre hooks instead of calling +`next()`. When the returned promise resolves, kareem will kick off the +next middleware. + +```javascript +hooks.pre('cook', function() { + return new Promise(resolve => { + setTimeout(() => { + this.bacon = 3; + resolve(); + }, 100); + }); +}); + +const obj = { bacon: 0 }; + +await hooks.execPre('cook', obj); +assert.equal(3, obj.bacon); +``` + +### It supports filtering which hooks to run + +You can pass a `filter` option to `execPre()` to select which hooks +to run. The filter function receives each hook object and should return +`true` to run the hook or `false` to skip it. + +```javascript +const execed = []; + +const fn1 = function() { execed.push('first'); }; +fn1.skipMe = true; +hooks.pre('cook', fn1); + +const fn2 = function() { execed.push('second'); }; +hooks.pre('cook', fn2); + +// Only runs fn2, skips fn1 because fn1.skipMe is true +await hooks.execPre('cook', null, [], { + filter: hook => !hook.fn.skipMe +}); + +assert.deepStrictEqual(execed, ['second']); +``` + +## post hooks + +### It runs without any hooks specified + +```javascript +const [eggs] = await hooks.execPost('cook', null, [1]); +assert.equal(eggs, 1); +``` + +### It executes with parameters passed in + +```javascript +hooks.post('cook', function(eggs, bacon, callback) { + assert.equal(eggs, 1); + assert.equal(bacon, 2); + callback(); +}); + +const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]); +assert.equal(eggs, 1); +assert.equal(bacon, 2); +``` + +### It can use synchronous post hooks + +```javascript +const execed = {}; + +hooks.post('cook', function(eggs, bacon) { + execed.first = true; + assert.equal(eggs, 1); + assert.equal(bacon, 2); +}); + +hooks.post('cook', function(eggs, bacon, callback) { + execed.second = true; + assert.equal(eggs, 1); + assert.equal(bacon, 2); + callback(); +}); + +const [eggs, bacon] = await hooks.execPost('cook', null, [1, 2]); +assert.equal(Object.keys(execed).length, 2); +assert.ok(execed.first); +assert.ok(execed.second); +assert.equal(eggs, 1); +assert.equal(bacon, 2); +``` + +### It supports returning a promise + +You can also return a promise from your post hooks instead of calling +`next()`. When the returned promise resolves, kareem will kick off the +next middleware. + +```javascript +hooks.post('cook', function() { + return new Promise(resolve => { + setTimeout(() => { + this.bacon = 3; + resolve(); + }, 100); + }); +}); + +const obj = { bacon: 0 }; + +await hooks.execPost('cook', obj, [obj]); +assert.equal(obj.bacon, 3); +``` + +### It supports filtering which hooks to run + +You can pass a `filter` option to `execPost()` to select which hooks +to run. The filter function receives each hook object and should return +`true` to run the hook or `false` to skip it. + +```javascript +const execed = []; + +const fn1 = function() { execed.push('first'); }; +fn1.skipMe = true; +hooks.post('cook', fn1); + +const fn2 = function() { execed.push('second'); }; +hooks.post('cook', fn2); + +// Only runs fn2, skips fn1 because fn1.skipMe is true +await hooks.execPost('cook', null, [], { + filter: hook => !hook.fn.skipMe +}); + +assert.deepStrictEqual(execed, ['second']); +``` + +## wrap() + +### It wraps pre and post calls into one call + +```javascript +hooks.pre('cook', function() { + return new Promise(resolve => { + this.bacon = 3; + setTimeout(() => { + resolve(); + }, 5); + }); +}); + +hooks.pre('cook', function() { + this.eggs = 4; + return Promise.resolve(); +}); + +hooks.pre('cook', function() { + this.waffles = false; + return Promise.resolve(); +}); + +hooks.post('cook', function(obj) { + obj.tofu = 'no'; +}); + +const obj = { bacon: 0, eggs: 0 }; + +const args = [obj]; + +const result = await hooks.wrap( + 'cook', + function(o) { + assert.equal(obj.bacon, 3); + assert.equal(obj.eggs, 4); + assert.equal(obj.waffles, false); + assert.equal(obj.tofu, undefined); + return o; + }, + obj, + args); + +assert.equal(obj.bacon, 3); +assert.equal(obj.eggs, 4); +assert.equal(obj.waffles, false); +assert.equal(obj.tofu, 'no'); +assert.equal(result, obj); +``` + +## createWrapper() + +### It wraps wrap() into a callable function + +```javascript +hooks.pre('cook', function() { + this.bacon = 3; + return Promise.resolve(); +}); + +hooks.pre('cook', function() { + return new Promise(resolve => { + this.eggs = 4; + setTimeout(function() { + resolve(); + }, 10); + }); +}); + +hooks.pre('cook', function() { + this.waffles = false; + return Promise.resolve(); +}); + +hooks.post('cook', function(obj) { + obj.tofu = 'no'; +}); + +const obj = { bacon: 0, eggs: 0 }; + +const cook = hooks.createWrapper( + 'cook', + function(o) { + assert.equal(3, obj.bacon); + assert.equal(4, obj.eggs); + assert.equal(false, obj.waffles); + assert.equal(undefined, obj.tofu); + return o; + }, + obj); + +const result = await cook(obj); +assert.equal(obj.bacon, 3); +assert.equal(obj.eggs, 4); +assert.equal(obj.waffles, false); +assert.equal(obj.tofu, 'no'); + +assert.equal(result, obj); +``` + +## clone() + +### It clones a Kareem object + +```javascript +const k1 = new Kareem(); +k1.pre('cook', function() {}); +k1.post('cook', function() {}); + +const k2 = k1.clone(); +assert.deepEqual(Array.from(k2._pres.keys()), ['cook']); +assert.deepEqual(Array.from(k2._posts.keys()), ['cook']); +``` + +## merge() + +### It pulls hooks from another Kareem object + +```javascript +const k1 = new Kareem(); +const test1 = function() {}; +k1.pre('cook', test1); +k1.post('cook', function() {}); + +const k2 = new Kareem(); +const test2 = function() {}; +k2.pre('cook', test2); +const k3 = k2.merge(k1); +assert.equal(k3._pres.get('cook').length, 2); +assert.equal(k3._pres.get('cook')[0].fn, test2); +assert.equal(k3._pres.get('cook')[1].fn, test1); +assert.equal(k3._posts.get('cook').length, 1); +``` diff --git a/backend/node_modules/kareem/SECURITY.md b/backend/node_modules/kareem/SECURITY.md new file mode 100644 index 0000000..da9c516 --- /dev/null +++ b/backend/node_modules/kareem/SECURITY.md @@ -0,0 +1,5 @@ +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. diff --git a/backend/node_modules/kareem/coverage/lcov-report/base.css b/backend/node_modules/kareem/coverage/lcov-report/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/backend/node_modules/kareem/coverage/lcov-report/block-navigation.js b/backend/node_modules/kareem/coverage/lcov-report/block-navigation.js new file mode 100644 index 0000000..cc12130 --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selecter that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/backend/node_modules/kareem/coverage/lcov-report/favicon.png b/backend/node_modules/kareem/coverage/lcov-report/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..c1525b811a167671e9de1fa78aab9f5c0b61cef7 GIT binary patch literal 445 zcmV;u0Yd(XP))rP{nL}Ln%S7`m{0DjX9TLF* zFCb$4Oi7vyLOydb!7n&^ItCzb-%BoB`=x@N2jll2Nj`kauio%aw_@fe&*}LqlFT43 z8doAAe))z_%=P%v^@JHp3Hjhj^6*Kr_h|g_Gr?ZAa&y>wxHE99Gk>A)2MplWz2xdG zy8VD2J|Uf#EAw*bo5O*PO_}X2Tob{%bUoO2G~T`@%S6qPyc}VkhV}UifBuRk>%5v( z)x7B{I~z*k<7dv#5tC+m{km(D087J4O%+<<;K|qwefb6@GSX45wCK}Sn*> + + + + Code coverage report for All files + + + + + + + + + +
+
+

All files

+
+ +
+ 96.66% + Statements + 232/240 +
+ + +
+ 90.08% + Branches + 109/121 +
+ + +
+ 100% + Functions + 30/30 +
+ + +
+ 96.56% + Lines + 225/233 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FileStatementsBranchesFunctionsLines
index.js +
+
96.66%232/24090.08%109/121100%30/3096.56%225/233
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/node_modules/kareem/coverage/lcov-report/index.js.html b/backend/node_modules/kareem/coverage/lcov-report/index.js.html new file mode 100644 index 0000000..79937ed --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/index.js.html @@ -0,0 +1,1603 @@ + + + + + + Code coverage report for index.js + + + + + + + + + +
+
+

All files index.js

+
+ +
+ 96.66% + Statements + 232/240 +
+ + +
+ 90.08% + Branches + 109/121 +
+ + +
+ 100% + Functions + 30/30 +
+ + +
+ 96.56% + Lines + 225/233 +
+ + +
+

+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +

+ +
+
+

+
1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 +375 +376 +377 +378 +379 +380 +381 +382 +383 +384 +385 +386 +387 +388 +389 +390 +391 +392 +393 +394 +395 +396 +397 +398 +399 +400 +401 +402 +403 +404 +405 +406 +407 +408 +409 +410 +411 +412 +413 +414 +415 +416 +417 +418 +419 +420 +421 +422 +423 +424 +425 +426 +427 +428 +429 +430 +431 +432 +433 +434 +435 +436 +437 +438 +439 +440 +441 +442 +443 +444 +445 +446 +447 +448 +449 +450 +451 +452 +453 +454 +455 +456 +457 +458 +459 +460 +461 +462 +463 +464 +465 +466 +467 +468 +469 +470 +471 +472 +473 +474 +475 +476 +477 +478 +479 +480 +481 +482 +483 +484 +485 +486 +487 +488 +489 +490 +491 +492 +493 +494 +495 +496 +497 +498 +499 +500 +501 +502 +503 +504 +505 +506 +507  +  +  +  +  +  +62x +62x +  +  +1x +6x +3x +  +  +3x +  +  +1x +12x +6x +  +  +6x +  +  +  +  +  +  +  +  +  +1x +27x +27x +27x +27x +  +27x +3x +  +  +24x +24x +33x +27x +27x +27x +27x +27x +  +27x +27x +27x +19x +  +  +19x +  +  +27x +27x +27x +  +  +27x +  +  +9x +3x +3x +  +6x +  +  +6x +6x +  +2x +  +  +  +2x +  +  +  +  +16x +  +16x +3x +  +  +  +  +  +  +  +  +  +  +1x +4x +4x +  +4x +4x +  +  +  +  +  +  +  +  +  +  +  +1x +27x +27x +  +27x +27x +6x +  +  +27x +1x +  +  +1x +  +  +26x +41x +41x +41x +41x +41x +40x +40x +40x +  +  +  +41x +1x +1x +1x +  +  +  +  +  +41x +41x +41x +  +41x +24x +12x +  +12x +  +  +  +41x +14x +8x +8x +8x +1x +7x +  +6x +  +  +5x +  +  +  +5x +  +  +6x +  +  +27x +  +1x +  +26x +26x +26x +26x +3x +23x +  +18x +  +  +7x +2x +2x +  +5x +5x +  +  +19x +2x +2x +  +  +  +  +  +26x +11x +  +  +15x +  +  +  +  +  +  +  +  +  +1x +5x +5x +  +5x +6x +6x +2x +  +  +  +5x +  +  +  +  +  +  +  +  +1x +2x +2x +2x +  +2x +  +2x +  +2x +  +  +  +  +  +  +  +  +  +  +  +  +1x +  +17x +17x +17x +  +7x +2x +2x +  +5x +  +  +  +12x +10x +  +  +9x +  +7x +  +  +  +  +  +  +  +1x +2x +  +2x +2x +4x +6x +  +  +4x +1x +1x +  +  +3x +  +  +2x +2x +4x +6x +  +  +4x +1x +1x +  +  +3x +  +  +2x +  +  +  +  +  +  +  +1x +2x +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +  +  +  +1x +1x +1x +  +  +  +  +  +  +  +  +  +  +  +  +1x +51x +43x +43x +8x +1x +  +  +51x +51x +  +51x +1x +  +  +50x +2x +  +48x +  +  +50x +  +  +  +  +  +  +  +  +  +  +  +1x +60x +  +60x +52x +52x +52x +  +  +60x +1x +  +  +59x +2x +  +57x +  +59x +59x +  +  +  +  +  +  +  +  +  +  +  +1x +1x +1x +1x +1x +  +1x +  +  +  +  +  +  +1x +4x +  +4x +6x +6x +  +4x +5x +  +  +4x +  +  +  +  +  +  +  +  +1x +1x +1x +  +1x +1x +1x +  +1x +1x +1x +  +1x +1x +1x +1x +1x +  +  +1x +  +  +  +61x +  +  +  +41x +2x +  +39x +  +  +1x + 
'use strict';
+ 
+/**
+ * Create a new instance
+ */
+function Kareem() {
+  this._pres = new Map();
+  this._posts = new Map();
+}
+ 
+Kareem.skipWrappedFunction = function skipWrappedFunction() {
+  if (!(this instanceof Kareem.skipWrappedFunction)) {
+    return new Kareem.skipWrappedFunction(...arguments);
+  }
+ 
+  this.args = [...arguments];
+};
+ 
+Kareem.overwriteResult = function overwriteResult() {
+  if (!(this instanceof Kareem.overwriteResult)) {
+    return new Kareem.overwriteResult(...arguments);
+  }
+ 
+  this.args = [...arguments];
+};
+ 
+/**
+ * Execute all "pre" hooks for "name"
+ * @param {String} name The hook name to execute
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Array|Function} args arguments passed to the pre hooks
+ * @returns {void}
+ */
+Kareem.prototype.execPre = async function execPre(name, context, args) {
+  const pres = this._pres.get(name) || [];
+  const numPres = pres.length;
+  const $args = args;
+  let skipWrappedFunction = null;
+ 
+  if (!numPres) {
+    return;
+  }
+ 
+  const asyncPrePromises = [];
+  for (const pre of pres) {
+    if (pre.fn.length > 0) {
+      let resolve = null;
+      let reject = null;
+      const cbPromise = new Promise((_resolve, _reject) => {
+        resolve = _resolve;
+        reject = _reject;
+      });
+      const args = [(err) => err ? reject(err) : resolve()];
+      const _args = [null].concat($args);
+      for (let i = 1; i < _args.length; ++i) {
+        Iif (i === _args.length - 1 && typeof _args[i] === 'function') {
+          continue; // skip callbacks to avoid accidentally calling the callback from a hook
+        }
+        args.push(_args[i]);
+      }
+ 
+      try {
+        const maybePromiseLike = pre.fn.apply(context, args);
+        Iif (isPromiseLike(maybePromiseLike)) {
+          await maybePromiseLike;
+        } else {
+          await cbPromise;
+        }
+      } catch (error) {
+        if (error instanceof Kareem.skipWrappedFunction) {
+          skipWrappedFunction = error;
+          continue;
+        }
+        throw error;
+      }
+    } else {
+      try {
+        await pre.fn.call(context);
+      } catch (error) {
+        Iif (error instanceof Kareem.skipWrappedFunction) {
+          skipWrappedFunction = error;
+          continue;
+        }
+        throw error;
+      }
+    }
+  }
+ 
+  await Promise.all(asyncPrePromises);
+ 
+  if (skipWrappedFunction) {
+    throw skipWrappedFunction;
+  }
+};
+ 
+/**
+ * Execute all "pre" hooks for "name" synchronously
+ * @param {String} name The hook name to execute
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Array} [args] Apply custom arguments to the hook
+ * @returns {void}
+ */
+Kareem.prototype.execPreSync = function(name, context, args) {
+  const pres = this._pres.get(name) || [];
+  const numPres = pres.length;
+ 
+  for (let i = 0; i < numPres; ++i) {
+    pres[i].fn.apply(context, args || []);
+  }
+};
+ 
+/**
+ * Execute all "post" hooks for "name"
+ * @param {String} name The hook name to execute
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Array|Function} args Apply custom arguments to the hook
+ * @param {*} options Optional options or directly the callback
+ * @returns {void}
+ */
+Kareem.prototype.execPost = async function execPost(name, context, args, options) {
+  const posts = this._posts.get(name) || [];
+  const numPosts = posts.length;
+ 
+  let firstError = null;
+  if (options && options.error) {
+    firstError = options.error;
+  }
+ 
+  if (!numPosts) {
+    Iif (firstError != null) {
+      throw firstError;
+    }
+    return args;
+  }
+ 
+  for (const currentPost of posts) {
+    const post = currentPost.fn;
+    let numArgs = 0;
+    const newArgs = [];
+    const argLength = args.length;
+    for (let i = 0; i < argLength; ++i) {
+      Eif (!args[i] || !args[i]._kareemIgnore) {
+        numArgs += 1;
+        newArgs.push(args[i]);
+      }
+    }
+    // If numCallbackParams set, fill in the rest with null to enforce consistent number of args
+    if (options?.numCallbackParams != null) {
+      numArgs = options.numCallbackParams;
+      for (let i = newArgs.length; i < numArgs; ++i) {
+        newArgs.push(null);
+      }
+    }
+ 
+    let resolve;
+    let reject;
+    const cbPromise = new Promise((_resolve, _reject) => {
+      resolve = _resolve;
+      reject = _reject;
+    });
+    newArgs.push(function nextCallback(err) {
+      if (err) {
+        reject(err);
+      } else {
+        resolve();
+      }
+    });
+ 
+    if (firstError) {
+      if (isErrorHandlingMiddleware(currentPost, numArgs)) {
+        try {
+          const res = post.apply(context, [firstError].concat(newArgs));
+          if (isPromiseLike(res)) {
+            await res;
+          } else if (post.length === numArgs + 2) {
+            // `numArgs + 2` because we added the error and the callback
+            await cbPromise;
+          }
+        } catch (error) {
+          Iif (error instanceof Kareem.overwriteResult) {
+            args = error.args;
+            continue;
+          }
+          firstError = error;
+        }
+      } else {
+        continue;
+      }
+    } else {
+      if (isErrorHandlingMiddleware(currentPost, numArgs)) {
+        // Skip error handlers if no error
+        continue;
+      } else {
+        let res = null;
+        try {
+          res = post.apply(context, newArgs);
+          if (isPromiseLike(res)) {
+            res = await res;
+          } else if (post.length === numArgs + 1) {
+            // If post function takes a callback, wait for the post function to call the callback
+            res = await cbPromise;
+          }
+        } catch (error) {
+          if (error instanceof Kareem.overwriteResult) {
+            args = error.args;
+            continue;
+          }
+          firstError = error;
+          continue;
+        }
+ 
+        if (res instanceof Kareem.overwriteResult) {
+          args = res.args;
+          continue;
+        }
+      }
+    }
+  }
+ 
+  if (firstError != null) {
+    throw firstError;
+  }
+ 
+  return args;
+};
+ 
+/**
+ * Execute all "post" hooks for "name" synchronously
+ * @param {String} name The hook name to execute
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Array|Function} args Apply custom arguments to the hook
+ * @returns {Array} The used arguments
+ */
+Kareem.prototype.execPostSync = function(name, context, args) {
+  const posts = this._posts.get(name) || [];
+  const numPosts = posts.length;
+ 
+  for (let i = 0; i < numPosts; ++i) {
+    const res = posts[i].fn.apply(context, args || []);
+    if (res instanceof Kareem.overwriteResult) {
+      args = res.args;
+    }
+  }
+ 
+  return args;
+};
+ 
+/**
+ * Create a synchronous wrapper for "fn"
+ * @param {String} name The name of the hook
+ * @param {Function} fn The function to wrap
+ * @returns {Function} The wrapped function
+ */
+Kareem.prototype.createWrapperSync = function(name, fn) {
+  const _this = this;
+  return function syncWrapper() {
+    _this.execPreSync(name, this, arguments);
+ 
+    const toReturn = fn.apply(this, arguments);
+ 
+    const result = _this.execPostSync(name, this, [toReturn]);
+ 
+    return result[0];
+  };
+};
+ 
+/**
+ * Executes pre hooks, followed by the wrapped function, followed by post hooks.
+ * @param {String} name The name of the hook
+ * @param {Function} fn The function for the hook
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Array} args Apply custom arguments to the hook
+ * @param {Object} options Additional options for the hook
+ * @returns {void}
+ */
+Kareem.prototype.wrap = async function wrap(name, fn, context, args, options) {
+  let ret;
+  let skipWrappedFunction = false;
+  try {
+    await this.execPre(name, context, args);
+  } catch (error) {
+    if (error instanceof Kareem.skipWrappedFunction) {
+      ret = error.args;
+      skipWrappedFunction = true;
+    } else {
+      await this.execPost(name, context, args, { ...options, error });
+    }
+  }
+ 
+  if (!skipWrappedFunction) {
+    ret = await fn.apply(context, args);
+  }
+ 
+  ret = await this.execPost(name, context, [ret], options);
+ 
+  return ret[0];
+};
+ 
+/**
+ * Filter current instance for something specific and return the filtered clone
+ * @param {Function} fn The filter function
+ * @returns {Kareem} The cloned and filtered instance
+ */
+Kareem.prototype.filter = function(fn) {
+  const clone = this.clone();
+ 
+  const pres = Array.from(clone._pres.keys());
+  for (const name of pres) {
+    const hooks = this._pres.get(name).
+      map(h => Object.assign({}, h, { name: name })).
+      filter(fn);
+ 
+    if (hooks.length === 0) {
+      clone._pres.delete(name);
+      continue;
+    }
+ 
+    clone._pres.set(name, hooks);
+  }
+ 
+  const posts = Array.from(clone._posts.keys());
+  for (const name of posts) {
+    const hooks = this._posts.get(name).
+      map(h => Object.assign({}, h, { name: name })).
+      filter(fn);
+ 
+    if (hooks.length === 0) {
+      clone._posts.delete(name);
+      continue;
+    }
+ 
+    clone._posts.set(name, hooks);
+  }
+ 
+  return clone;
+};
+ 
+/**
+ * Check for a "name" to exist either in pre or post hooks
+ * @param {String} name The name of the hook
+ * @returns {Boolean} "true" if found, "false" otherwise
+ */
+Kareem.prototype.hasHooks = function(name) {
+  return this._pres.has(name) || this._posts.has(name);
+};
+ 
+/**
+ * Create a Wrapper for "fn" on "name" and return the wrapped function
+ * @param {String} name The name of the hook
+ * @param {Function} fn The function to wrap
+ * @param {*} context Overwrite the "this" for the hook
+ * @param {Object} [options]
+ * @returns {Function} The wrapped function
+ */
+Kareem.prototype.createWrapper = function(name, fn, context, options) {
+  const _this = this;
+  Iif (!this.hasHooks(name)) {
+    // Fast path: if there's no hooks for this function, just return the function
+    return fn;
+  }
+  return function kareemWrappedFunction() {
+    const _context = context || this;
+    return _this.wrap(name, fn, _context, Array.from(arguments), options);
+  };
+};
+ 
+/**
+ * Register a new hook for "pre"
+ * @param {String} name The name of the hook
+ * @param {Object} [options]
+ * @param {Function} fn The function to register for "name"
+ * @param {never} error Unused
+ * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
+ * @returns {Kareem}
+ */
+Kareem.prototype.pre = function(name, options, fn, error, unshift) {
+  if (typeof options === 'function') {
+    fn = options;
+    options = {};
+  } else if (options == null) {
+    options = {};
+  }
+ 
+  const pres = this._pres.get(name) || [];
+  this._pres.set(name, pres);
+ 
+  if (typeof fn !== 'function') {
+    throw new Error('pre() requires a function, got "' + typeof fn + '"');
+  }
+ 
+  if (unshift) {
+    pres.unshift(Object.assign({}, options, { fn: fn }));
+  } else {
+    pres.push(Object.assign({}, options, { fn: fn }));
+  }
+ 
+  return this;
+};
+ 
+/**
+ * Register a new hook for "post"
+ * @param {String} name The name of the hook
+ * @param {Object} [options]
+ * @param {Boolean} [options.errorHandler] Whether this is an error handler
+ * @param {Function} fn The function to register for "name"
+ * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
+ * @returns {Kareem}
+ */
+Kareem.prototype.post = function(name, options, fn, unshift) {
+  const posts = this._posts.get(name) || [];
+ 
+  if (typeof options === 'function') {
+    unshift = !!fn;
+    fn = options;
+    options = {};
+  }
+ 
+  if (typeof fn !== 'function') {
+    throw new Error('post() requires a function, got "' + typeof fn + '"');
+  }
+ 
+  if (unshift) {
+    posts.unshift(Object.assign({}, options, { fn: fn }));
+  } else {
+    posts.push(Object.assign({}, options, { fn: fn }));
+  }
+  this._posts.set(name, posts);
+  return this;
+};
+ 
+/**
+ * Register a new error handler for "name"
+ * @param {String} name The name of the hook
+ * @param {Object} [options]
+ * @param {Function} fn The function to register for "name"
+ * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook
+ * @returns {Kareem}
+ */
+ 
+Kareem.prototype.postError = function postError(name, options, fn, unshift) {
+  Eif (typeof options === 'function') {
+    unshift = !!fn;
+    fn = options;
+    options = {};
+  }
+  return this.post(name, { ...options, errorHandler: true }, fn, unshift);
+};
+ 
+/**
+ * Clone the current instance
+ * @returns {Kareem} The cloned instance
+ */
+Kareem.prototype.clone = function() {
+  const n = new Kareem();
+ 
+  for (const key of this._pres.keys()) {
+    const clone = this._pres.get(key).slice();
+    n._pres.set(key, clone);
+  }
+  for (const key of this._posts.keys()) {
+    n._posts.set(key, this._posts.get(key).slice());
+  }
+ 
+  return n;
+};
+ 
+/**
+ * Merge "other" into self or "clone"
+ * @param {Kareem} other The instance to merge with
+ * @param {Kareem} [clone] The instance to merge onto (if not defined, using "this")
+ * @returns {Kareem} The merged instance
+ */
+Kareem.prototype.merge = function(other, clone) {
+  clone = arguments.length === 1 ? true : clone;
+  const ret = clone ? this.clone() : this;
+ 
+  for (const key of other._pres.keys()) {
+    const sourcePres = ret._pres.get(key) || [];
+    const deduplicated = other._pres.get(key).
+      // Deduplicate based on `fn`
+      filter(p => sourcePres.map(_p => _p.fn).indexOf(p.fn) === -1);
+    const combined = sourcePres.concat(deduplicated);
+    ret._pres.set(key, combined);
+  }
+  for (const key of other._posts.keys()) {
+    const sourcePosts = ret._posts.get(key) || [];
+    const deduplicated = other._posts.get(key).
+      filter(p => sourcePosts.indexOf(p) === -1);
+    ret._posts.set(key, sourcePosts.concat(deduplicated));
+  }
+ 
+  return ret;
+};
+ 
+function isPromiseLike(v) {
+  return (typeof v === 'object' && v !== null && typeof v.then === 'function');
+}
+ 
+function isErrorHandlingMiddleware(post, numArgs) {
+  if (post.errorHandler) {
+    return true;
+  }
+  return post.fn.length === numArgs + 2;
+}
+ 
+module.exports = Kareem;
+ 
+ +
+
+ + + + + + + + \ No newline at end of file diff --git a/backend/node_modules/kareem/coverage/lcov-report/prettify.css b/backend/node_modules/kareem/coverage/lcov-report/prettify.css new file mode 100644 index 0000000..b317a7c --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/prettify.css @@ -0,0 +1 @@ +.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} diff --git a/backend/node_modules/kareem/coverage/lcov-report/prettify.js b/backend/node_modules/kareem/coverage/lcov-report/prettify.js new file mode 100644 index 0000000..b322523 --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/prettify.js @@ -0,0 +1,2 @@ +/* eslint-disable */ +window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); diff --git a/backend/node_modules/kareem/coverage/lcov-report/sort-arrow-sprite.png b/backend/node_modules/kareem/coverage/lcov-report/sort-arrow-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed68316eb3f65dec9063332d2f69bf3093bbfab GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^>_9Bd!3HEZxJ@+%Qh}Z>jv*C{$p!i!8j}?a+@3A= zIAGwzjijN=FBi!|L1t?LM;Q;gkwn>2cAy-KV{dn nf0J1DIvEHQu*n~6U}x}qyky7vi4|9XhBJ7&`njxgN@xNA8m%nc literal 0 HcmV?d00001 diff --git a/backend/node_modules/kareem/coverage/lcov-report/sorter.js b/backend/node_modules/kareem/coverage/lcov-report/sorter.js new file mode 100644 index 0000000..2bb296a --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov-report/sorter.js @@ -0,0 +1,196 @@ +/* eslint-disable */ +var addSorting = (function() { + 'use strict'; + var cols, + currentSort = { + index: 0, + desc: false + }; + + // returns the summary table element + function getTable() { + return document.querySelector('.coverage-summary'); + } + // returns the thead element of the summary table + function getTableHeader() { + return getTable().querySelector('thead tr'); + } + // returns the tbody element of the summary table + function getTableBody() { + return getTable().querySelector('tbody'); + } + // returns the th element for nth column + function getNthColumn(n) { + return getTableHeader().querySelectorAll('th')[n]; + } + + function onFilterInput() { + const searchValue = document.getElementById('fileSearch').value; + const rows = document.getElementsByTagName('tbody')[0].children; + for (let i = 0; i < rows.length; i++) { + const row = rows[i]; + if ( + row.textContent + .toLowerCase() + .includes(searchValue.toLowerCase()) + ) { + row.style.display = ''; + } else { + row.style.display = 'none'; + } + } + } + + // loads the search box + function addSearchBox() { + var template = document.getElementById('filterTemplate'); + var templateClone = template.content.cloneNode(true); + templateClone.getElementById('fileSearch').oninput = onFilterInput; + template.parentElement.appendChild(templateClone); + } + + // loads all columns + function loadColumns() { + var colNodes = getTableHeader().querySelectorAll('th'), + colNode, + cols = [], + col, + i; + + for (i = 0; i < colNodes.length; i += 1) { + colNode = colNodes[i]; + col = { + key: colNode.getAttribute('data-col'), + sortable: !colNode.getAttribute('data-nosort'), + type: colNode.getAttribute('data-type') || 'string' + }; + cols.push(col); + if (col.sortable) { + col.defaultDescSort = col.type === 'number'; + colNode.innerHTML = + colNode.innerHTML + ''; + } + } + return cols; + } + // attaches a data attribute to every tr element with an object + // of data values keyed by column name + function loadRowData(tableRow) { + var tableCols = tableRow.querySelectorAll('td'), + colNode, + col, + data = {}, + i, + val; + for (i = 0; i < tableCols.length; i += 1) { + colNode = tableCols[i]; + col = cols[i]; + val = colNode.getAttribute('data-value'); + if (col.type === 'number') { + val = Number(val); + } + data[col.key] = val; + } + return data; + } + // loads all row data + function loadData() { + var rows = getTableBody().querySelectorAll('tr'), + i; + + for (i = 0; i < rows.length; i += 1) { + rows[i].data = loadRowData(rows[i]); + } + } + // sorts the table using the data for the ith column + function sortByIndex(index, desc) { + var key = cols[index].key, + sorter = function(a, b) { + a = a.data[key]; + b = b.data[key]; + return a < b ? -1 : a > b ? 1 : 0; + }, + finalSorter = sorter, + tableBody = document.querySelector('.coverage-summary tbody'), + rowNodes = tableBody.querySelectorAll('tr'), + rows = [], + i; + + if (desc) { + finalSorter = function(a, b) { + return -1 * sorter(a, b); + }; + } + + for (i = 0; i < rowNodes.length; i += 1) { + rows.push(rowNodes[i]); + tableBody.removeChild(rowNodes[i]); + } + + rows.sort(finalSorter); + + for (i = 0; i < rows.length; i += 1) { + tableBody.appendChild(rows[i]); + } + } + // removes sort indicators for current column being sorted + function removeSortIndicators() { + var col = getNthColumn(currentSort.index), + cls = col.className; + + cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); + col.className = cls; + } + // adds sort indicators for current column being sorted + function addSortIndicators() { + getNthColumn(currentSort.index).className += currentSort.desc + ? ' sorted-desc' + : ' sorted'; + } + // adds event listeners for all sorter widgets + function enableUI() { + var i, + el, + ithSorter = function ithSorter(i) { + var col = cols[i]; + + return function() { + var desc = col.defaultDescSort; + + if (currentSort.index === i) { + desc = !currentSort.desc; + } + sortByIndex(i, desc); + removeSortIndicators(); + currentSort.index = i; + currentSort.desc = desc; + addSortIndicators(); + }; + }; + for (i = 0; i < cols.length; i += 1) { + if (cols[i].sortable) { + // add the click event handler on the th so users + // dont have to click on those tiny arrows + el = getNthColumn(i).querySelector('.sorter').parentElement; + if (el.addEventListener) { + el.addEventListener('click', ithSorter(i)); + } else { + el.attachEvent('onclick', ithSorter(i)); + } + } + } + } + // adds sorting functionality to the UI + return function() { + if (!getTable()) { + return; + } + cols = loadColumns(); + loadData(); + addSearchBox(); + addSortIndicators(); + enableUI(); + }; +})(); + +window.addEventListener('load', addSorting); diff --git a/backend/node_modules/kareem/coverage/lcov.info b/backend/node_modules/kareem/coverage/lcov.info new file mode 100644 index 0000000..10b3f24 --- /dev/null +++ b/backend/node_modules/kareem/coverage/lcov.info @@ -0,0 +1,423 @@ +TN: +SF:index.js +FN:6,Kareem +FN:11,skipWrappedFunction +FN:19,overwriteResult +FN:34,execPre +FN:49,(anonymous_4) +FN:53,(anonymous_5) +FN:103,(anonymous_6) +FN:120,execPost +FN:157,(anonymous_8) +FN:161,nextCallback +FN:234,(anonymous_10) +FN:254,(anonymous_11) +FN:256,syncWrapper +FN:276,wrap +FN:304,(anonymous_14) +FN:310,(anonymous_15) +FN:324,(anonymous_16) +FN:343,(anonymous_17) +FN:355,(anonymous_18) +FN:361,kareemWrappedFunction +FN:376,(anonymous_20) +FN:409,(anonymous_21) +FN:440,postError +FN:453,(anonymous_23) +FN:473,(anonymous_24) +FN:481,(anonymous_25) +FN:481,(anonymous_26) +FN:488,(anonymous_27) +FN:495,isPromiseLike +FN:499,isErrorHandlingMiddleware +FNF:30 +FNH:30 +FNDA:62,Kareem +FNDA:6,skipWrappedFunction +FNDA:12,overwriteResult +FNDA:27,execPre +FNDA:27,(anonymous_4) +FNDA:27,(anonymous_5) +FNDA:4,(anonymous_6) +FNDA:27,execPost +FNDA:41,(anonymous_8) +FNDA:24,nextCallback +FNDA:5,(anonymous_10) +FNDA:2,(anonymous_11) +FNDA:2,syncWrapper +FNDA:17,wrap +FNDA:2,(anonymous_14) +FNDA:6,(anonymous_15) +FNDA:6,(anonymous_16) +FNDA:2,(anonymous_17) +FNDA:1,(anonymous_18) +FNDA:1,kareemWrappedFunction +FNDA:51,(anonymous_20) +FNDA:60,(anonymous_21) +FNDA:1,postError +FNDA:4,(anonymous_23) +FNDA:1,(anonymous_24) +FNDA:1,(anonymous_25) +FNDA:1,(anonymous_26) +FNDA:1,(anonymous_27) +FNDA:61,isPromiseLike +FNDA:41,isErrorHandlingMiddleware +DA:7,62 +DA:8,62 +DA:11,1 +DA:12,6 +DA:13,3 +DA:16,3 +DA:19,1 +DA:20,12 +DA:21,6 +DA:24,6 +DA:34,1 +DA:35,27 +DA:36,27 +DA:37,27 +DA:38,27 +DA:40,27 +DA:41,3 +DA:44,24 +DA:45,24 +DA:46,33 +DA:47,27 +DA:48,27 +DA:49,27 +DA:50,27 +DA:51,27 +DA:53,27 +DA:54,27 +DA:55,27 +DA:56,19 +DA:57,0 +DA:59,19 +DA:62,27 +DA:63,27 +DA:64,27 +DA:65,0 +DA:67,27 +DA:70,9 +DA:71,3 +DA:72,3 +DA:74,6 +DA:77,6 +DA:78,6 +DA:80,2 +DA:81,0 +DA:82,0 +DA:84,2 +DA:89,16 +DA:91,16 +DA:92,3 +DA:103,1 +DA:104,4 +DA:105,4 +DA:107,4 +DA:108,4 +DA:120,1 +DA:121,27 +DA:122,27 +DA:124,27 +DA:125,27 +DA:126,6 +DA:129,27 +DA:130,1 +DA:131,0 +DA:133,1 +DA:136,26 +DA:137,41 +DA:138,41 +DA:139,41 +DA:140,41 +DA:141,41 +DA:142,40 +DA:143,40 +DA:144,40 +DA:148,41 +DA:149,1 +DA:150,1 +DA:151,1 +DA:157,41 +DA:158,41 +DA:159,41 +DA:161,41 +DA:162,24 +DA:163,12 +DA:165,12 +DA:169,41 +DA:170,14 +DA:171,8 +DA:172,8 +DA:173,8 +DA:174,1 +DA:175,7 +DA:177,6 +DA:180,5 +DA:181,0 +DA:182,0 +DA:184,5 +DA:187,6 +DA:190,27 +DA:192,1 +DA:194,26 +DA:195,26 +DA:196,26 +DA:197,26 +DA:198,3 +DA:199,23 +DA:201,18 +DA:204,7 +DA:205,2 +DA:206,2 +DA:208,5 +DA:209,5 +DA:212,19 +DA:213,2 +DA:214,2 +DA:220,26 +DA:221,11 +DA:224,15 +DA:234,1 +DA:235,5 +DA:236,5 +DA:238,5 +DA:239,6 +DA:240,6 +DA:241,2 +DA:245,5 +DA:254,1 +DA:255,2 +DA:256,2 +DA:257,2 +DA:259,2 +DA:261,2 +DA:263,2 +DA:276,1 +DA:278,17 +DA:279,17 +DA:280,17 +DA:282,7 +DA:283,2 +DA:284,2 +DA:286,5 +DA:290,12 +DA:291,10 +DA:294,9 +DA:296,7 +DA:304,1 +DA:305,2 +DA:307,2 +DA:308,2 +DA:309,4 +DA:310,6 +DA:313,4 +DA:314,1 +DA:315,1 +DA:318,3 +DA:321,2 +DA:322,2 +DA:323,4 +DA:324,6 +DA:327,4 +DA:328,1 +DA:329,1 +DA:332,3 +DA:335,2 +DA:343,1 +DA:344,2 +DA:355,1 +DA:356,1 +DA:357,1 +DA:359,0 +DA:361,1 +DA:362,1 +DA:363,1 +DA:376,1 +DA:377,51 +DA:378,43 +DA:379,43 +DA:380,8 +DA:381,1 +DA:384,51 +DA:385,51 +DA:387,51 +DA:388,1 +DA:391,50 +DA:392,2 +DA:394,48 +DA:397,50 +DA:409,1 +DA:410,60 +DA:412,60 +DA:413,52 +DA:414,52 +DA:415,52 +DA:418,60 +DA:419,1 +DA:422,59 +DA:423,2 +DA:425,57 +DA:427,59 +DA:428,59 +DA:440,1 +DA:441,1 +DA:442,1 +DA:443,1 +DA:444,1 +DA:446,1 +DA:453,1 +DA:454,4 +DA:456,4 +DA:457,6 +DA:458,6 +DA:460,4 +DA:461,5 +DA:464,4 +DA:473,1 +DA:474,1 +DA:475,1 +DA:477,1 +DA:478,1 +DA:479,1 +DA:481,1 +DA:482,1 +DA:483,1 +DA:485,1 +DA:486,1 +DA:487,1 +DA:488,1 +DA:489,1 +DA:492,1 +DA:496,61 +DA:500,41 +DA:501,2 +DA:503,39 +DA:506,1 +LF:233 +LH:225 +BRDA:12,0,0,3 +BRDA:12,0,1,3 +BRDA:20,1,0,6 +BRDA:20,1,1,6 +BRDA:35,2,0,27 +BRDA:35,2,1,3 +BRDA:40,3,0,3 +BRDA:40,3,1,24 +BRDA:46,4,0,27 +BRDA:46,4,1,6 +BRDA:53,5,0,9 +BRDA:53,5,1,18 +BRDA:56,6,0,0 +BRDA:56,6,1,19 +BRDA:56,7,0,19 +BRDA:56,7,1,19 +BRDA:64,8,0,0 +BRDA:64,8,1,27 +BRDA:70,9,0,3 +BRDA:70,9,1,6 +BRDA:80,10,0,0 +BRDA:80,10,1,2 +BRDA:91,11,0,3 +BRDA:91,11,1,13 +BRDA:104,12,0,4 +BRDA:104,12,1,1 +BRDA:108,13,0,4 +BRDA:108,13,1,2 +BRDA:121,14,0,27 +BRDA:121,14,1,1 +BRDA:125,15,0,6 +BRDA:125,15,1,21 +BRDA:125,16,0,27 +BRDA:125,16,1,10 +BRDA:129,17,0,1 +BRDA:129,17,1,26 +BRDA:130,18,0,0 +BRDA:130,18,1,1 +BRDA:142,19,0,40 +BRDA:142,19,1,0 +BRDA:142,20,0,40 +BRDA:142,20,1,37 +BRDA:148,21,0,1 +BRDA:148,21,1,40 +BRDA:162,22,0,12 +BRDA:162,22,1,12 +BRDA:169,23,0,14 +BRDA:169,23,1,27 +BRDA:170,24,0,8 +BRDA:170,24,1,6 +BRDA:173,25,0,1 +BRDA:173,25,1,7 +BRDA:175,26,0,6 +BRDA:175,26,1,1 +BRDA:180,27,0,0 +BRDA:180,27,1,5 +BRDA:190,28,0,1 +BRDA:190,28,1,26 +BRDA:197,29,0,3 +BRDA:197,29,1,23 +BRDA:199,30,0,18 +BRDA:199,30,1,5 +BRDA:204,31,0,2 +BRDA:204,31,1,5 +BRDA:212,32,0,2 +BRDA:212,32,1,17 +BRDA:220,33,0,11 +BRDA:220,33,1,15 +BRDA:235,34,0,5 +BRDA:235,34,1,1 +BRDA:239,35,0,6 +BRDA:239,35,1,2 +BRDA:240,36,0,2 +BRDA:240,36,1,4 +BRDA:282,37,0,2 +BRDA:282,37,1,5 +BRDA:290,38,0,10 +BRDA:290,38,1,2 +BRDA:313,39,0,1 +BRDA:313,39,1,3 +BRDA:327,40,0,1 +BRDA:327,40,1,3 +BRDA:344,41,0,2 +BRDA:344,41,1,1 +BRDA:357,42,0,0 +BRDA:357,42,1,1 +BRDA:362,43,0,1 +BRDA:362,43,1,0 +BRDA:377,44,0,43 +BRDA:377,44,1,8 +BRDA:380,45,0,1 +BRDA:380,45,1,7 +BRDA:384,46,0,51 +BRDA:384,46,1,35 +BRDA:387,47,0,1 +BRDA:387,47,1,50 +BRDA:391,48,0,2 +BRDA:391,48,1,48 +BRDA:410,49,0,60 +BRDA:410,49,1,40 +BRDA:412,50,0,52 +BRDA:412,50,1,8 +BRDA:418,51,0,1 +BRDA:418,51,1,59 +BRDA:422,52,0,2 +BRDA:422,52,1,57 +BRDA:441,53,0,1 +BRDA:441,53,1,0 +BRDA:474,54,0,1 +BRDA:474,54,1,0 +BRDA:475,55,0,1 +BRDA:475,55,1,0 +BRDA:478,56,0,1 +BRDA:478,56,1,0 +BRDA:486,57,0,1 +BRDA:486,57,1,1 +BRDA:496,58,0,61 +BRDA:496,58,1,5 +BRDA:496,58,2,5 +BRDA:500,59,0,2 +BRDA:500,59,1,39 +BRF:121 +BRH:109 +end_of_record diff --git a/backend/node_modules/kareem/index.d.ts b/backend/node_modules/kareem/index.d.ts new file mode 100644 index 0000000..3688c1d --- /dev/null +++ b/backend/node_modules/kareem/index.d.ts @@ -0,0 +1,31 @@ +declare module "kareem" { + export default class Kareem { + static skipWrappedFunction(): SkipWrappedFunction; + static overwriteMiddlewareResult(): OverwriteMiddlewareResult; + static overwriteArguments(): OverwriteArguments; + + pre(name: string | RegExp, fn: Function): this; + pre(name: string | RegExp, options: Record, fn: Function, error?: any, unshift?: boolean): this; + post(name: string | RegExp, fn: Function): this; + post(name: string | RegExp, options: Record, fn: Function, unshift?: boolean): this; + + clone(): Kareem; + merge(other: Kareem, clone?: boolean): this; + + createWrapper(name: string, fn: Function, context?: any, options?: Record): Function; + createWrapperSync(name: string, fn: Function): Function; + hasHooks(name: string): boolean; + filter(fn: Function): Kareem; + + wrap(name: string, fn: Function, context: any, args: any[], options?: Record): Function; + + execPostSync(name: string, context: any, args: any[]): any; + execPost(name: string, context: any, args: any[], options?: Record, callback?: Function): void; + execPreSync(name: string, context: any, args: any[]): any; + execPre(name: string, context: any, args: any[], callback?: Function): void; + } + + class SkipWrappedFunction {} + class OverwriteMiddlewareResult {} + class OverwriteArguments {} +} diff --git a/backend/node_modules/kareem/index.js b/backend/node_modules/kareem/index.js new file mode 100644 index 0000000..4e1ac7b --- /dev/null +++ b/backend/node_modules/kareem/index.js @@ -0,0 +1,539 @@ +'use strict'; + +/** + * Create a new instance + */ +function Kareem() { + this._pres = new Map(); + this._posts = new Map(); +} + +Kareem.skipWrappedFunction = function skipWrappedFunction() { + if (!(this instanceof Kareem.skipWrappedFunction)) { + return new Kareem.skipWrappedFunction(...arguments); + } + + this.args = [...arguments]; +}; + +Kareem.overwriteResult = function overwriteResult() { + if (!(this instanceof Kareem.overwriteResult)) { + return new Kareem.overwriteResult(...arguments); + } + + this.args = [...arguments]; +}; + +Kareem.overwriteArguments = function overwriteArguments() { + if (!(this instanceof Kareem.overwriteArguments)) { + return new Kareem.overwriteArguments(...arguments); + } + + this.args = [...arguments]; +}; + +/** + * Execute all "pre" hooks for "name" + * @param {String} name The hook name to execute + * @param {*} context Overwrite the "this" for the hook + * @param {Array} args arguments passed to the pre hooks + * @param {Object} [options] Optional options + * @param {Function} [options.filter] Filter function to select which hooks to run + * @returns {Array} The potentially modified arguments + */ +Kareem.prototype.execPre = async function execPre(name, context, args, options) { + let pres = this._pres.get(name) || []; + if (options?.filter) { + pres = pres.filter(options.filter); + } + const numPres = pres.length; + let $args = args; + let skipWrappedFunction = null; + + if (!numPres) { + return $args; + } + + for (const pre of pres) { + const args = []; + const _args = [null].concat($args); + for (let i = 1; i < _args.length; ++i) { + if (i === _args.length - 1 && typeof _args[i] === 'function') { + continue; // skip callbacks to avoid accidentally calling the callback from a hook + } + args.push(_args[i]); + } + + try { + const maybePromiseLike = pre.fn.apply(context, args); + if (isPromiseLike(maybePromiseLike)) { + const result = await maybePromiseLike; + if (result instanceof Kareem.overwriteArguments) { + $args = result.args; + } + } else if (maybePromiseLike instanceof Kareem.overwriteArguments) { + $args = maybePromiseLike.args; + } + } catch (error) { + if (error instanceof Kareem.skipWrappedFunction) { + skipWrappedFunction = error; + continue; + } + if (error instanceof Kareem.overwriteArguments) { + $args = error.args; + continue; + } + throw error; + } + } + + if (skipWrappedFunction) { + throw skipWrappedFunction; + } + + return $args; +}; + +/** + * Execute all "pre" hooks for "name" synchronously + * @param {String} name The hook name to execute + * @param {*} context Overwrite the "this" for the hook + * @param {Array} [args] Apply custom arguments to the hook + * @param {Object} [options] Optional options + * @param {Function} [options.filter] Filter function to select which hooks to run + * @returns {Array} The potentially modified arguments + */ +Kareem.prototype.execPreSync = function(name, context, args, options) { + let pres = this._pres.get(name) || []; + if (options?.filter) { + pres = pres.filter(options.filter); + } + const numPres = pres.length; + let $args = args || []; + + for (let i = 0; i < numPres; ++i) { + const result = pres[i].fn.apply(context, $args); + if (result instanceof Kareem.overwriteArguments) { + $args = result.args; + } + } + + return $args; +}; + +/** + * Execute all "post" hooks for "name" + * @param {String} name The hook name to execute + * @param {*} context Overwrite the "this" for the hook + * @param {Array} args Apply custom arguments to the hook + * @param {Object} [options] Optional options + * @param {Error} [options.error] Error to pass to error-handling middleware + * @param {Function} [options.filter] Filter function to select which hooks to run + * @returns {void} + */ +Kareem.prototype.execPost = async function execPost(name, context, args, options) { + let posts = this._posts.get(name) || []; + if (options?.filter) { + posts = posts.filter(options.filter); + } + const numPosts = posts.length; + + let firstError = null; + if (options && options.error) { + firstError = options.error; + } + + if (!numPosts) { + if (firstError != null) { + throw firstError; + } + return args; + } + + for (const currentPost of posts) { + const post = currentPost.fn; + let numArgs = 0; + const newArgs = []; + const argLength = args.length; + for (let i = 0; i < argLength; ++i) { + if (!args[i] || !args[i]._kareemIgnore) { + numArgs += 1; + newArgs.push(args[i]); + } + } + // If numCallbackParams set, fill in the rest with null to enforce consistent number of args + if (options?.numCallbackParams != null) { + numArgs = options.numCallbackParams; + for (let i = newArgs.length; i < numArgs; ++i) { + newArgs.push(null); + } + } + + let resolve; + let reject; + const cbPromise = new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + newArgs.push(function nextCallback(err) { + if (err) { + reject(err); + } else { + resolve(); + } + }); + + if (firstError) { + if (isErrorHandlingMiddleware(currentPost, numArgs)) { + try { + const res = post.apply(context, [firstError].concat(newArgs)); + if (isPromiseLike(res)) { + await res; + } else if (post.length === numArgs + 2) { + // `numArgs + 2` because we added the error and the callback + await cbPromise; + } + } catch (error) { + if (error instanceof Kareem.overwriteResult) { + args = error.args; + continue; + } + firstError = error; + } + } else { + continue; + } + } else { + if (isErrorHandlingMiddleware(currentPost, numArgs)) { + // Skip error handlers if no error + continue; + } else { + let res = null; + try { + res = post.apply(context, newArgs); + if (isPromiseLike(res)) { + res = await res; + } else if (post.length === numArgs + 1) { + // If post function takes a callback, wait for the post function to call the callback + res = await cbPromise; + } + } catch (error) { + if (error instanceof Kareem.overwriteResult) { + args = error.args; + continue; + } + firstError = error; + continue; + } + + if (res instanceof Kareem.overwriteResult) { + args = res.args; + continue; + } + } + } + } + + if (firstError != null) { + throw firstError; + } + + return args; +}; + +/** + * Execute all "post" hooks for "name" synchronously + * @param {String} name The hook name to execute + * @param {*} context Overwrite the "this" for the hook + * @param {Array} args Apply custom arguments to the hook + * @param {Object} [options] Optional options + * @param {Function} [options.filter] Filter function to select which hooks to run + * @returns {Array} The used arguments + */ +Kareem.prototype.execPostSync = function(name, context, args, options) { + let posts = this._posts.get(name) || []; + if (options?.filter) { + posts = posts.filter(options.filter); + } + const numPosts = posts.length; + + for (let i = 0; i < numPosts; ++i) { + const res = posts[i].fn.apply(context, args || []); + if (res instanceof Kareem.overwriteResult) { + args = res.args; + } + } + + return args; +}; + +/** + * Create a synchronous wrapper for "fn" + * @param {String} name The name of the hook + * @param {Function} fn The function to wrap + * @param {*} context Overwrite the "this" for the hook. If null/undefined, uses the calling context. + * @param {Object} [options] Options for the wrapper + * @param {Function} [options.getOptions] Function that receives the wrapper arguments and returns options for execPreSync/execPostSync. Can return `{ filter }` for both, or `{ pre: { filter }, post: { filter } }` for separate options. + * @returns {Function} The wrapped function + */ +Kareem.prototype.createWrapperSync = function(name, fn, context, options) { + const _this = this; + const getOptions = options?.getOptions; + return function syncWrapper() { + const _context = context || this; + const args = Array.from(arguments); + const execOptions = typeof getOptions === 'function' ? getOptions(args) : {}; + const preOptions = execOptions.pre ?? execOptions; + const postOptions = execOptions.post ?? execOptions; + + const modifiedArgs = _this.execPreSync(name, _context, args, preOptions); + + const toReturn = fn.apply(_context, modifiedArgs); + + const result = _this.execPostSync(name, _context, [toReturn], postOptions); + + return result[0]; + }; +}; + +/** + * Executes pre hooks, followed by the wrapped function, followed by post hooks. + * @param {String} name The name of the hook + * @param {Function} fn The function for the hook + * @param {*} context Overwrite the "this" for the hook + * @param {Array} args Apply custom arguments to the hook + * @param {Object} options Additional options for the hook + * @returns {void} + */ +Kareem.prototype.wrap = async function wrap(name, fn, context, args, options) { + let ret; + let skipWrappedFunction = false; + let modifiedArgs = args; + try { + modifiedArgs = await this.execPre(name, context, args); + } catch (error) { + if (error instanceof Kareem.skipWrappedFunction) { + ret = error.args; + skipWrappedFunction = true; + } else { + await this.execPost(name, context, args, { ...options, error }); + } + } + + if (!skipWrappedFunction) { + ret = await fn.apply(context, modifiedArgs); + } + + ret = await this.execPost(name, context, [ret], options); + + return ret[0]; +}; + +/** + * Filter current instance for something specific and return the filtered clone + * @param {Function} fn The filter function + * @returns {Kareem} The cloned and filtered instance + */ +Kareem.prototype.filter = function(fn) { + const clone = this.clone(); + + const pres = Array.from(clone._pres.keys()); + for (const name of pres) { + const hooks = this._pres.get(name). + map(h => Object.assign({}, h, { name: name })). + filter(fn); + + if (hooks.length === 0) { + clone._pres.delete(name); + continue; + } + + clone._pres.set(name, hooks); + } + + const posts = Array.from(clone._posts.keys()); + for (const name of posts) { + const hooks = this._posts.get(name). + map(h => Object.assign({}, h, { name: name })). + filter(fn); + + if (hooks.length === 0) { + clone._posts.delete(name); + continue; + } + + clone._posts.set(name, hooks); + } + + return clone; +}; + +/** + * Check for a "name" to exist either in pre or post hooks + * @param {String} name The name of the hook + * @returns {Boolean} "true" if found, "false" otherwise + */ +Kareem.prototype.hasHooks = function(name) { + return this._pres.has(name) || this._posts.has(name); +}; + +/** + * Create a Wrapper for "fn" on "name" and return the wrapped function + * @param {String} name The name of the hook + * @param {Function} fn The function to wrap + * @param {*} context Overwrite the "this" for the hook + * @param {Object} [options] + * @returns {Function} The wrapped function + */ +Kareem.prototype.createWrapper = function(name, fn, context, options) { + const _this = this; + if (!this.hasHooks(name)) { + // Fast path: if there's no hooks for this function, just return the function + return fn; + } + return function kareemWrappedFunction() { + const _context = context || this; + return _this.wrap(name, fn, _context, Array.from(arguments), options); + }; +}; + +/** + * Register a new hook for "pre" + * @param {String} name The name of the hook + * @param {Object} [options] + * @param {Function} fn The function to register for "name" + * @param {never} error Unused + * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook + * @returns {Kareem} + */ +Kareem.prototype.pre = function(name, options, fn, error, unshift) { + if (typeof options === 'function') { + fn = options; + options = {}; + } else if (options == null) { + options = {}; + } + + const pres = this._pres.get(name) || []; + this._pres.set(name, pres); + + if (typeof fn !== 'function') { + throw new Error('pre() requires a function, got "' + typeof fn + '"'); + } + + if (unshift) { + pres.unshift(Object.assign({}, options, { fn: fn })); + } else { + pres.push(Object.assign({}, options, { fn: fn })); + } + + return this; +}; + +/** + * Register a new hook for "post" + * @param {String} name The name of the hook + * @param {Object} [options] + * @param {Boolean} [options.errorHandler] Whether this is an error handler + * @param {Function} fn The function to register for "name" + * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook + * @returns {Kareem} + */ +Kareem.prototype.post = function(name, options, fn, unshift) { + const posts = this._posts.get(name) || []; + + if (typeof options === 'function') { + unshift = !!fn; + fn = options; + options = {}; + } + + if (typeof fn !== 'function') { + throw new Error('post() requires a function, got "' + typeof fn + '"'); + } + + if (unshift) { + posts.unshift(Object.assign({}, options, { fn: fn })); + } else { + posts.push(Object.assign({}, options, { fn: fn })); + } + this._posts.set(name, posts); + return this; +}; + +/** + * Register a new error handler for "name" + * @param {String} name The name of the hook + * @param {Object} [options] + * @param {Function} fn The function to register for "name" + * @param {Boolean} [unshift] Wheter to "push" or to "unshift" the new hook + * @returns {Kareem} + */ + +Kareem.prototype.postError = function postError(name, options, fn, unshift) { + if (typeof options === 'function') { + unshift = !!fn; + fn = options; + options = {}; + } + return this.post(name, { ...options, errorHandler: true }, fn, unshift); +}; + +/** + * Clone the current instance + * @returns {Kareem} The cloned instance + */ +Kareem.prototype.clone = function() { + const n = new Kareem(); + + for (const key of this._pres.keys()) { + const clone = this._pres.get(key).slice(); + n._pres.set(key, clone); + } + for (const key of this._posts.keys()) { + n._posts.set(key, this._posts.get(key).slice()); + } + + return n; +}; + +/** + * Merge "other" into self or "clone" + * @param {Kareem} other The instance to merge with + * @param {Kareem} [clone] The instance to merge onto (if not defined, using "this") + * @returns {Kareem} The merged instance + */ +Kareem.prototype.merge = function(other, clone) { + clone = arguments.length === 1 ? true : clone; + const ret = clone ? this.clone() : this; + + for (const key of other._pres.keys()) { + const sourcePres = ret._pres.get(key) || []; + const deduplicated = other._pres.get(key). + // Deduplicate based on `fn` + filter(p => sourcePres.map(_p => _p.fn).indexOf(p.fn) === -1); + const combined = sourcePres.concat(deduplicated); + ret._pres.set(key, combined); + } + for (const key of other._posts.keys()) { + const sourcePosts = ret._posts.get(key) || []; + const deduplicated = other._posts.get(key). + filter(p => sourcePosts.indexOf(p) === -1); + ret._posts.set(key, sourcePosts.concat(deduplicated)); + } + + return ret; +}; + +function isPromiseLike(v) { + return (typeof v === 'object' && v !== null && typeof v.then === 'function'); +} + +function isErrorHandlingMiddleware(post, numArgs) { + if (post.errorHandler) { + return true; + } + return post.fn.length === numArgs + 2; +} + +module.exports = Kareem; diff --git a/backend/node_modules/kareem/package.json b/backend/node_modules/kareem/package.json new file mode 100644 index 0000000..b789f25 --- /dev/null +++ b/backend/node_modules/kareem/package.json @@ -0,0 +1,28 @@ +{ + "name": "kareem", + "version": "3.2.0", + "description": "Next-generation take on pre/post function hooks", + "main": "index.js", + "scripts": { + "lint": "eslint .", + "test": "mocha ./test/*", + "test-coverage": "nyc --reporter lcov mocha ./test/*", + "docs": "node ./docs.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/mongoosejs/kareem.git" + }, + "devDependencies": { + "acquit": "1.x", + "acquit-ignore": "0.2.x", + "eslint": "8.20.0", + "mocha": "11.x", + "nyc": "15.1.0" + }, + "author": "Valeri Karpov ", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } +} diff --git a/backend/node_modules/mongoose/LICENSE.md b/backend/node_modules/mongoose/LICENSE.md new file mode 100644 index 0000000..54b4a4c --- /dev/null +++ b/backend/node_modules/mongoose/LICENSE.md @@ -0,0 +1,22 @@ +# MIT License + +Copyright (c) 2010-2013 LearnBoost +Copyright (c) 2013-2021 Automattic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/backend/node_modules/mongoose/README.md b/backend/node_modules/mongoose/README.md new file mode 100644 index 0000000..cc5b360 --- /dev/null +++ b/backend/node_modules/mongoose/README.md @@ -0,0 +1,395 @@ +# Mongoose + +Mongoose is a [MongoDB](https://www.mongodb.org/) object modeling tool designed to work in an asynchronous environment. Mongoose supports [Node.js](https://nodejs.org/en/) and [Deno](https://deno.land/) (alpha). + +[![Build Status](https://github.com/Automattic/mongoose/workflows/Test/badge.svg)](https://github.com/Automattic/mongoose) +[![NPM version](https://badge.fury.io/js/mongoose.svg)](http://badge.fury.io/js/mongoose) +[![Deno version](https://deno.land/badge/mongoose/version)](https://deno.land/x/mongoose) +[![Deno popularity](https://deno.land/badge/mongoose/popularity)](https://deno.land/x/mongoose) + +[![npm](https://nodei.co/npm/mongoose.png)](https://www.npmjs.com/package/mongoose) + +## Documentation + +The official documentation website is [mongoosejs.com](https://mongoosejs.com/). + +Mongoose 9.0.0 was released on November 21, 2025. You can find more details on [backwards breaking changes in 9.0.0 on our docs site](https://mongoosejs.com/docs/migrating_to_9.html). + +## Support + +* [Stack Overflow](http://stackoverflow.com/questions/tagged/mongoose) +* [Bug Reports](https://github.com/Automattic/mongoose/issues/) +* [Mongoose Slack Channel](http://slack.mongoosejs.io/) +* [Help Forum](http://groups.google.com/group/mongoose-orm) +* [MongoDB Support](https://www.mongodb.com/docs/manual/support/) + +## Plugins + +Check out the [plugins search site](https://plugins.mongoosejs.io/) to see hundreds of related modules from the community. Next, learn how to write your own plugin from the [docs](https://mongoosejs.com/docs/plugins.html) or [this blog post](http://thecodebarbarian.com/2015/03/06/guide-to-mongoose-plugins). + +## Contributors + +Pull requests are always welcome! Please base pull requests against the `master` +branch and follow the [contributing guide](https://github.com/Automattic/mongoose/blob/master/CONTRIBUTING.md). + +If your pull requests makes documentation changes, please do **not** +modify any `.html` files. The `.html` files are compiled code, so please make +your changes in `docs/*.pug`, `lib/*.js`, or `test/docs/*.js`. + +View all 400+ [contributors](https://github.com/Automattic/mongoose/graphs/contributors). + +## Installation + +First install [Node.js](https://nodejs.org/) and [MongoDB](https://www.mongodb.org/downloads), then install the `mongoose` package using your preferred package manager: + +### Using npm + +```sh +npm install mongoose +``` + +### Using pnpm + +```sh +pnpm add mongoose +``` + +### Using Yarn + +```sh +yarn add mongoose +``` + +### Using Bun + +```sh +bun add mongoose +``` + +Mongoose 6.8.0 also includes alpha support for [Deno](https://deno.land/). + +## Importing + +```javascript +// Using Node.js `require()` +const mongoose = require('mongoose'); + +// Using ES6 imports +import mongoose from 'mongoose'; +``` + +Or, using [Deno's `createRequire()` for CommonJS support](https://deno.land/std@0.113.0/node/README.md?source=#commonjs-modules-loading) as follows. + +```javascript +import { createRequire } from 'https://deno.land/std@0.177.0/node/module.ts'; +const require = createRequire(import.meta.url); + +const mongoose = require('mongoose'); + +mongoose.connect('mongodb://127.0.0.1:27017/test') + .then(() => console.log('Connected!')); +``` + +You can then run the above script using the following. + +```sh +deno run --allow-net --allow-read --allow-sys --allow-env mongoose-test.js +``` + +## Mongoose for Enterprise + +Available as part of the Tidelift Subscription + +The maintainers of mongoose and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-mongoose?utm_source=npm-mongoose&utm_medium=referral&utm_campaign=enterprise&utm_term=repo) + +## Overview + +### Connecting to MongoDB + +First, we need to define a connection. If your app uses only one database, you should use `mongoose.connect`. If you need to create additional connections, use `mongoose.createConnection`. + +Both `connect` and `createConnection` take a `mongodb://` URI, or the parameters `host, database, port, options`. + +```js +await mongoose.connect('mongodb://127.0.0.1/my_database'); +``` + +Once connected, the `open` event is fired on the `Connection` instance. If you're using `mongoose.connect`, the `Connection` is `mongoose.connection`. Otherwise, `mongoose.createConnection` return value is a `Connection`. + +**Note:** *If the local connection fails then try using 127.0.0.1 instead of localhost. Sometimes issues may arise when the local hostname has been changed.* + +**Important!** Mongoose buffers all the commands until it's connected to the database. This means that you don't have to wait until it connects to MongoDB in order to define models, run queries, etc. + +### Defining a Model + +Models are defined through the `Schema` interface. + +```js +const Schema = mongoose.Schema; +const ObjectId = Schema.ObjectId; + +const BlogPost = new Schema({ + author: ObjectId, + title: String, + body: String, + date: Date +}); +``` + +Aside from defining the structure of your documents and the types of data you're storing, a Schema handles the definition of: + +* [Validators](https://mongoosejs.com/docs/validation.html) (async and sync) +* [Defaults](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-default) +* [Getters](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-get) +* [Setters](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-set) +* [Indexes](https://mongoosejs.com/docs/guide.html#indexes) +* [Middleware](https://mongoosejs.com/docs/middleware.html) +* [Methods](https://mongoosejs.com/docs/guide.html#methods) definition +* [Statics](https://mongoosejs.com/docs/guide.html#statics) definition +* [Plugins](https://mongoosejs.com/docs/plugins.html) +* [pseudo-JOINs](https://mongoosejs.com/docs/populate.html) + +The following example shows some of these features: + +```js +const Comment = new Schema({ + name: { type: String, default: 'hahaha' }, + age: { type: Number, min: 18, index: true }, + bio: { type: String, match: /[a-z]/ }, + date: { type: Date, default: Date.now }, + buff: Buffer +}); + +// a setter +Comment.path('name').set(function(v) { + return capitalize(v); +}); + +// middleware +Comment.pre('save', function(next) { + notify(this.get('email')); + next(); +}); +``` + +Take a look at the example in [`examples/schema/schema.js`](https://github.com/Automattic/mongoose/blob/master/examples/schema/schema.js) for an end-to-end example of a typical setup. + +### Accessing a Model + +Once we define a model through `mongoose.model('ModelName', mySchema)`, we can access it through the same function + +```js +const MyModel = mongoose.model('ModelName'); +``` + +Or just do it all at once + +```js +const MyModel = mongoose.model('ModelName', mySchema); +``` + +The first argument is the *singular* name of the collection your model is for. **Mongoose automatically looks for the *plural* version of your model name.** For example, if you use + +```js +const MyModel = mongoose.model('Ticket', mySchema); +``` + +Then `MyModel` will use the **tickets** collection, not the **ticket** collection. For more details read the [model docs](https://mongoosejs.com/docs/api/mongoose.html#mongoose_Mongoose-model). + +Once we have our model, we can then instantiate it, and save it: + +```js +const instance = new MyModel(); +instance.my.key = 'hello'; +await instance.save(); +``` + +Or we can find documents from the same collection + +```js +await MyModel.find({}); +``` + +You can also `findOne`, `findById`, `update`, etc. + +```js +const instance = await MyModel.findOne({ /* ... */ }); +console.log(instance.my.key); // 'hello' +``` + +For more details check out [the docs](https://mongoosejs.com/docs/queries.html). + +**Important!** If you opened a separate connection using `mongoose.createConnection()` but attempt to access the model through `mongoose.model('ModelName')` it will not work as expected since it is not hooked up to an active db connection. In this case access your model through the connection you created: + +```js +const conn = mongoose.createConnection('your connection string'); +const MyModel = conn.model('ModelName', schema); +const m = new MyModel(); +await m.save(); // works +``` + +vs + +```js +const conn = mongoose.createConnection('your connection string'); +const MyModel = mongoose.model('ModelName', schema); +const m = new MyModel(); +await m.save(); // does not work b/c the default connection object was never connected +``` + +### Embedded Documents + +In the first example snippet, we defined a key in the Schema that looks like: + +```txt +comments: [Comment] +``` + +Where `Comment` is a `Schema` we created. This means that creating embedded documents is as simple as: + +```js +// retrieve my model +const BlogPost = mongoose.model('BlogPost'); + +// create a blog post +const post = new BlogPost(); + +// create a comment +post.comments.push({ title: 'My comment' }); + +await post.save(); +``` + +The same goes for removing them: + +```js +const post = await BlogPost.findById(myId); +post.comments[0].deleteOne(); +await post.save(); +``` + +Embedded documents enjoy all the same features as your models. Defaults, validators, middleware. + +### Middleware + +See the [docs](https://mongoosejs.com/docs/middleware.html) page. + +#### Intercepting and mutating method arguments + +You can intercept method arguments via middleware. + +For example, this would allow you to broadcast changes about your Documents every time someone `set`s a path in your Document to a new value: + +```js +schema.pre('set', function(next, path, val, typel) { + // `this` is the current Document + this.emit('set', path, val); + + // Pass control to the next pre + next(); +}); +``` + +Moreover, you can mutate the incoming `method` arguments so that subsequent middleware see different values for those arguments. To do so, just pass the new values to `next`: + +```js +schema.pre(method, function firstPre(next, methodArg1, methodArg2) { + // Mutate methodArg1 + next('altered-' + methodArg1.toString(), methodArg2); +}); + +// pre declaration is chainable +schema.pre(method, function secondPre(next, methodArg1, methodArg2) { + console.log(methodArg1); + // => 'altered-originalValOfMethodArg1' + + console.log(methodArg2); + // => 'originalValOfMethodArg2' + + // Passing no arguments to `next` automatically passes along the current argument values + // i.e., the following `next()` is equivalent to `next(methodArg1, methodArg2)` + // and also equivalent to, with the example method arg + // values, `next('altered-originalValOfMethodArg1', 'originalValOfMethodArg2')` + next(); +}); +``` + +#### Schema gotcha + +`type`, when used in a schema has special meaning within Mongoose. If your schema requires using `type` as a nested property you must use object notation: + +```js +new Schema({ + broken: { type: Boolean }, + asset: { + name: String, + type: String // uh oh, it broke. asset will be interpreted as String + } +}); + +new Schema({ + works: { type: Boolean }, + asset: { + name: String, + type: { type: String } // works. asset is an object with a type property + } +}); +``` + +### Driver Access + +Mongoose is built on top of the [official MongoDB Node.js driver](https://github.com/mongodb/node-mongodb-native). Each mongoose model keeps a reference to a [native MongoDB driver collection](http://mongodb.github.io/node-mongodb-native/2.1/api/Collection.html). The collection object can be accessed using `YourModel.collection`. However, using the collection object directly bypasses all mongoose features, including hooks, validation, etc. The one +notable exception that `YourModel.collection` still buffers +commands. As such, `YourModel.collection.find()` will **not** +return a cursor. + +## API Docs + +[Mongoose API documentation](https://mongoosejs.com/docs/api/mongoose.html), generated using [dox](https://github.com/tj/dox) +and [acquit](https://github.com/vkarpov15/acquit). + +## Related Projects + +### MongoDB Runners + +* [run-rs](https://www.npmjs.com/package/run-rs) +* [mongodb-memory-server](https://www.npmjs.com/package/mongodb-memory-server) +* [mongodb-topology-manager](https://www.npmjs.com/package/mongodb-topology-manager) + +### Unofficial CLIs + +* [mongoosejs-cli](https://www.npmjs.com/package/mongoosejs-cli) + +### Data Seeding + +* [dookie](https://www.npmjs.com/package/dookie) +* [seedgoose](https://www.npmjs.com/package/seedgoose) +* [mongoose-data-seed](https://www.npmjs.com/package/mongoose-data-seed) + +### Express Session Stores + +* [connect-mongodb-session](https://www.npmjs.com/package/connect-mongodb-session) +* [connect-mongo](https://www.npmjs.com/package/connect-mongo) + +## License + +Copyright (c) 2010 LearnBoost <dev@learnboost.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/mongoose/SECURITY.md b/backend/node_modules/mongoose/SECURITY.md new file mode 100644 index 0000000..41b89d8 --- /dev/null +++ b/backend/node_modules/mongoose/SECURITY.md @@ -0,0 +1 @@ +Please follow the instructions on [Tidelift's security page](https://tidelift.com/docs/security) to report a security issue. diff --git a/backend/node_modules/mongoose/eslint.config.mjs b/backend/node_modules/mongoose/eslint.config.mjs new file mode 100644 index 0000000..29bafed --- /dev/null +++ b/backend/node_modules/mongoose/eslint.config.mjs @@ -0,0 +1,198 @@ +import { defineConfig, globalIgnores } from 'eslint/config'; +import mochaNoOnly from 'eslint-plugin-mocha-no-only'; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; +import js from '@eslint/js'; + +export default defineConfig([ + globalIgnores([ + '**/tools', + '**/dist', + 'test/files/*', + '**/benchmarks', + '**/*.min.js', + '**/docs/js/native.js', + '!**/.*', + '**/node_modules', + '**/.git', + '**/data' + ]), + js.configs.recommended, + // general options + { + languageOptions: { + globals: globals.node, + ecmaVersion: 2022, // nodejs 18.0.0, + sourceType: 'commonjs' + }, + rules: { + 'comma-style': 'error', + + indent: ['error', 2, { + SwitchCase: 1, + VariableDeclarator: 2 + }], + + 'keyword-spacing': 'error', + 'no-whitespace-before-property': 'error', + 'no-buffer-constructor': 'warn', + 'no-console': 'off', + 'no-constant-condition': 'off', + 'no-multi-spaces': 'error', + 'func-call-spacing': 'error', + 'no-trailing-spaces': 'error', + 'no-undef': 'error', + 'no-unneeded-ternary': 'error', + 'no-const-assign': 'error', + 'no-useless-rename': 'error', + 'no-dupe-keys': 'error', + 'space-in-parens': ['error', 'never'], + + 'spaced-comment': ['error', 'always', { + block: { + markers: ['!'], + balanced: true + } + }], + + 'key-spacing': ['error', { + beforeColon: false, + afterColon: true + }], + + 'comma-spacing': ['error', { + before: false, + after: true + }], + + 'array-bracket-spacing': 1, + + 'arrow-spacing': ['error', { + before: true, + after: true + }], + + 'object-curly-spacing': ['error', 'always'], + 'comma-dangle': ['error', 'never'], + 'no-unreachable': 'error', + quotes: ['error', 'single'], + 'quote-props': ['error', 'as-needed'], + semi: 'error', + 'no-extra-semi': 'error', + 'semi-spacing': 'error', + 'no-spaced-func': 'error', + 'no-throw-literal': 'error', + 'space-before-blocks': 'error', + 'space-before-function-paren': ['error', 'never'], + 'space-infix-ops': 'error', + 'space-unary-ops': 'error', + 'no-var': 'warn', + 'prefer-const': 'warn', + strict: ['error', 'global'], + + 'no-restricted-globals': ['error', { + name: 'context', + message: 'Don\'t use Mocha\'s global context' + }], + + 'no-prototype-builtins': 'off', + 'no-empty': 'off', + 'eol-last': 'warn', + + 'no-multiple-empty-lines': ['warn', { + max: 2 + }] + } + }, + // general typescript options + { + files: ['**/*.{ts,tsx}', '**/*.md/*.ts', '**/*.md/*.typescript'], + extends: [ + tseslint.configs.recommended + ], + languageOptions: { + parserOptions: { + projectService: { + allowDefaultProject: [], + defaultProject: 'tsconfig.json' + } + } + }, + rules: { + '@typescript-eslint/triple-slash-reference': 'off', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-empty-function': 'off', + + 'spaced-comment': ['error', 'always', { + block: { + markers: ['!'], + balanced: true + }, + + markers: ['/'] + }], + + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/ban-types': 'off', + '@typescript-eslint/no-unused-vars': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/prefer-optional-chain': 'error', + '@typescript-eslint/no-dupe-class-members': 'error', + '@typescript-eslint/no-redeclare': 'error', + '@typescript-eslint/space-infix-ops': 'off', + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-empty-object-type': 'off', + '@typescript-eslint/no-wrapper-object-types': 'off', + '@typescript-eslint/no-unused-expressions': 'off', + '@typescript-eslint/no-unsafe-function-type': 'off' + } + }, + // type test specific options + { + files: ['test/types/**/*.ts'], + rules: { + '@typescript-eslint/no-empty-interface': 'off' + } + }, + // test specific options (including type tests) + { + files: ['test/**/*.js', 'test/**/*.ts'], + ignores: ['deno*.mjs'], + plugins: { + 'mocha-no-only': mochaNoOnly + }, + languageOptions: { + globals: globals.mocha + }, + rules: { + 'no-self-assign': 'off', + 'mocha-no-only/mocha-no-only': ['error'] + } + }, + // deno specific options + { + files: ['**/deno*.mjs'], + languageOptions: { + globals: { + // "globals" currently has no definition for deno + Deno: 'readonly' + } + } + }, + // general options for module files + { + files: ['**/*.mjs'], + languageOptions: { + sourceType: 'module' + } + }, + // doc script specific options + { + files: ['**/docs/js/**/*.js'], + languageOptions: { + globals: { + ...Object.fromEntries(Object.entries(globals.node).map(([key]) => [key, 'off'])), + ...globals.browser } + } + } +]); diff --git a/backend/node_modules/mongoose/index.js b/backend/node_modules/mongoose/index.js new file mode 100644 index 0000000..6ebbd5f --- /dev/null +++ b/backend/node_modules/mongoose/index.js @@ -0,0 +1,64 @@ +/** + * Export lib/mongoose + * + */ + +'use strict'; + +const mongoose = require('./lib/'); + +module.exports = mongoose; +module.exports.default = mongoose; +module.exports.mongoose = mongoose; + +// Re-export for ESM support +module.exports.cast = mongoose.cast; +module.exports.STATES = mongoose.STATES; +module.exports.setDriver = mongoose.setDriver; +module.exports.set = mongoose.set; +module.exports.get = mongoose.get; +module.exports.createConnection = mongoose.createConnection; +module.exports.connect = mongoose.connect; +module.exports.disconnect = mongoose.disconnect; +module.exports.startSession = mongoose.startSession; +module.exports.pluralize = mongoose.pluralize; +module.exports.model = mongoose.model; +module.exports.deleteModel = mongoose.deleteModel; +module.exports.modelNames = mongoose.modelNames; +module.exports.plugin = mongoose.plugin; +module.exports.connections = mongoose.connections; +module.exports.version = mongoose.version; +module.exports.Aggregate = mongoose.Aggregate; +module.exports.Mongoose = mongoose.Mongoose; +module.exports.Schema = mongoose.Schema; +module.exports.SchemaType = mongoose.SchemaType; +module.exports.SchemaTypes = mongoose.SchemaTypes; +module.exports.VirtualType = mongoose.VirtualType; +module.exports.Types = mongoose.Types; +module.exports.Query = mongoose.Query; +module.exports.Model = mongoose.Model; +module.exports.Document = mongoose.Document; +module.exports.ObjectId = mongoose.ObjectId; +module.exports.isValidObjectId = mongoose.isValidObjectId; +module.exports.isObjectIdOrHexString = mongoose.isObjectIdOrHexString; +module.exports.syncIndexes = mongoose.syncIndexes; +module.exports.Decimal128 = mongoose.Decimal128; +module.exports.Mixed = mongoose.Mixed; +module.exports.Date = mongoose.Date; +module.exports.Number = mongoose.Number; +module.exports.Error = mongoose.Error; +module.exports.MongooseError = mongoose.MongooseError; +module.exports.now = mongoose.now; +module.exports.CastError = mongoose.CastError; +module.exports.SchemaTypeOptions = mongoose.SchemaTypeOptions; +module.exports.mongo = mongoose.mongo; +module.exports.mquery = mongoose.mquery; +module.exports.sanitizeFilter = mongoose.sanitizeFilter; +module.exports.trusted = mongoose.trusted; +module.exports.skipMiddlewareFunction = mongoose.skipMiddlewareFunction; +module.exports.overwriteMiddlewareResult = mongoose.overwriteMiddlewareResult; + +// The following properties are not exported using ESM because `setDriver()` can mutate these +// module.exports.connection = mongoose.connection; +// module.exports.Collection = mongoose.Collection; +// module.exports.Connection = mongoose.Connection; diff --git a/backend/node_modules/mongoose/lib/aggregate.js b/backend/node_modules/mongoose/lib/aggregate.js new file mode 100644 index 0000000..81096a4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/aggregate.js @@ -0,0 +1,1226 @@ +'use strict'; + +/*! + * Module dependencies + */ + +const AggregationCursor = require('./cursor/aggregationCursor'); +const MongooseError = require('./error/mongooseError'); +const Query = require('./query'); +const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption'); +const clone = require('./helpers/clone'); +const getConstructorName = require('./helpers/getConstructorName'); +const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline'); +const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter'); +const stringifyFunctionOperators = require('./helpers/aggregate/stringifyFunctionOperators'); +const utils = require('./utils'); +const { modelSymbol } = require('./helpers/symbols'); +const read = Query.prototype.read; +const readConcern = Query.prototype.readConcern; + +const validRedactStringValues = new Set(['$$DESCEND', '$$PRUNE', '$$KEEP']); + +/** + * Aggregate constructor used for building aggregation pipelines. Do not + * instantiate this class directly, use [Model.aggregate()](https://mongoosejs.com/docs/api/model.html#Model.aggregate()) instead. + * + * #### Example: + * + * const aggregate = Model.aggregate([ + * { $project: { a: 1, b: 1 } }, + * { $skip: 5 } + * ]); + * + * Model. + * aggregate([{ $match: { age: { $gte: 21 }}}]). + * unwind('tags'). + * exec(); + * + * #### Note: + * + * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned). + * - Mongoose does **not** cast pipeline stages. The below will **not** work unless `_id` is a string in the database + * + * new Aggregate([{ $match: { _id: '00000000000000000000000a' } }]); + * // Do this instead to cast to an ObjectId + * new Aggregate([{ $match: { _id: new mongoose.Types.ObjectId('00000000000000000000000a') } }]); + * + * @see MongoDB https://www.mongodb.com/docs/manual/applications/aggregation/ + * @see driver https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#aggregate + * @param {Array} [pipeline] aggregation pipeline as an array of objects + * @param {Model|Connection} [modelOrConn] the model or connection to use with this aggregate. + * @api public + */ + +function Aggregate(pipeline, modelOrConn) { + this._pipeline = []; + if (modelOrConn == null || modelOrConn[modelSymbol]) { + this._model = modelOrConn; + } else { + this._connection = modelOrConn; + } + this.options = {}; + + if (arguments.length === 1 && Array.isArray(pipeline)) { + this.append.apply(this, pipeline); + } +} + +/** + * Contains options passed down to the [aggregate command](https://www.mongodb.com/docs/manual/reference/command/aggregate/). + * Supported options are: + * + * - [`allowDiskUse`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.allowDiskUse()) + * - `bypassDocumentValidation` + * - [`collation`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.collation()) + * - `comment` + * - [`cursor`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.cursor()) + * - [`explain`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.explain()) + * - `fieldsAsRaw` + * - [`hint`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.hint()) + * - `let` + * - `maxTimeMS` + * - `raw` + * - [`readConcern`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.readConcern()) + * - `readPreference` + * - [`session`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.session()) + * - `writeConcern` + * + * @property options + * @memberOf Aggregate + * @api public + */ + +Aggregate.prototype.options; + +/** + * Returns default options for this aggregate. + * + * @param {Model} model + * @api private + */ + +Aggregate.prototype._optionsForExec = function() { + const options = this.options || {}; + + const asyncLocalStorage = this.model()?.db?.base.transactionAsyncLocalStorage?.getStore(); + if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) { + options.session = asyncLocalStorage.session; + } + + return options; +}; + +/** + * Get/set the model that this aggregation will execute on. + * + * #### Example: + * + * const aggregate = MyModel.aggregate([{ $match: { answer: 42 } }]); + * aggregate.model() === MyModel; // true + * + * // Change the model. There's rarely any reason to do this. + * aggregate.model(SomeOtherModel); + * aggregate.model() === SomeOtherModel; // true + * + * @param {Model} [model] Set the model associated with this aggregate. If not provided, returns the already stored model. + * @return {Model} + * @api public + */ + +Aggregate.prototype.model = function(model) { + if (arguments.length === 0) { + return this._model; + } + + this._model = model; + if (model.schema != null) { + if (this.options.readPreference == null && + model.schema.options.read != null) { + this.options.readPreference = model.schema.options.read; + } + if (this.options.collation == null && + model.schema.options.collation != null) { + this.options.collation = model.schema.options.collation; + } + } + + return model; +}; + +/** + * Appends new operators to this aggregate pipeline + * + * #### Example: + * + * aggregate.append({ $project: { field: 1 }}, { $limit: 2 }); + * + * // or pass an array + * const pipeline = [{ $match: { daw: 'Logic Audio X' }} ]; + * aggregate.append(pipeline); + * + * @param {...object|object[]} ops operator(s) to append. Can either be a spread of objects or a single parameter of an object array. + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.append = function() { + const args = (arguments.length === 1 && Array.isArray(arguments[0])) + ? arguments[0] + : [...arguments]; + + if (!args.every(isOperator)) { + throw new MongooseError('Arguments must be aggregate pipeline operators'); + } + + this._pipeline = this._pipeline.concat(args); + + return this; +}; + +/** + * Appends a new $addFields operator to this aggregate pipeline. + * Requires MongoDB v3.4+ to work + * + * #### Example: + * + * // adding new fields based on existing fields + * aggregate.addFields({ + * newField: '$b.nested' + * , plusTen: { $add: ['$val', 10]} + * , sub: { + * name: '$a' + * } + * }) + * + * // etc + * aggregate.addFields({ salary_k: { $divide: [ "$salary", 1000 ] } }); + * + * @param {object} arg field specification + * @see $addFields https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/ + * @return {Aggregate} + * @api public + */ +Aggregate.prototype.addFields = function(arg) { + if (typeof arg !== 'object' || arg === null || Array.isArray(arg)) { + throw new MongooseError('Invalid addFields() argument. Must be an object'); + } + return this.append({ $addFields: Object.assign({}, arg) }); +}; + +/** + * Appends a new $project operator to this aggregate pipeline. + * + * Mongoose query [selection syntax](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) is also supported. + * + * #### Example: + * + * // include a, include b, exclude _id + * aggregate.project("a b -_id"); + * + * // or you may use object notation, useful when + * // you have keys already prefixed with a "-" + * aggregate.project({a: 1, b: 1, _id: 0}); + * + * // reshaping documents + * aggregate.project({ + * newField: '$b.nested' + * , plusTen: { $add: ['$val', 10]} + * , sub: { + * name: '$a' + * } + * }) + * + * // etc + * aggregate.project({ salary_k: { $divide: [ "$salary", 1000 ] } }); + * + * @param {object|string} arg field specification + * @see projection https://www.mongodb.com/docs/manual/reference/aggregation/project/ + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.project = function(arg) { + const fields = {}; + + if (typeof arg === 'object' && !Array.isArray(arg)) { + Object.keys(arg).forEach(function(field) { + fields[field] = arg[field]; + }); + } else if (arguments.length === 1 && typeof arg === 'string') { + arg.split(/\s+/).forEach(function(field) { + if (!field) { + return; + } + const include = field[0] === '-' ? 0 : 1; + if (include === 0) { + field = field.substring(1); + } + fields[field] = include; + }); + } else { + throw new MongooseError('Invalid project() argument. Must be string or object'); + } + + return this.append({ $project: fields }); +}; + +/** + * Appends a new custom $group operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.group({ _id: "$department" }); + * + * @see $group https://www.mongodb.com/docs/manual/reference/aggregation/group/ + * @method group + * @memberOf Aggregate + * @instance + * @param {object} arg $group operator contents + * @return {Aggregate} + * @api public + */ + +/** + * Appends a new custom $match operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.match({ department: { $in: [ "sales", "engineering" ] } }); + * + * @see $match https://www.mongodb.com/docs/manual/reference/aggregation/match/ + * @method match + * @memberOf Aggregate + * @instance + * @param {object} arg $match operator contents + * @return {Aggregate} + * @api public + */ + +/** + * Appends a new $skip operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.skip(10); + * + * @see $skip https://www.mongodb.com/docs/manual/reference/aggregation/skip/ + * @method skip + * @memberOf Aggregate + * @instance + * @param {number} num number of records to skip before next stage + * @return {Aggregate} + * @api public + */ + +/** + * Appends a new $limit operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.limit(10); + * + * @see $limit https://www.mongodb.com/docs/manual/reference/aggregation/limit/ + * @method limit + * @memberOf Aggregate + * @instance + * @param {number} num maximum number of records to pass to the next stage + * @return {Aggregate} + * @api public + */ + + +/** + * Appends a new $densify operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.densify({ + * field: 'timestamp', + * range: { + * step: 1, + * unit: 'hour', + * bounds: [new Date('2021-05-18T00:00:00.000Z'), new Date('2021-05-18T08:00:00.000Z')] + * } + * }); + * + * @see $densify https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/ + * @method densify + * @memberOf Aggregate + * @instance + * @param {object} arg $densify operator contents + * @return {Aggregate} + * @api public + */ + +/** + * Appends a new $fill operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.fill({ + * output: { + * bootsSold: { value: 0 }, + * sandalsSold: { value: 0 }, + * sneakersSold: { value: 0 } + * } + * }); + * + * @see $fill https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/ + * @method fill + * @memberOf Aggregate + * @instance + * @param {object} arg $fill operator contents + * @return {Aggregate} + * @api public + */ + +/** + * Appends a new $geoNear operator to this aggregate pipeline. + * + * #### Note: + * + * **MUST** be used as the first operator in the pipeline. + * + * #### Example: + * + * aggregate.near({ + * near: { type: 'Point', coordinates: [40.724, -73.997] }, + * distanceField: "dist.calculated", // required + * maxDistance: 0.008, + * query: { type: "public" }, + * includeLocs: "dist.location", + * spherical: true, + * }); + * + * @see $geoNear https://www.mongodb.com/docs/manual/reference/aggregation/geoNear/ + * @method near + * @memberOf Aggregate + * @instance + * @param {object} arg + * @param {object|number[]} arg.near GeoJSON point or coordinates array + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.near = function(arg) { + if (arg == null) { + throw new MongooseError('Aggregate `near()` must be called with non-nullish argument'); + } + if (arg.near == null) { + throw new MongooseError('Aggregate `near()` argument must have a `near` property'); + } + const coordinates = Array.isArray(arg.near) ? arg.near : arg.near.coordinates; + if (typeof arg.near === 'object' && (!Array.isArray(coordinates) || coordinates.length < 2 || coordinates.find(c => typeof c !== 'number'))) { + throw new MongooseError(`Aggregate \`near()\` argument has invalid coordinates, got "${coordinates}"`); + } + + const op = {}; + op.$geoNear = arg; + return this.append(op); +}; + +/*! + * define methods + */ + +'group match skip limit out densify fill'.split(' ').forEach(function($operator) { + Aggregate.prototype[$operator] = function(arg) { + const op = {}; + op['$' + $operator] = arg; + return this.append(op); + }; +}); + +/** + * Appends new custom $unwind operator(s) to this aggregate pipeline. + * + * Note that the `$unwind` operator requires the path name to start with '$'. + * Mongoose will prepend '$' if the specified field doesn't start '$'. + * + * #### Example: + * + * aggregate.unwind("tags"); + * aggregate.unwind("a", "b", "c"); + * aggregate.unwind({ path: '$tags', preserveNullAndEmptyArrays: true }); + * + * @see $unwind https://www.mongodb.com/docs/manual/reference/aggregation/unwind/ + * @param {string|object|string[]|object[]} fields the field(s) to unwind, either as field names or as [objects with options](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#document-operand-with-options). If passing a string, prefixing the field name with '$' is optional. If passing an object, `path` must start with '$'. + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.unwind = function() { + const args = [...arguments]; + + const res = []; + for (const arg of args) { + if (arg && typeof arg === 'object') { + res.push({ $unwind: arg }); + } else if (typeof arg === 'string') { + res.push({ + $unwind: (arg[0] === '$') ? arg : '$' + arg + }); + } else { + throw new MongooseError('Invalid arg "' + arg + '" to unwind(), ' + + 'must be string or object'); + } + } + + return this.append.apply(this, res); +}; + +/** + * Appends a new $replaceRoot operator to this aggregate pipeline. + * + * Note that the `$replaceRoot` operator requires field strings to start with '$'. + * If you are passing in a string Mongoose will prepend '$' if the specified field doesn't start '$'. + * If you are passing in an object the strings in your expression will not be altered. + * + * #### Example: + * + * aggregate.replaceRoot("user"); + * + * aggregate.replaceRoot({ x: { $concat: ['$this', '$that'] } }); + * + * @see $replaceRoot https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot + * @param {string|object} newRoot the field or document which will become the new root document + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.replaceRoot = function(newRoot) { + let ret; + + if (typeof newRoot === 'string') { + ret = newRoot.startsWith('$') ? newRoot : '$' + newRoot; + } else { + ret = newRoot; + } + + return this.append({ + $replaceRoot: { + newRoot: ret + } + }); +}; + +/** + * Appends a new $count operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.count("userCount"); + * + * @see $count https://www.mongodb.com/docs/manual/reference/operator/aggregation/count + * @param {string} fieldName The name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character. + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.count = function(fieldName) { + return this.append({ $count: fieldName }); +}; + +/** + * Appends a new $sortByCount operator to this aggregate pipeline. Accepts either a string field name + * or a pipeline object. + * + * Note that the `$sortByCount` operator requires the new root to start with '$'. + * Mongoose will prepend '$' if the specified field name doesn't start with '$'. + * + * #### Example: + * + * aggregate.sortByCount('users'); + * aggregate.sortByCount({ $mergeObjects: [ "$employee", "$business" ] }) + * + * @see $sortByCount https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/ + * @param {object|string} arg + * @return {Aggregate} this + * @api public + */ + +Aggregate.prototype.sortByCount = function(arg) { + if (arg && typeof arg === 'object') { + return this.append({ $sortByCount: arg }); + } else if (typeof arg === 'string') { + return this.append({ + $sortByCount: (arg[0] === '$') ? arg : '$' + arg + }); + } else { + throw new TypeError('Invalid arg "' + arg + '" to sortByCount(), ' + + 'must be string or object'); + } +}; + +/** + * Appends new custom $lookup operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.lookup({ from: 'users', localField: 'userId', foreignField: '_id', as: 'users' }); + * + * @see $lookup https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#pipe._S_lookup + * @param {object} options to $lookup as described in the above link + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.lookup = function(options) { + return this.append({ $lookup: options }); +}; + +/** + * Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection. + * + * Note that graphLookup can only consume at most 100MB of memory, and does not allow disk use even if `{ allowDiskUse: true }` is specified. + * + * #### Example: + * + * // Suppose we have a collection of courses, where a document might look like `{ _id: 0, name: 'Calculus', prerequisite: 'Trigonometry'}` and `{ _id: 0, name: 'Trigonometry', prerequisite: 'Algebra' }` + * aggregate.graphLookup({ from: 'courses', startWith: '$prerequisite', connectFromField: 'prerequisite', connectToField: 'name', as: 'prerequisites', maxDepth: 3 }) // this will recursively search the 'courses' collection up to 3 prerequisites + * + * @see $graphLookup https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/#pipe._S_graphLookup + * @param {object} options to $graphLookup as described in the above link + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.graphLookup = function(options) { + const cloneOptions = {}; + if (options) { + if (!utils.isObject(options)) { + throw new TypeError('Invalid graphLookup() argument. Must be an object.'); + } + + utils.mergeClone(cloneOptions, options); + const startWith = cloneOptions.startWith; + + if (startWith && typeof startWith === 'string') { + cloneOptions.startWith = cloneOptions.startWith.startsWith('$') ? + cloneOptions.startWith : + '$' + cloneOptions.startWith; + } + + } + return this.append({ $graphLookup: cloneOptions }); +}; + +/** + * Appends new custom $sample operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.sample(3); // Add a pipeline that picks 3 random documents + * + * @see $sample https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/#pipe._S_sample + * @param {number} size number of random documents to pick + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.sample = function(size) { + return this.append({ $sample: { size: size } }); +}; + +/** + * Appends a new $sort operator to this aggregate pipeline. + * + * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`. + * + * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending. + * + * #### Example: + * + * // these are equivalent + * aggregate.sort({ field: 'asc', test: -1 }); + * aggregate.sort('field -test'); + * + * @see $sort https://www.mongodb.com/docs/manual/reference/aggregation/sort/ + * @param {object|string} arg + * @return {Aggregate} this + * @api public + */ + +Aggregate.prototype.sort = function(arg) { + // TODO refactor to reuse the query builder logic + + const sort = {}; + + if (getConstructorName(arg) === 'Object') { + const desc = ['desc', 'descending', -1]; + Object.keys(arg).forEach(function(field) { + // If sorting by text score, skip coercing into 1/-1 + if (arg[field] instanceof Object && arg[field].$meta) { + sort[field] = arg[field]; + return; + } + sort[field] = desc.indexOf(arg[field]) === -1 ? 1 : -1; + }); + } else if (arguments.length === 1 && typeof arg === 'string') { + arg.split(/\s+/).forEach(function(field) { + if (!field) { + return; + } + const ascend = field[0] === '-' ? -1 : 1; + if (ascend === -1) { + field = field.substring(1); + } + sort[field] = ascend; + }); + } else { + throw new TypeError('Invalid sort() argument. Must be a string or object.'); + } + + return this.append({ $sort: sort }); +}; + +/** + * Appends new $unionWith operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.unionWith({ coll: 'users', pipeline: [ { $match: { _id: 1 } } ] }); + * + * @see $unionWith https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith + * @param {object} options to $unionWith query as described in the above link + * @return {Aggregate} + * @api public + */ + +Aggregate.prototype.unionWith = function(options) { + return this.append({ $unionWith: options }); +}; + + +/** + * Sets the readPreference option for the aggregation query. + * + * #### Example: + * + * await Model.aggregate(pipeline).read('primaryPreferred'); + * + * @param {string|ReadPreference} pref one of the listed preference options or their aliases + * @param {Array} [tags] optional tags for this query. + * @return {Aggregate} this + * @api public + * @see mongodb https://www.mongodb.com/docs/manual/applications/replication/#read-preference + */ + +Aggregate.prototype.read = function(pref, tags) { + read.call(this, pref, tags); + return this; +}; + +/** + * Sets the readConcern level for the aggregation query. + * + * #### Example: + * + * await Model.aggregate(pipeline).readConcern('majority'); + * + * @param {'local'|'available'|'majority'|'snapshot'|'linearizable'|'l'|'a'|'m'|'s'|'lz'} level one of the listed read concern level or their aliases + * @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/ + * @return {Aggregate} this + * @api public + */ + +Aggregate.prototype.readConcern = function(level) { + readConcern.call(this, level); + return this; +}; + +/** + * Appends a new $redact operator to this aggregate pipeline. + * + * If 3 arguments are supplied, Mongoose will wrap them with if-then-else of $cond operator respectively + * If `thenExpr` or `elseExpr` is string, make sure it starts with $$, like `$$DESCEND`, `$$PRUNE` or `$$KEEP`. + * + * #### Example: + * + * await Model.aggregate(pipeline).redact({ + * $cond: { + * if: { $eq: [ '$level', 5 ] }, + * then: '$$PRUNE', + * else: '$$DESCEND' + * } + * }); + * + * // $redact often comes with $cond operator, you can also use the following syntax provided by mongoose + * await Model.aggregate(pipeline).redact({ $eq: [ '$level', 5 ] }, '$$PRUNE', '$$DESCEND'); + * + * @param {object} expression redact options or conditional expression + * @param {string|object} [thenExpr] true case for the condition + * @param {string|object} [elseExpr] false case for the condition + * @return {Aggregate} this + * @see $redact https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/ + * @api public + */ + +Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) { + if (arguments.length === 3) { + if ((typeof thenExpr === 'string' && !validRedactStringValues.has(thenExpr)) || + (typeof elseExpr === 'string' && !validRedactStringValues.has(elseExpr))) { + throw new MongooseError('If thenExpr or elseExpr is string, it must be either $$DESCEND, $$PRUNE or $$KEEP'); + } + + expression = { + $cond: { + if: expression, + then: thenExpr, + else: elseExpr + } + }; + } else if (arguments.length !== 1) { + throw new TypeError('Invalid arguments'); + } + + return this.append({ $redact: expression }); +}; + +/** + * Execute the aggregation with explain + * + * #### Example: + * + * Model.aggregate(..).explain() + * + * @param {'queryPlanner'|'executionStats'|'allPlansExecution'} [verbosity] + * @return {Promise} + */ + +Aggregate.prototype.explain = async function explain(verbosity) { + if (typeof verbosity === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Aggregate.prototype.explain() no longer accepts a callback'); + } + const model = this._model; + + if (!this._pipeline.length) { + throw new MongooseError('Aggregate has empty pipeline'); + } + + prepareDiscriminatorPipeline(this._pipeline, this._model.schema); + + const preFilter = buildMiddlewareFilter(this.options, 'pre'); + const postFilter = buildMiddlewareFilter(this.options, 'post'); + + // Remove middleware option before passing to MongoDB + const options = this.options != null ? { ...this.options } : {}; + delete options.middleware; + + try { + await model.hooks.execPre('aggregate', this, [], { filter: preFilter }); + } catch (error) { + return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter }); + } + + const cursor = await model.collection.aggregate(this._pipeline, options); + + if (verbosity == null) { + verbosity = true; + } + + let result = null; + try { + result = await cursor.explain(verbosity); + } catch (error) { + return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter }); + } + + await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter }); + + return result; +}; + +/** + * Sets the allowDiskUse option for the aggregation query + * + * #### Example: + * + * await Model.aggregate([{ $match: { foo: 'bar' } }]).allowDiskUse(true); + * + * @param {boolean} value Should tell server it can use hard drive to store data during aggregation. + * @return {Aggregate} this + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + +Aggregate.prototype.allowDiskUse = function(value) { + this.options.allowDiskUse = value; + return this; +}; + +/** + * Sets the hint option for the aggregation query + * + * #### Example: + * + * Model.aggregate(..).hint({ qty: 1, category: 1 }).exec(); + * + * @param {object|string} value a hint object or the index name + * @return {Aggregate} this + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + +Aggregate.prototype.hint = function(value) { + this.options.hint = value; + return this; +}; + +/** + * Sets the session for this aggregation. Useful for [transactions](https://mongoosejs.com/docs/transactions.html). + * + * #### Example: + * + * const session = await Model.startSession(); + * await Model.aggregate(..).session(session); + * + * @param {ClientSession} session + * @return {Aggregate} this + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/ + */ + +Aggregate.prototype.session = function(session) { + if (session == null) { + delete this.options.session; + } else { + this.options.session = session; + } + return this; +}; + +/** + * Lets you set arbitrary options, for middleware or plugins. + * + * #### Example: + * + * const agg = Model.aggregate(..).option({ allowDiskUse: true }); // Set the `allowDiskUse` option + * agg.options; // `{ allowDiskUse: true }` + * + * @param {object} options keys to merge into current options + * @param {number} [options.maxTimeMS] number limits the time this aggregation will run, see [MongoDB docs on `maxTimeMS`](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) + * @param {boolean} [options.allowDiskUse] boolean if true, the MongoDB server will use the hard drive to store data during this aggregation + * @param {object} [options.collation] object see [`Aggregate.prototype.collation()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.collation()) + * @param {ClientSession} [options.session] ClientSession see [`Aggregate.prototype.session()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.session()) + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/aggregate/ + * @return {Aggregate} this + * @api public + */ + +Aggregate.prototype.option = function(value) { + for (const key in value) { + this.options[key] = value[key]; + } + return this; +}; + +/** + * Sets the `cursor` option and executes this aggregation, returning an aggregation cursor. + * Cursors are useful if you want to process the results of the aggregation one-at-a-time + * because the aggregation result is too big to fit into memory. + * + * #### Example: + * + * const cursor = Model.aggregate(..).cursor({ batchSize: 1000 }); + * cursor.eachAsync(function(doc, i) { + * // use doc + * }); + * + * @param {object} options + * @param {number} [options.batchSize] set the cursor batch size + * @param {boolean} [options.useMongooseAggCursor] use experimental mongoose-specific aggregation cursor (for `eachAsync()` and other query cursor semantics) + * @return {AggregationCursor} cursor representing this aggregation + * @api public + * @see mongodb https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html + */ + +Aggregate.prototype.cursor = function(options) { + this._optionsForExec(); + this.options.cursor = options || {}; + return new AggregationCursor(this); // return this; +}; + +/** + * Adds a collation + * + * #### Example: + * + * const res = await Model.aggregate(pipeline).collation({ locale: 'en_US', strength: 1 }); + * + * @param {object} collation options + * @return {Aggregate} this + * @api public + * @see mongodb https://mongodb.github.io/node-mongodb-native/7.0/interfaces/CollationOptions.html + */ + +Aggregate.prototype.collation = function(collation) { + this.options.collation = collation; + return this; +}; + +/** + * Combines multiple aggregation pipelines. + * + * #### Example: + * + * const res = await Model.aggregate().facet({ + * books: [{ groupBy: '$author' }], + * price: [{ $bucketAuto: { groupBy: '$price', buckets: 2 } }] + * }); + * + * // Output: { books: [...], price: [{...}, {...}] } + * + * @param {object} facet options + * @return {Aggregate} this + * @see $facet https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/ + * @api public + */ + +Aggregate.prototype.facet = function(options) { + return this.append({ $facet: options }); +}; + +/** + * Helper for [Atlas Text Search](https://www.mongodb.com/docs/atlas/atlas-search/tutorial/)'s + * `$search` stage. + * + * #### Example: + * + * const res = await Model.aggregate(). + * search({ + * text: { + * query: 'baseball', + * path: 'plot' + * } + * }); + * + * // Output: [{ plot: '...', title: '...' }] + * + * @param {object} $search options + * @return {Aggregate} this + * @see $search https://www.mongodb.com/docs/atlas/atlas-search/tutorial/ + * @api public + */ + +Aggregate.prototype.search = function(options) { + return this.append({ $search: options }); +}; + +/** + * Returns the current pipeline + * + * #### Example: + * + * MyModel.aggregate().match({ test: 1 }).pipeline(); // [{ $match: { test: 1 } }] + * + * @return {PipelineStage[]} The current pipeline similar to the operation that will be executed + * @api public + */ + +Aggregate.prototype.pipeline = function() { + return this._pipeline; +}; + +/** + * Returns the current pipeline as a `$unionWith`-safe pipeline. + * Throws if this pipeline contains `$out` or `$merge`. + * + * #### Example: + * + * const base = MyModel.aggregate().match({ test: 1 }); + * base.pipelineForUnionWith(); // [{ $match: { test: 1 } }] + * + * @return {Array} The current pipeline with `$unionWith` stage restrictions + * @api public + */ + +Aggregate.prototype.pipelineForUnionWith = function pipelineForUnionWith() { + for (const stage of this._pipeline) { + if (stage?.$out != null || stage?.$merge != null) { + throw new MongooseError('Aggregate pipeline for $unionWith cannot include `$out` or `$merge` stages'); + } + } + + return this._pipeline; +}; + +/** + * Executes the aggregate pipeline on the currently bound Model. + * + * #### Example: + * const result = await aggregate.exec(); + * + * @return {Promise} + * @api public + */ + +Aggregate.prototype.exec = async function exec() { + if (!this._model && !this._connection) { + throw new MongooseError('Aggregate not bound to any Model'); + } + if (typeof arguments[0] === 'function') { + throw new MongooseError('Aggregate.prototype.exec() no longer accepts a callback'); + } + + if (this._connection) { + if (!this._pipeline.length) { + throw new MongooseError('Aggregate has empty pipeline'); + } + + this._optionsForExec(); + + const cursor = await this._connection.client.db().aggregate(this._pipeline, this.options); + return await cursor.toArray(); + } + + const model = this._model; + const collection = this._model.collection; + + applyGlobalMaxTimeMS(this.options, model.db.options, model.base.options); + applyGlobalDiskUse(this.options, model.db.options, model.base.options); + + this._optionsForExec(); + + if (this.options?.cursor) { + return new AggregationCursor(this); + } + + prepareDiscriminatorPipeline(this._pipeline, this._model.schema); + stringifyFunctionOperators(this._pipeline); + + const preFilter = buildMiddlewareFilter(this.options, 'pre'); + const postFilter = buildMiddlewareFilter(this.options, 'post'); + + try { + await model.hooks.execPre('aggregate', this, [], { filter: preFilter }); + } catch (error) { + return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter }); + } + + if (!this._pipeline.length) { + throw new MongooseError('Aggregate has empty pipeline'); + } + + const options = clone(this.options || {}); + delete options.middleware; + + let result; + try { + const cursor = await collection.aggregate(this._pipeline, options); + result = await cursor.toArray(); + } catch (error) { + return await model.hooks.execPost('aggregate', this, [null], { error, filter: postFilter }); + } + + await model.hooks.execPost('aggregate', this, [result], { error: null, filter: postFilter }); + return result; +}; + +/** + * Provides a Promise-like `then` function, which will call `.exec` without a callback + * Compatible with `await`. + * + * #### Example: + * + * Model.aggregate(..).then(successCallback, errorCallback); + * + * @param {Function} [resolve] successCallback + * @param {Function} [reject] errorCallback + * @return {Promise} + */ +Aggregate.prototype.then = function(resolve, reject) { + return this.exec().then(resolve, reject); +}; + +/** + * Executes the aggregation returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * Like [`.then()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.then), but only takes a rejection handler. + * Compatible with `await`. + * + * @param {Function} [reject] + * @return {Promise} + * @api public + */ + +Aggregate.prototype.catch = function(reject) { + return this.exec().then(null, reject); +}; + +/** + * Executes the aggregate returning a `Promise` which will be + * resolved with `.finally()` chained. + * + * More about [Promise `finally()` in JavaScript](https://thecodebarbarian.com/using-promise-finally-in-node-js.html). + * + * @param {Function} [onFinally] + * @return {Promise} + * @api public + */ + +Aggregate.prototype.finally = function(onFinally) { + return this.exec().finally(onFinally); +}; + +/** + * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js) + * You do not need to call this function explicitly, the JavaScript runtime + * will call it for you. + * + * #### Example: + * + * const agg = Model.aggregate([{ $match: { age: { $gte: 25 } } }]); + * for await (const doc of agg) { + * console.log(doc.name); + * } + * + * @method [Symbol.asyncIterator] + * @memberOf Aggregate + * @instance + * @api public + */ + +Aggregate.prototype[Symbol.asyncIterator] = function() { + return this.cursor({ useMongooseAggCursor: true }).transformNull()._transformForAsyncIterator(); +}; + +/*! + * Helpers + */ + +/** + * Checks whether an object is likely a pipeline operator + * + * @param {object} obj object to check + * @return {boolean} + * @api private + */ + +function isOperator(obj) { + if (typeof obj !== 'object' || obj === null) { + return false; + } + + const k = Object.keys(obj); + + return k.length === 1 && k[0][0] === '$'; +} + +/** + * Adds the appropriate `$match` pipeline step to the top of an aggregate's + * pipeline, should it's model is a non-root discriminator type. This is + * analogous to the `prepareDiscriminatorCriteria` function in `lib/query.js`. + * + * @param {Aggregate} aggregate Aggregate to prepare + * @api private + */ + +Aggregate._prepareDiscriminatorPipeline = prepareDiscriminatorPipeline; + +/*! + * Exports + */ + +module.exports = Aggregate; diff --git a/backend/node_modules/mongoose/lib/cast.js b/backend/node_modules/mongoose/lib/cast.js new file mode 100644 index 0000000..4faa5cd --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast.js @@ -0,0 +1,436 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('./error/cast'); +const StrictModeError = require('./error/strict'); +const Types = require('./schema/index'); +const cast$expr = require('./helpers/query/cast$expr'); +const castString = require('./cast/string'); +const castTextSearch = require('./schema/operators/text'); +const get = require('./helpers/get'); +const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue'); +const isOperator = require('./helpers/query/isOperator'); +const util = require('util'); +const isObject = require('./helpers/isObject'); +const isMongooseObject = require('./helpers/isMongooseObject'); +const utils = require('./utils'); + +const ALLOWED_GEOWITHIN_GEOJSON_TYPES = ['Polygon', 'MultiPolygon']; + +/** + * Handles internal casting for query filters. + * + * @param {Schema} schema + * @param {object} obj Object to cast + * @param {object} [options] the query options + * @param {boolean|"throw"} [options.strict] Whether to enable all strict options + * @param {boolean|"throw"} [options.strictQuery] Enable strict Queries + * @param {boolean} [options.sanitizeFilter] avoid adding implicit query selectors ($in) + * @param {boolean} [options.upsert] + * @param {Query} [context] passed to setters + * @api private + */ +module.exports = function cast(schema, obj, options, context) { + if (Array.isArray(obj)) { + throw new Error('Query filter must be an object, got an array ' + util.inspect(obj)); + } + + if (obj == null) { + return obj; + } + + if (schema?.discriminators != null && obj[schema.options.discriminatorKey] != null) { + schema = getSchemaDiscriminatorByValue(schema, obj[schema.options.discriminatorKey]) || schema; + } + + const paths = Object.keys(obj); + let i = paths.length; + let _keys; + let any$conditionals; + let schematype; + let nested; + let path; + let type; + let val; + + options = options || {}; + + while (i--) { + path = paths[i]; + val = obj[path]; + + if (path === '$or' || path === '$nor' || path === '$and') { + if (!Array.isArray(val)) { + throw new CastError('Array', val, path); + } + for (let k = val.length - 1; k >= 0; k--) { + if (val[k] == null || typeof val[k] !== 'object') { + throw new CastError('Object', val[k], path + '.' + k); + } + const beforeCastKeysLength = Object.keys(val[k]).length; + const discriminatorValue = val[k][schema.options.discriminatorKey]; + if (discriminatorValue == null) { + val[k] = cast(schema, val[k], options, context); + } else { + const discriminatorSchema = getSchemaDiscriminatorByValue(context.schema, discriminatorValue); + val[k] = cast(discriminatorSchema ? discriminatorSchema : schema, val[k], options, context); + } + + if (utils.hasOwnKeys(val[k]) === false && beforeCastKeysLength !== 0) { + val.splice(k, 1); + } + } + + // delete empty: {$or: []} -> {} + if (val.length === 0) { + delete obj[path]; + } + } else if (path === '$where') { + type = typeof val; + + if (type !== 'string' && type !== 'function') { + throw new Error('Must have a string or function for $where'); + } + + if (type === 'function') { + obj[path] = val.toString(); + } + + continue; + } else if (path === '$expr') { + val = cast$expr(val, schema); + continue; + } else if (path === '$elemMatch') { + val = cast(schema, val, options, context); + } else if (path === '$text') { + val = castTextSearch(val, path); + } else if (path === '$comment' && !Object.hasOwn(schema.paths, '$comment')) { + val = castString(val, path); + obj[path] = val; + } else { + if (!schema) { + // no casting for Mixed types + continue; + } + + schematype = schema.path(path); + + // Check for embedded discriminator paths + if (!schematype) { + const split = path.split('.'); + let j = split.length; + while (j--) { + const pathFirstHalf = split.slice(0, j).join('.'); + const pathLastHalf = split.slice(j).join('.'); + const _schematype = schema.path(pathFirstHalf); + const discriminatorKey = _schematype?.schema?.options?.discriminatorKey; + + // gh-6027: if we haven't found the schematype but this path is + // underneath an embedded discriminator and the embedded discriminator + // key is in the query, use the embedded discriminator schema + if (_schematype?.schema?.discriminators != null && + discriminatorKey != null && + pathLastHalf !== discriminatorKey) { + const discriminatorVal = get(obj, pathFirstHalf + '.' + discriminatorKey); + const discriminators = _schematype.schema.discriminators; + if (typeof discriminatorVal === 'string' && discriminators[discriminatorVal] != null) { + + schematype = discriminators[discriminatorVal].path(pathLastHalf); + } else if (discriminatorVal != null && + Object.keys(discriminatorVal).length === 1 && + Array.isArray(discriminatorVal.$in) && + discriminatorVal.$in.length === 1 && + typeof discriminatorVal.$in[0] === 'string' && + discriminators[discriminatorVal.$in[0]] != null) { + schematype = discriminators[discriminatorVal.$in[0]].path(pathLastHalf); + } + } + } + } + + if (!schematype) { + // Handle potential embedded array queries + const split = path.split('.'); + let j = split.length; + let pathFirstHalf; + let pathLastHalf; + let remainingConds; + + // Find the part of the var path that is a path of the Schema + while (j--) { + pathFirstHalf = split.slice(0, j).join('.'); + schematype = schema.path(pathFirstHalf); + if (schematype) { + break; + } + } + + // If a substring of the input path resolves to an actual real path... + if (schematype) { + // Apply the casting; similar code for $elemMatch in schema/array.js + if (schematype.schema) { + remainingConds = {}; + pathLastHalf = split.slice(j).join('.'); + remainingConds[pathLastHalf] = val; + + const ret = cast(schematype.schema, remainingConds, options, context)[pathLastHalf]; + if (ret === void 0) { + delete obj[path]; + } else { + obj[path] = ret; + } + } else { + obj[path] = val; + } + continue; + } + + if (isObject(val)) { + // handle geo schemas that use object notation + // { loc: { long: Number, lat: Number } + + let geo = ''; + if (val.$near) { + geo = '$near'; + } else if (val.$nearSphere) { + geo = '$nearSphere'; + } else if (val.$within) { + geo = '$within'; + } else if (val.$geoIntersects) { + geo = '$geoIntersects'; + } else if (val.$geoWithin) { + geo = '$geoWithin'; + } + + if (geo) { + const numbertype = new Types.Number('__QueryCasting__', null, null, schema); + let value = val[geo]; + + if (val.$maxDistance != null) { + val.$maxDistance = numbertype.castForQuery( + null, + val.$maxDistance, + context + ); + } + if (val.$minDistance != null) { + val.$minDistance = numbertype.castForQuery( + null, + val.$minDistance, + context + ); + } + + if (geo === '$within') { + const withinType = value.$center + || value.$centerSphere + || value.$box + || value.$polygon; + + if (!withinType) { + throw new Error('Bad $within parameter: ' + JSON.stringify(val)); + } + + value = withinType; + } else if (geo === '$near' && + typeof value.type === 'string' && Array.isArray(value.coordinates)) { + // geojson; cast the coordinates + value = value.coordinates; + } else if ((geo === '$near' || geo === '$nearSphere' || geo === '$geoIntersects') && + value.$geometry && typeof value.$geometry.type === 'string' && + Array.isArray(value.$geometry.coordinates)) { + if (value.$maxDistance != null) { + value.$maxDistance = numbertype.castForQuery( + null, + value.$maxDistance, + context + ); + } + if (value.$minDistance != null) { + value.$minDistance = numbertype.castForQuery( + null, + value.$minDistance, + context + ); + } + if (isMongooseObject(value.$geometry)) { + value.$geometry = value.$geometry.toObject({ + transform: false, + virtuals: false + }); + } + value = value.$geometry.coordinates; + } else if (geo === '$geoWithin') { + if (value.$geometry) { + if (isMongooseObject(value.$geometry)) { + value.$geometry = value.$geometry.toObject({ virtuals: false }); + } + const geoWithinType = value.$geometry.type; + if (ALLOWED_GEOWITHIN_GEOJSON_TYPES.indexOf(geoWithinType) === -1) { + throw new Error('Invalid geoJSON type for $geoWithin "' + + geoWithinType + '", must be "Polygon" or "MultiPolygon"'); + } + value = value.$geometry.coordinates; + } else { + value = value.$box || value.$polygon || value.$center || + value.$centerSphere; + if (isMongooseObject(value)) { + value = value.toObject({ virtuals: false }); + } + } + } + + _cast(value, numbertype, context); + continue; + } + } + + if (schema.nested[path]) { + continue; + } + + const strict = 'strict' in options ? options.strict : schema.options.strict; + const strictQuery = getStrictQuery(options, schema._userProvidedOptions, schema.options, context); + if (options.upsert && strict) { + if (strict === 'throw') { + throw new StrictModeError(path); + } + throw new StrictModeError(path, 'Path "' + path + '" is not in ' + + 'schema, strict mode is `true`, and upsert is `true`.'); + } if (strictQuery === 'throw') { + throw new StrictModeError(path, 'Path "' + path + '" is not in ' + + 'schema and strictQuery is \'throw\'.'); + } else if (strictQuery) { + delete obj[path]; + } + } else if (val == null) { + continue; + } else if (utils.isPOJO(val)) { + any$conditionals = Object.keys(val).some(isOperator); + + if (!any$conditionals) { + obj[path] = schematype.castForQuery( + null, + val, + context + ); + } else { + const ks = Object.keys(val); + let $cond; + let k = ks.length; + + while (k--) { + $cond = ks[k]; + nested = val[$cond]; + if ($cond === '$elemMatch') { + if (nested && schematype?.schema != null) { + cast(schematype.schema, nested, options, context); + } else if (nested && schematype?.$isMongooseArray) { + if (utils.isPOJO(nested) && nested.$not != null) { + cast(schema, nested, options, context); + } else { + val[$cond] = schematype.castForQuery( + $cond, + nested, + context + ); + } + } + } else if ($cond === '$not') { + if (nested && schematype) { + _keys = Object.keys(nested); + if (_keys.length && isOperator(_keys[0])) { + for (const key in nested) { + nested[key] = schematype.castForQuery( + key, + nested[key], + context + ); + } + } else { + val[$cond] = schematype.castForQuery( + $cond, + nested, + context + ); + } + continue; + } + } else { + val[$cond] = schematype.castForQuery( + $cond, + nested, + context + ); + } + + } + } + } else if (Array.isArray(val) && ['Buffer', 'Array'].indexOf(schematype.instance) === -1 && !options.sanitizeFilter) { + const casted = []; + const valuesArray = val; + + for (const _val of valuesArray) { + casted.push(schematype.castForQuery( + null, + _val, + context + )); + } + + obj[path] = { $in: casted }; + } else { + obj[path] = schematype.castForQuery( + null, + val, + context + ); + } + } + } + + return obj; +}; + +function _cast(val, numbertype, context) { + if (Array.isArray(val)) { + val.forEach(function(item, i) { + if (Array.isArray(item) || isObject(item)) { + return _cast(item, numbertype, context); + } + val[i] = numbertype.castForQuery(null, item, context); + }); + } else { + const nearKeys = Object.keys(val); + let nearLen = nearKeys.length; + while (nearLen--) { + const nkey = nearKeys[nearLen]; + const item = val[nkey]; + if (Array.isArray(item) || isObject(item)) { + _cast(item, numbertype, context); + val[nkey] = item; + } else { + val[nkey] = numbertype.castForQuery({ val: item, context: context }); + } + } + } +} + +function getStrictQuery(queryOptions, schemaUserProvidedOptions, schemaOptions, context) { + if ('strictQuery' in queryOptions) { + return queryOptions.strictQuery; + } + if ('strictQuery' in schemaUserProvidedOptions) { + return schemaUserProvidedOptions.strictQuery; + } + const mongooseOptions = context?.mongooseCollection?.conn?.base?.options; + if (mongooseOptions) { + if ('strictQuery' in mongooseOptions) { + return mongooseOptions.strictQuery; + } + } + return schemaOptions.strictQuery; +} diff --git a/backend/node_modules/mongoose/lib/cast/bigint.js b/backend/node_modules/mongoose/lib/cast/bigint.js new file mode 100644 index 0000000..f0649d1 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/bigint.js @@ -0,0 +1,46 @@ +'use strict'; + +const { Long } = require('mongodb/lib/bson'); + +/** + * Given a value, cast it to a BigInt, or throw an `Error` if the value + * cannot be casted. `null` and `undefined` are considered valid. + * + * @param {any} value + * @return {bigint|null|undefined} + * @throws {Error} if `value` is not one of the allowed values + * @api private + */ + +const MAX_BIGINT = 9223372036854775807n; +const MIN_BIGINT = -9223372036854775808n; +const ERROR_MESSAGE = `Mongoose only supports BigInts between ${MIN_BIGINT} and ${MAX_BIGINT} because MongoDB does not support arbitrary precision integers`; + +module.exports = function castBigInt(val) { + if (val == null) { + return val; + } + if (val === '') { + return null; + } + if (typeof val === 'bigint') { + if (val > MAX_BIGINT || val < MIN_BIGINT) { + throw new Error(ERROR_MESSAGE); + } + return val; + } + + if (val instanceof Long) { + return val.toBigInt(); + } + + if (typeof val === 'string' || typeof val === 'number') { + val = BigInt(val); + if (val > MAX_BIGINT || val < MIN_BIGINT) { + throw new Error(ERROR_MESSAGE); + } + return val; + } + + throw new Error(`Cannot convert value to BigInt: "${val}"`); +}; diff --git a/backend/node_modules/mongoose/lib/cast/boolean.js b/backend/node_modules/mongoose/lib/cast/boolean.js new file mode 100644 index 0000000..557d5a9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/boolean.js @@ -0,0 +1,32 @@ +'use strict'; + +const CastError = require('../error/cast'); + +/** + * Given a value, cast it to a boolean, or throw a `CastError` if the value + * cannot be casted. `null` and `undefined` are considered valid. + * + * @param {any} value + * @param {string} [path] optional the path to set on the CastError + * @return {boolean|null|undefined} + * @throws {CastError} if `value` is not one of the allowed values + * @api private + */ + +module.exports = function castBoolean(value, path) { + if (module.exports.convertToTrue.has(value)) { + return true; + } + if (module.exports.convertToFalse.has(value)) { + return false; + } + + if (value == null) { + return value; + } + + throw new CastError('boolean', value, path); +}; + +module.exports.convertToTrue = new Set([true, 'true', 1, '1', 'yes']); +module.exports.convertToFalse = new Set([false, 'false', 0, '0', 'no']); diff --git a/backend/node_modules/mongoose/lib/cast/date.js b/backend/node_modules/mongoose/lib/cast/date.js new file mode 100644 index 0000000..c7f9b04 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/date.js @@ -0,0 +1,41 @@ +'use strict'; + +const assert = require('assert'); + +module.exports = function castDate(value) { + // Support empty string because of empty form values. Originally introduced + // in https://github.com/Automattic/mongoose/commit/efc72a1898fc3c33a319d915b8c5463a22938dfe + if (value == null || value === '') { + return null; + } + + if (value instanceof Date) { + assert.ok(!isNaN(value.valueOf())); + + return value; + } + + let date; + + assert.ok(typeof value !== 'boolean'); + + if (value instanceof Number || typeof value === 'number') { + date = new Date(value); + } else if (typeof value === 'string' && !isNaN(Number(value)) && (Number(value) >= 275761 || Number(value) < -271820)) { + // string representation of milliseconds take this path + date = new Date(Number(value)); + } else if (typeof value.valueOf === 'function') { + // support for moment.js. This is also the path strings will take because + // strings have a `valueOf()` + date = new Date(value.valueOf()); + } else { + // fallback + date = new Date(value); + } + + if (!isNaN(date.valueOf())) { + return date; + } + + assert.ok(false); +}; diff --git a/backend/node_modules/mongoose/lib/cast/decimal128.js b/backend/node_modules/mongoose/lib/cast/decimal128.js new file mode 100644 index 0000000..2bec997 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/decimal128.js @@ -0,0 +1,39 @@ +'use strict'; + +const Decimal128Type = require('../types/decimal128'); +const assert = require('assert'); + +module.exports = function castDecimal128(value) { + if (value == null) { + return value; + } + + if (typeof value === 'object' && typeof value.$numberDecimal === 'string') { + return Decimal128Type.fromString(value.$numberDecimal); + } + + if (value instanceof Decimal128Type) { + return value; + } + + if (typeof value === 'string') { + return Decimal128Type.fromString(value); + } + + if (typeof Buffer === 'function' && Buffer.isBuffer(value)) { + return new Decimal128Type(value); + } + if (typeof Uint8Array === 'function' && value instanceof Uint8Array) { + return new Decimal128Type(value); + } + + if (typeof value === 'number') { + return Decimal128Type.fromString(String(value)); + } + + if (typeof value.valueOf === 'function' && typeof value.valueOf() === 'string') { + return Decimal128Type.fromString(value.valueOf()); + } + + assert.ok(false); +}; diff --git a/backend/node_modules/mongoose/lib/cast/double.js b/backend/node_modules/mongoose/lib/cast/double.js new file mode 100644 index 0000000..66e8cc8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/double.js @@ -0,0 +1,50 @@ +'use strict'; + +const assert = require('assert'); +const BSON = require('mongodb/lib/bson'); +const isBsonType = require('../helpers/isBsonType'); + +/** + * Given a value, cast it to a IEEE 754-2008 floating point, or throw an `Error` if the value + * cannot be casted. `null`, `undefined`, and `NaN` are considered valid inputs. + * + * @param {any} value + * @return {number} + * @throws {Error} if `value` does not represent a IEEE 754-2008 floating point. If casting from a string, see [BSON Double.fromString API documentation](https://mongodb.github.io/node-mongodb-native/Next/classes/BSON.Double.html#fromString) + * @api private + */ + +module.exports = function castDouble(val) { + if (val == null || val === '') { + return null; + } + + let coercedVal; + if (isBsonType(val, 'Long')) { + coercedVal = val.toNumber(); + } else if (typeof val === 'string') { + try { + coercedVal = BSON.Double.fromString(val); + return coercedVal; + } catch { + assert.ok(false); + } + } else if (typeof val === 'object') { + const tempVal = val.valueOf() ?? val.toString(); + // ex: { a: 'im an object, valueOf: () => 'helloworld' } // throw an error + if (typeof tempVal === 'string') { + try { + coercedVal = BSON.Double.fromString(tempVal); + return coercedVal; + } catch { + assert.ok(false); + } + } else { + coercedVal = Number(tempVal); + } + } else { + coercedVal = Number(val); + } + + return new BSON.Double(coercedVal); +}; diff --git a/backend/node_modules/mongoose/lib/cast/int32.js b/backend/node_modules/mongoose/lib/cast/int32.js new file mode 100644 index 0000000..a1b384f --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/int32.js @@ -0,0 +1,36 @@ +'use strict'; + +const isBsonType = require('../helpers/isBsonType'); +const assert = require('assert'); + +/** + * Given a value, cast it to a Int32, or throw an `Error` if the value + * cannot be casted. `null` and `undefined` are considered valid. + * + * @param {any} value + * @return {number} + * @throws {Error} if `value` does not represent an integer, or is outside the bounds of an 32-bit integer. + * @api private + */ + +module.exports = function castInt32(val) { + if (val == null) { + return val; + } + if (val === '') { + return null; + } + + const coercedVal = isBsonType(val, 'Long') ? val.toNumber() : Number(val); + + const INT32_MAX = 0x7FFFFFFF; + const INT32_MIN = -0x80000000; + + if (coercedVal === (coercedVal | 0) && + coercedVal >= INT32_MIN && + coercedVal <= INT32_MAX + ) { + return coercedVal; + } + assert.ok(false); +}; diff --git a/backend/node_modules/mongoose/lib/cast/number.js b/backend/node_modules/mongoose/lib/cast/number.js new file mode 100644 index 0000000..2c1e3f3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/number.js @@ -0,0 +1,42 @@ +'use strict'; + +/** + * Given a value, cast it to a number, or throw an `Error` if the value + * cannot be casted. `null` and `undefined` are considered valid. + * + * @param {any} value + * @return {number} + * @throws {Error} if `value` is not one of the allowed values + * @api private + */ + +module.exports = function castNumber(val) { + if (val == null) { + return val; + } + if (val === '') { + return null; + } + + if (typeof val === 'string' || typeof val === 'boolean') { + val = Number(val); + } + + if (isNaN(val)) { + throw new Error('Cast to Number failed: value is not a valid number'); + } + if (val instanceof Number) { + return val.valueOf(); + } + if (typeof val === 'number') { + return val; + } + if (!Array.isArray(val) && typeof val.valueOf === 'function') { + return Number(val.valueOf()); + } + if (val.toString && !Array.isArray(val) && val.toString() == Number(val)) { + return Number(val); + } + + throw new Error('Cast to Number failed: value is not a valid number'); +}; diff --git a/backend/node_modules/mongoose/lib/cast/objectid.js b/backend/node_modules/mongoose/lib/cast/objectid.js new file mode 100644 index 0000000..095a0a1 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/objectid.js @@ -0,0 +1,29 @@ +'use strict'; + +const isBsonType = require('../helpers/isBsonType'); +const ObjectId = require('../types/objectid'); + +module.exports = function castObjectId(value) { + if (value == null) { + return value; + } + + if (isBsonType(value, 'ObjectId')) { + return value; + } + + if (value._id) { + if (isBsonType(value._id, 'ObjectId')) { + return value._id; + } + if (value._id.toString instanceof Function) { + return new ObjectId(value._id.toString()); + } + } + + if (value.toString instanceof Function) { + return new ObjectId(value.toString()); + } + + return new ObjectId(value); +}; diff --git a/backend/node_modules/mongoose/lib/cast/string.js b/backend/node_modules/mongoose/lib/cast/string.js new file mode 100644 index 0000000..e5198b9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/string.js @@ -0,0 +1,37 @@ +'use strict'; + +const CastError = require('../error/cast'); + +/** + * Given a value, cast it to a string, or throw a `CastError` if the value + * cannot be casted. `null` and `undefined` are considered valid. + * + * @param {any} value + * @param {string} [path] optional the path to set on the CastError + * @return {string|null|undefined} + * @throws {CastError} + * @api private + */ + +module.exports = function castString(value, path) { + // If null or undefined + if (value == null) { + return value; + } + + // handle documents being passed + if (typeof value?._id === 'string') { + return value._id; + } + + // Re: gh-647 and gh-3030, we're ok with casting using `toString()` + // **unless** its the default Object.toString, because "[object Object]" + // doesn't really qualify as useful data + if (value.toString && + value.toString !== Object.prototype.toString && + !Array.isArray(value)) { + return value.toString(); + } + + throw new CastError('string', value, path); +}; diff --git a/backend/node_modules/mongoose/lib/cast/uuid.js b/backend/node_modules/mongoose/lib/cast/uuid.js new file mode 100644 index 0000000..05b867c --- /dev/null +++ b/backend/node_modules/mongoose/lib/cast/uuid.js @@ -0,0 +1,35 @@ +'use strict'; + +const UUID = require('mongodb/lib/bson').UUID; + +const UUID_FORMAT = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}/i; + +module.exports = function castUUID(value) { + if (value == null) { + return value; + } + + if (value instanceof UUID) { + return value; + } + if (typeof value === 'string') { + if (UUID_FORMAT.test(value)) { + return new UUID(value); + } else { + throw new Error(`"${value}" is not a valid UUID string`); + } + } + + // Re: gh-647 and gh-3030, we're ok with casting using `toString()` + // **unless** its the default Object.toString, because "[object Object]" + // doesn't really qualify as useful data + if (value.toString && value.toString !== Object.prototype.toString) { + if (UUID_FORMAT.test(value.toString())) { + return new UUID(value.toString()); + } + } + + throw new Error(`"${value}" cannot be casted to a UUID`); +}; + +module.exports.UUID_FORMAT = UUID_FORMAT; diff --git a/backend/node_modules/mongoose/lib/collection.js b/backend/node_modules/mongoose/lib/collection.js new file mode 100644 index 0000000..2506e14 --- /dev/null +++ b/backend/node_modules/mongoose/lib/collection.js @@ -0,0 +1,321 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const EventEmitter = require('events').EventEmitter; +const STATES = require('./connectionState'); +const immediate = require('./helpers/immediate'); + +/** + * Abstract Collection constructor + * + * This is the base class that drivers inherit from and implement. + * + * @param {string} name name of the collection + * @param {Connection} conn A MongooseConnection instance + * @param {object} [opts] optional collection options + * @api public + */ + +function Collection(name, conn, opts) { + if (opts === void 0) { + opts = {}; + } + + this.opts = opts; + this.name = name; + this.collectionName = name; + this.conn = conn; + this.queue = []; + this.buffer = !conn?._hasOpened; + this.emitter = new EventEmitter(); + + if (STATES.connected === this.conn.readyState) { + this.onOpen(); + } +} + +/** + * The collection name + * + * @api public + * @property name + */ + +Collection.prototype.name; + +/** + * The collection name + * + * @api public + * @property collectionName + */ + +Collection.prototype.collectionName; + +/** + * The Connection instance + * + * @api public + * @property conn + */ + +Collection.prototype.conn; + +/** + * Called when the database connects + * + * @api private + */ + +Collection.prototype.onOpen = function() { + this.buffer = false; + immediate(() => this.doQueue()); +}; + +/** + * Called when the database disconnects + * + * @api private + */ + +Collection.prototype.onClose = function() {}; + +/** + * Queues a method for later execution when its + * database connection opens. + * + * @param {string} name name of the method to queue + * @param {Array} args arguments to pass to the method when executed + * @api private + */ + +Collection.prototype.addQueue = function(name, args) { + this.queue.push([name, args]); + return this; +}; + +/** + * Removes a queued method + * + * @param {string} name name of the method to queue + * @param {Array} args arguments to pass to the method when executed + * @api private + */ + +Collection.prototype.removeQueue = function(name, args) { + const index = this.queue.findIndex(v => v[0] === name && v[1] === args); + if (index === -1) { + return false; + } + this.queue.splice(index, 1); + return true; +}; + +/** + * Executes all queued methods and clears the queue. + * + * @api private + */ + +Collection.prototype.doQueue = function() { + for (const method of this.queue) { + if (typeof method[0] === 'function') { + method[0].apply(this, method[1]); + } else { + this[method[0]].apply(this, method[1]); + } + } + this.queue = []; + const _this = this; + immediate(function() { + _this.emitter.emit('queue'); + }); + return this; +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.ensureIndex = function() { + throw new Error('Collection#ensureIndex unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.createIndex = function() { + throw new Error('Collection#createIndex unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findAndModify = function() { + throw new Error('Collection#findAndModify unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findOneAndUpdate = function() { + throw new Error('Collection#findOneAndUpdate unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findOneAndDelete = function() { + throw new Error('Collection#findOneAndDelete unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findOneAndReplace = function() { + throw new Error('Collection#findOneAndReplace unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.findOne = function() { + throw new Error('Collection#findOne unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.find = function() { + throw new Error('Collection#find unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.insert = function() { + throw new Error('Collection#insert unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.insertOne = function() { + throw new Error('Collection#insertOne unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.insertMany = function() { + throw new Error('Collection#insertMany unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.save = function() { + throw new Error('Collection#save unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.updateOne = function() { + throw new Error('Collection#updateOne unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.updateMany = function() { + throw new Error('Collection#updateMany unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.deleteOne = function() { + throw new Error('Collection#deleteOne unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.deleteMany = function() { + throw new Error('Collection#deleteMany unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.getIndexes = function() { + throw new Error('Collection#getIndexes unimplemented by driver'); +}; + +/** + * Abstract method that drivers must implement. + */ + +Collection.prototype.watch = function() { + throw new Error('Collection#watch unimplemented by driver'); +}; + +/*! + * ignore + */ + +Collection.prototype._shouldBufferCommands = function _shouldBufferCommands() { + const opts = this.opts; + + if (opts.bufferCommands != null) { + return opts.bufferCommands; + } + if (opts?.schemaUserProvidedOptions?.bufferCommands != null) { + return opts.schemaUserProvidedOptions.bufferCommands; + } + + return this.conn._shouldBufferCommands(); +}; + +/*! + * ignore + */ + +Collection.prototype._getBufferTimeoutMS = function _getBufferTimeoutMS() { + const conn = this.conn; + const opts = this.opts; + + if (opts.bufferTimeoutMS != null) { + return opts.bufferTimeoutMS; + } + if (opts?.schemaUserProvidedOptions?.bufferTimeoutMS != null) { + return opts.schemaUserProvidedOptions.bufferTimeoutMS; + } + return conn._getBufferTimeoutMS(); +}; + +/*! + * Module exports. + */ + +module.exports = Collection; diff --git a/backend/node_modules/mongoose/lib/connection.js b/backend/node_modules/mongoose/lib/connection.js new file mode 100644 index 0000000..163d33e --- /dev/null +++ b/backend/node_modules/mongoose/lib/connection.js @@ -0,0 +1,1841 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const ChangeStream = require('./cursor/changeStream'); +const EventEmitter = require('events').EventEmitter; +const Schema = require('./schema'); +const STATES = require('./connectionState'); +const MongooseBulkWriteError = require('./error/bulkWriteError'); +const MongooseError = require('./error/index'); +const ServerSelectionError = require('./error/serverSelection'); +const SyncIndexesError = require('./error/syncIndexes'); +const applyPlugins = require('./helpers/schema/applyPlugins'); +const clone = require('./helpers/clone'); +const driver = require('./driver'); +const get = require('./helpers/get'); +const getDefaultBulkwriteResult = require('./helpers/getDefaultBulkwriteResult'); +const immediate = require('./helpers/immediate'); +const utils = require('./utils'); +const CreateCollectionsError = require('./error/createCollectionsError'); +const castBulkWrite = require('./helpers/model/castBulkWrite'); +const { modelSymbol } = require('./helpers/symbols'); +const isPromise = require('./helpers/isPromise'); +const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult'); + +const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol; +const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments; + +// Snapshot the native Date constructor to ensure both Date.now() and new Date() (and other Date methods) +// bypass timer mocks such as those set up by useFakeTimers(). +const Date = globalThis.Date; + +/** + * A list of authentication mechanisms that don't require a password for authentication. + * This is used by the authMechanismDoesNotRequirePassword method. + * + * @api private + */ +const noPasswordAuthMechanisms = [ + 'MONGODB-X509' +]; + +/** + * Connection constructor + * + * For practical reasons, a Connection equals a Db. + * + * @param {Mongoose} base a mongoose instance + * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#class-eventemitter + * @event `connecting`: Emitted when `connection.openUri()` is executed on this connection. + * @event `connected`: Emitted when this connection successfully connects to the db. May be emitted _multiple_ times in `reconnected` scenarios. + * @event `open`: Emitted after we `connected` and `onOpen` is executed on all of this connection's models. + * @event `disconnecting`: Emitted when `connection.close()` was executed. + * @event `disconnected`: Emitted after getting disconnected from the db. + * @event `close`: Emitted after we `disconnected` and `onClose` executed on all of this connection's models. + * @event `reconnected`: Emitted after we `connected` and subsequently `disconnected`, followed by successfully another successful connection. + * @event `error`: Emitted when an error occurs on this connection. + * @event `operation-start`: Emitted when a call to the MongoDB Node.js driver, like a `find()` or `insertOne()`, happens on any collection tied to this connection. + * @event `operation-end`: Emitted when a call to the MongoDB Node.js driver, like a `find()` or `insertOne()`, either succeeds or errors. + * @api public + */ + +function Connection(base) { + this.base = base; + this.collections = {}; + this.models = {}; + this.config = {}; + this.replica = false; + this.options = null; + this.otherDbs = []; // FIXME: To be replaced with relatedDbs + this.relatedDbs = {}; // Hashmap of other dbs that share underlying connection + this.states = STATES; + this._readyState = STATES.disconnected; + this._closeCalled = false; + this._hasOpened = false; + this.plugins = []; + if (typeof base === 'undefined' || !base.connections.length) { + this.id = 0; + } else { + this.id = base.nextConnectionId; + } + + // Internal queue of objects `{ fn, ctx, args }` that Mongoose calls when this connection is successfully + // opened. In `onOpen()`, Mongoose calls every entry in `_queue` and empties the queue. + this._queue = []; +} + +/*! + * Inherit from EventEmitter + */ + +Object.setPrototypeOf(Connection.prototype, EventEmitter.prototype); + +/** + * Connection ready state + * + * - 0 = disconnected + * - 1 = connected + * - 2 = connecting + * - 3 = disconnecting + * + * Each state change emits its associated event name. + * + * #### Example: + * + * conn.on('connected', callback); + * conn.on('disconnected', callback); + * + * @property readyState + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'readyState', { + get: function() { + // If connection thinks it is connected, but we haven't received a heartbeat in 2 heartbeat intervals, + // that likely means the connection is stale (potentially due to frozen AWS Lambda container) + if ( + this._readyState === STATES.connected && + this._lastHeartbeatAt != null && + // LoadBalanced topology (behind haproxy, including Atlas serverless instances) don't use heartbeats, + // so we can't use this check in that case. + this.client?.topology?.s?.description?.type !== 'LoadBalanced' && + typeof this.client?.topology?.s?.description?.heartbeatFrequencyMS === 'number' && + Date.now() - this._lastHeartbeatAt >= this.client.topology.s.description.heartbeatFrequencyMS * 2) { + return STATES.disconnected; + } + return this._readyState; + }, + set: function(val) { + if (!(val in STATES)) { + throw new MongooseError('Invalid connection state: ' + val); + } + + if (this._readyState !== val) { + this._readyState = val; + // [legacy] loop over the otherDbs on this connection and change their state + for (const db of this.otherDbs) { + db.readyState = val; + } + + if (STATES.connected === val) { + this._hasOpened = true; + } + + this.emit(STATES[val]); + } + } +}); + +/** + * Gets the value of the option `key`. Equivalent to `conn.options[key]` + * + * #### Example: + * + * conn.get('test'); // returns the 'test' value + * + * @param {string} key + * @method get + * @api public + */ + +Connection.prototype.get = function getOption(key) { + if (Object.hasOwn(this.config, key)) { + return this.config[key]; + } + + return get(this.options, key); +}; + +/** + * Sets the value of the option `key`. Equivalent to `conn.options[key] = val` + * + * Supported options include: + * + * - `maxTimeMS`: Set [`maxTimeMS`](https://mongoosejs.com/docs/api/query.html#Query.prototype.maxTimeMS()) for all queries on this connection. + * - 'debug': If `true`, prints the operations mongoose sends to MongoDB to the console. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arguments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`. + * + * #### Example: + * + * conn.set('test', 'foo'); + * conn.get('test'); // 'foo' + * conn.options.test; // 'foo' + * + * @param {string} key + * @param {any} val + * @method set + * @api public + */ + +Connection.prototype.set = function setOption(key, val) { + if (Object.hasOwn(this.config, key)) { + this.config[key] = val; + return val; + } + + this.options = this.options || {}; + this.options[key] = val; + return val; +}; + +/** + * A hash of the collections associated with this connection + * + * @property collections + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.collections; + +/** + * The name of the database this connection points to. + * + * #### Example: + * + * mongoose.createConnection('mongodb://127.0.0.1:27017/mydb').name; // "mydb" + * + * @property name + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.name; + +/** + * A [POJO](https://masteringjs.io/tutorials/fundamentals/pojo) containing + * a map from model names to models. Contains all models that have been + * added to this connection using [`Connection#model()`](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.model()). + * + * #### Example: + * + * const conn = mongoose.createConnection(); + * const Test = conn.model('Test', mongoose.Schema({ name: String })); + * + * Object.keys(conn.models).length; // 1 + * conn.models.Test === Test; // true + * + * @property models + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.models; + +/** + * A number identifier for this connection. Used for debugging when + * you have [multiple connections](https://mongoosejs.com/docs/connections.html#multiple_connections). + * + * #### Example: + * + * // The default connection has `id = 0` + * mongoose.connection.id; // 0 + * + * // If you create a new connection, Mongoose increments id + * const conn = mongoose.createConnection(); + * conn.id; // 1 + * + * @property id + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.id; + +/** + * The plugins that will be applied to all models created on this connection. + * + * #### Example: + * + * const db = mongoose.createConnection('mongodb://127.0.0.1:27017/mydb'); + * db.plugin(() => console.log('Applied')); + * db.plugins.length; // 1 + * + * db.model('Test', new Schema({})); // Prints "Applied" + * + * @property plugins + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'plugins', { + configurable: false, + enumerable: true, + writable: true +}); + +/** + * The host name portion of the URI. If multiple hosts, such as a replica set, + * this will contain the first host name in the URI + * + * #### Example: + * + * mongoose.createConnection('mongodb://127.0.0.1:27017/mydb').host; // "127.0.0.1" + * + * @property host + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'host', { + configurable: true, + enumerable: true, + writable: true +}); + +/** + * The port portion of the URI. If multiple hosts, such as a replica set, + * this will contain the port from the first host name in the URI. + * + * #### Example: + * + * mongoose.createConnection('mongodb://127.0.0.1:27017/mydb').port; // 27017 + * + * @property port + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'port', { + configurable: true, + enumerable: true, + writable: true +}); + +/** + * The username specified in the URI + * + * #### Example: + * + * mongoose.createConnection('mongodb://val:psw@127.0.0.1:27017/mydb').user; // "val" + * + * @property user + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'user', { + configurable: true, + enumerable: true, + writable: true +}); + +/** + * The password specified in the URI + * + * #### Example: + * + * mongoose.createConnection('mongodb://val:psw@127.0.0.1:27017/mydb').pass; // "psw" + * + * @property pass + * @memberOf Connection + * @instance + * @api public + */ + +Object.defineProperty(Connection.prototype, 'pass', { + configurable: true, + enumerable: true, + writable: true +}); + +/** + * The mongodb.Db instance, set when the connection is opened + * + * @property db + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.db; + +/** + * The MongoClient instance this connection uses to talk to MongoDB. Mongoose automatically sets this property + * when the connection is opened. + * + * @property client + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.client; + +/** + * A hash of the global options that are associated with this connection + * + * @property config + * @memberOf Connection + * @instance + * @api public + */ + +Connection.prototype.config; + +/** + * Helper for `createCollection()`. Will explicitly create the given collection + * with specified options. Used to create [capped collections](https://www.mongodb.com/docs/manual/core/capped-collections/) + * and [views](https://www.mongodb.com/docs/manual/core/views/) from mongoose. + * + * Options are passed down without modification to the [MongoDB driver's `createCollection()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection) + * + * @method createCollection + * @param {string} collection The collection to create + * @param {object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection) + * @return {Promise} + * @api public + */ + +Connection.prototype.createCollection = async function createCollection(collection, options) { + if (typeof options === 'function' || (arguments.length >= 3 && typeof arguments[2] === 'function')) { + throw new MongooseError('Connection.prototype.createCollection() no longer accepts a callback'); + } + + await this._waitForConnect(); + + return this.db.createCollection(collection, options); +}; + +/** + * _Requires MongoDB Server 8.0 or greater_. Executes bulk write operations across multiple models in a single operation. + * You must specify the `model` for each operation: Mongoose will use `model` for casting and validation, as well as + * determining which collection to apply the operation to. + * + * #### Example: + * const Test = mongoose.model('Test', new Schema({ name: String })); + * + * await db.bulkWrite([ + * { model: Test, name: 'insertOne', document: { name: 'test1' } }, // Can specify model as a Model class... + * { model: 'Test', name: 'insertOne', document: { name: 'test2' } } // or as a model name + * ], { ordered: false }); + * + * @method bulkWrite + * @param {Array} ops + * @param {object} [options] + * @param {boolean} [options.ordered] If false, perform unordered operations. If true, perform ordered operations. + * @param {Session} [options.session] The session to use for the operation. + * @return {Promise} + * @see MongoDB https://www.mongodb.com/docs/manual/reference/command/bulkWrite/#mongodb-dbcommand-dbcmd.bulkWrite + * @api public + */ + + +Connection.prototype.bulkWrite = async function bulkWrite(ops, options) { + await this._waitForConnect(); + options = options || {}; + + const ordered = options.ordered == null ? true : options.ordered; + const asyncLocalStorage = this.base.transactionAsyncLocalStorage?.getStore(); + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { + options = { ...options, session: asyncLocalStorage.session }; + } + + const now = this.base.now(); + + let res = null; + if (ordered) { + const opsToSend = []; + for (const op of ops) { + if (typeof op.model !== 'string' && !op.model?.[modelSymbol]) { + throw new MongooseError('Must specify model in Connection.prototype.bulkWrite() operations'); + } + const Model = op.model[modelSymbol] ? op.model : this.model(op.model); + + if (op.name == null) { + throw new MongooseError('Must specify operation name in Connection.prototype.bulkWrite()'); + } + if (!Object.hasOwn(castBulkWrite.cast, op.name)) { + throw new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`); + } + + await castBulkWrite.cast[op.name](Model, op, options, now); + opsToSend.push({ ...op, namespace: Model.namespace() }); + } + + res = await this.client.bulkWrite(opsToSend, options); + } else { + const validOps = []; + const validOpIndexes = []; + let validationErrors = []; + const asyncValidations = []; + const results = []; + for (let i = 0; i < ops.length; ++i) { + const op = ops[i]; + if (typeof op.model !== 'string' && !op.model?.[modelSymbol]) { + const error = new MongooseError('Must specify model in Connection.prototype.bulkWrite() operations'); + validationErrors.push({ index: i, error: error }); + results[i] = error; + continue; + } + let Model; + try { + Model = op.model[modelSymbol] ? op.model : this.model(op.model); + } catch (error) { + validationErrors.push({ index: i, error: error }); + continue; + } + if (op.name == null) { + const error = new MongooseError('Must specify operation name in Connection.prototype.bulkWrite()'); + validationErrors.push({ index: i, error: error }); + results[i] = error; + continue; + } + if (!Object.hasOwn(castBulkWrite.cast, op.name)) { + const error = new MongooseError(`Unrecognized bulkWrite() operation name ${op.name}`); + validationErrors.push({ index: i, error: error }); + results[i] = error; + continue; + } + + let maybePromise = null; + try { + maybePromise = castBulkWrite.cast[op.name](Model, op, options, now); + } catch (error) { + validationErrors.push({ index: i, error: error }); + results[i] = error; + continue; + } + if (isPromise(maybePromise)) { + asyncValidations.push( + maybePromise.then( + () => { + validOps.push({ ...op, namespace: Model.namespace() }); + validOpIndexes.push(i); + }, + error => { + validationErrors.push({ index: i, error: error }); + results[i] = error; + } + ) + ); + } else { + validOps.push({ ...op, namespace: Model.namespace() }); + validOpIndexes.push(i); + } + } + + if (asyncValidations.length > 0) { + await Promise.all(asyncValidations); + } + + validationErrors = validationErrors. + sort((v1, v2) => v1.index - v2.index). + map(v => v.error); + + if (validOps.length === 0) { + if (options.throwOnValidationError && validationErrors.length) { + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'bulkWrite' + ); + } + const BulkWriteResult = this.base.driver.get().BulkWriteResult; + const res = new BulkWriteResult(getDefaultBulkwriteResult(), false); + return decorateBulkWriteResult(res, validationErrors, results); + } + + let error; + [res, error] = await this.client.bulkWrite(validOps, options). + then(res => ([res, null])). + catch(err => ([null, err])); + + for (let i = 0; i < validOpIndexes.length; ++i) { + results[validOpIndexes[i]] = null; + } + if (error) { + if (validationErrors.length > 0) { + decorateBulkWriteResult(error, validationErrors, results); + error.mongoose = error.mongoose || {}; + error.mongoose.validationErrors = validationErrors; + } + } + + if (validationErrors.length > 0) { + if (options.throwOnValidationError) { + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'bulkWrite' + ); + } else { + decorateBulkWriteResult(res, validationErrors, results); + } + } + } + + return res; +}; + +/** + * Calls `createCollection()` on a models in a series. + * + * @method createCollections + * @param {boolean} continueOnError When true, will continue to create collections and create a new error class for the collections that errored. + * @returns {Promise} + * @api public + */ + +Connection.prototype.createCollections = async function createCollections(options = {}) { + const result = {}; + const errorsMap = { }; + + const { continueOnError } = options; + delete options.continueOnError; + for (const model of Object.values(this.models)) { + try { + result[model.modelName] = await model.createCollection({}); + } catch (err) { + if (!continueOnError) { + errorsMap[model.modelName] = err; + break; + } else { + result[model.modelName] = err; + } + } + } + + if (!continueOnError && utils.hasOwnKeys(errorsMap)) { + const message = Object.entries(errorsMap).map(([modelName, err]) => `${modelName}: ${err.message}`).join(', '); + const createCollectionsError = new CreateCollectionsError(message, errorsMap); + throw createCollectionsError; + } + return result; +}; + +/** + * A convenience wrapper for `connection.client.withSession()`. + * + * #### Example: + * + * await conn.withSession(async session => { + * const doc = await TestModel.findOne().session(session); + * }); + * + * @method withSession + * @param {Function} executor called with 1 argument: a `ClientSession` instance + * @return {Promise} resolves to the return value of the executor function + * @api public + */ + +Connection.prototype.withSession = async function withSession(executor) { + if (arguments.length === 0) { + throw new MongooseError('Please provide an executor function'); + } + return await this.client.withSession(executor); +}; + +/** + * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](https://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + * + * #### Example: + * + * const session = await conn.startSession(); + * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session }); + * await doc.deleteOne(); + * // `doc` will always be null, even if reading from a replica set + * // secondary. Without causal consistency, it is possible to + * // get a doc back from the below query if the query reads from a + * // secondary that is experiencing replication lag. + * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' }); + * + * + * @method startSession + * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#startSession) + * @param {boolean} [options.causalConsistency=true] set to false to disable causal consistency + * @return {Promise} promise that resolves to a MongoDB driver `ClientSession` + * @api public + */ + +Connection.prototype.startSession = async function startSession(options) { + if (arguments.length >= 2 && typeof arguments[1] === 'function') { + throw new MongooseError('Connection.prototype.startSession() no longer accepts a callback'); + } + + await this._waitForConnect(); + + const session = this.client.startSession(options); + return session; +}; + +/** + * _Requires MongoDB >= 3.6.0._ Executes the wrapped async function + * in a transaction. Mongoose will commit the transaction if the + * async function executes successfully and attempt to retry if + * there was a retriable error. + * + * Calls the MongoDB driver's [`session.withTransaction()`](https://mongodb.github.io/node-mongodb-native/7.0/classes/ClientSession.html#withTransaction), + * but also handles resetting Mongoose document state as shown below. + * + * #### Example: + * + * const doc = new Person({ name: 'Will Riker' }); + * await db.transaction(async function setRank(session) { + * doc.rank = 'Captain'; + * await doc.save({ session }); + * doc.isNew; // false + * + * // Throw an error to abort the transaction + * throw new MongooseError('Oops!'); + * },{ readPreference: 'primary' }).catch(() => {}); + * + * // true, `transaction()` reset the document's state because the + * // transaction was aborted. + * doc.isNew; + * + * @method transaction + * @param {Function} fn Function to execute in a transaction + * @param {mongodb.TransactionOptions} [options] Optional settings for the transaction + * @return {Promise} promise that is fulfilled if Mongoose successfully committed the transaction, or rejects if the transaction was aborted or if Mongoose failed to commit the transaction. If fulfilled, the promise resolves to a MongoDB command result. + * @api public + */ + +Connection.prototype.transaction = function transaction(fn, options) { + return this.startSession().then(session => { + session[sessionNewDocuments] = new Map(); + return session.withTransaction(() => _wrapUserTransaction(fn, session, this.base), options). + then(res => { + delete session[sessionNewDocuments]; + return res; + }). + catch(err => { + delete session[sessionNewDocuments]; + throw err; + }). + finally(() => { + session.endSession().catch(() => {}); + }); + }); +}; + +/*! + * Reset document state in between transaction retries re: gh-13698 + */ + +async function _wrapUserTransaction(fn, session, mongoose) { + try { + const res = mongoose.transactionAsyncLocalStorage == null + ? await fn(session) + : await new Promise(resolve => { + mongoose.transactionAsyncLocalStorage.run( + { session }, + () => resolve(fn(session)) + ); + }); + return res; + } catch (err) { + _resetSessionDocuments(session); + throw err; + } +} + +/*! + * If transaction was aborted, we need to reset newly inserted documents' `isNew`. + */ +function _resetSessionDocuments(session) { + for (const doc of session[sessionNewDocuments].keys()) { + const state = session[sessionNewDocuments].get(doc); + if (Object.hasOwn(state, 'isNew')) { + doc.$isNew = state.isNew; + } + if (Object.hasOwn(state, 'versionKey')) { + doc.set(doc.schema.options.versionKey, state.versionKey); + } + + if (state.modifiedPaths.length > 0 && doc.$__.activePaths.states.modify == null) { + doc.$__.activePaths.states.modify = {}; + } + for (const path of state.modifiedPaths) { + const currentState = doc.$__.activePaths.paths[path]; + if (currentState != null) { + delete doc.$__.activePaths[currentState][path]; + } + doc.$__.activePaths.paths[path] = 'modify'; + doc.$__.activePaths.states.modify[path] = true; + } + + for (const path of state.atomics.keys()) { + const val = doc.$__getValue(path); + if (val == null) { + continue; + } + val[arrayAtomicsSymbol] = state.atomics.get(path); + } + } +} + +/** + * Helper for `dropCollection()`. Will delete the given collection, including + * all documents and indexes. + * + * @method dropCollection + * @param {string} collection The collection to delete + * @return {Promise} + * @api public + */ + +Connection.prototype.dropCollection = async function dropCollection(collection) { + if (arguments.length >= 2 && typeof arguments[1] === 'function') { + throw new MongooseError('Connection.prototype.dropCollection() no longer accepts a callback'); + } + + await this._waitForConnect(); + + return this.db.dropCollection(collection); +}; + +/** + * Waits for connection to be established, so the connection has a `client` + * + * @param {boolean} [noTimeout=false] if set, don't put a timeout on the operation. Used internally so `mongoose.model()` doesn't leave open handles. + * @return {Promise} + * @api private + */ + +Connection.prototype._waitForConnect = async function _waitForConnect(noTimeout) { + if ((this.readyState === STATES.connecting || this.readyState === STATES.disconnected) && this._shouldBufferCommands()) { + const bufferTimeoutMS = this._getBufferTimeoutMS(); + let timeout = null; + let timedOut = false; + // The element that this function pushes onto `_queue`, stored to make it easy to remove later + const queueElement = {}; + + // Mongoose executes all elements in `_queue` when initial connection succeeds in `onOpen()`. + const waitForConnectPromise = new Promise(resolve => { + queueElement.fn = resolve; + this._queue.push(queueElement); + }); + + if (noTimeout) { + await waitForConnectPromise; + } else { + await Promise.race([ + waitForConnectPromise, + new Promise(resolve => { + timeout = setTimeout( + () => { + timedOut = true; + resolve(); + }, + bufferTimeoutMS + ); + }) + ]); + } + + if (timedOut) { + const index = this._queue.indexOf(queueElement); + if (index !== -1) { + this._queue.splice(index, 1); + } + const message = 'Connection operation buffering timed out after ' + bufferTimeoutMS + 'ms'; + throw new MongooseError(message); + } else if (timeout != null) { + // Not strictly necessary, but avoid the extra overhead of creating a new MongooseError + // in case of success + clearTimeout(timeout); + } + } +}; + +/*! + * Get the default buffer timeout for this connection + */ + +Connection.prototype._getBufferTimeoutMS = function _getBufferTimeoutMS() { + if (this.config.bufferTimeoutMS != null) { + return this.config.bufferTimeoutMS; + } + if (this.base?.get('bufferTimeoutMS') != null) { + return this.base.get('bufferTimeoutMS'); + } + return 10000; +}; + +/** + * Helper for MongoDB Node driver's `listCollections()`. + * Returns an array of collection objects. + * + * @method listCollections + * @return {Promise>} + * @api public + */ + +Connection.prototype.listCollections = async function listCollections() { + await this._waitForConnect(); + + const cursor = this.db.listCollections(); + return await cursor.toArray(); +}; + +/** + * Helper for MongoDB Node driver's `listDatabases()`. + * Returns an object with a `databases` property that contains an + * array of database objects. + * + * #### Example: + * const { databases } = await mongoose.connection.listDatabases(); + * databases; // [{ name: 'mongoose_test', sizeOnDisk: 0, empty: false }] + * + * @method listCollections + * @return {Promise>} + * @api public + */ + +Connection.prototype.listDatabases = async function listDatabases() { + // Implemented in `lib/drivers/node-mongodb-native/connection.js` + throw new MongooseError('listDatabases() not implemented by driver'); +}; + +/** + * Helper for `dropDatabase()`. Deletes the given database, including all + * collections, documents, and indexes. + * + * #### Example: + * + * const conn = mongoose.createConnection('mongodb://127.0.0.1:27017/mydb'); + * // Deletes the entire 'mydb' database + * await conn.dropDatabase(); + * + * @method dropDatabase + * @return {Promise} + * @api public + */ + +Connection.prototype.dropDatabase = async function dropDatabase() { + if (arguments.length >= 1 && typeof arguments[0] === 'function') { + throw new MongooseError('Connection.prototype.dropDatabase() no longer accepts a callback'); + } + + await this._waitForConnect(); + + // If `dropDatabase()` is called, this model's collection will not be + // init-ed. It is sufficiently common to call `dropDatabase()` after + // `mongoose.connect()` but before creating models that we want to + // support this. See gh-6796 + for (const model of Object.values(this.models)) { + delete model.$init; + } + + return this.db.dropDatabase(); +}; + +/*! + * ignore + */ + +Connection.prototype._shouldBufferCommands = function _shouldBufferCommands() { + if (this.config.bufferCommands != null) { + return this.config.bufferCommands; + } + if (this.base.get('bufferCommands') != null) { + return this.base.get('bufferCommands'); + } + return true; +}; + +/** + * error + * + * Graceful error handling, passes error to callback + * if available, else emits error on the connection. + * + * @param {Error} err + * @param {Function} callback optional + * @emits "error" Emits the `error` event with the given `err`, unless a callback is specified + * @returns {Promise|null} Returns a rejected Promise if no `callback` is given. + * @api private + */ + +Connection.prototype.error = function error(err, callback) { + if (callback) { + callback(err); + return null; + } + if (this.listeners('error').length > 0) { + this.emit('error', err); + } + return Promise.reject(err); +}; + +/** + * Called when the connection is opened + * + * @emits "open" + * @api private + */ + +Connection.prototype.onOpen = function() { + this.readyState = STATES.connected; + + for (const d of this._queue) { + d.fn.apply(d.ctx, d.args); + } + this._queue = []; + + // avoid having the collection subscribe to our event emitter + // to prevent 0.3 warning + for (const i in this.collections) { + if (Object.hasOwn(this.collections, i)) { + this.collections[i].onOpen(); + } + } + + this.emit('open'); +}; + +/** + * Opens the connection with a URI using `MongoClient.connect()`. + * + * @param {string} uri The URI to connect with. + * @param {object} [options] Passed on to [`MongoClient.connect`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#connect) + * @param {boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](https://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection. + * @param {number} [options.bufferTimeoutMS=10000] Mongoose specific option. If `bufferCommands` is true, Mongoose will throw an error after `bufferTimeoutMS` if the operation is still buffered. + * @param {string} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string. + * @param {string} [options.user] username for authentication, equivalent to `options.auth.username`. Maintained for backwards compatibility. + * @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. + * @param {number} [options.maxPoolSize=100] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {number} [options.minPoolSize=0] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds). + * @param {number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. + * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. + * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. + * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. + * @param {boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection. + * @returns {Promise} + * @api public + */ + +Connection.prototype.openUri = async function openUri(uri, options) { + if (this.readyState === STATES.connecting || this.readyState === STATES.connected) { + if (this._connectionString === uri) { + return this; + } + } + + this._closeCalled = false; + + // Internal option to skip `await this.$initialConnection` in + // this function for `createConnection()`. Because otherwise + // `createConnection()` would have an uncatchable error. + let _fireAndForget = false; + if (options && '_fireAndForget' in options) { + _fireAndForget = options._fireAndForget; + delete options._fireAndForget; + } + + try { + _validateArgs.apply(arguments); + } catch (err) { + if (_fireAndForget) { + throw err; + } + this.$initialConnection = Promise.reject(err); + throw err; + } + + this.$initialConnection = this.createClient(uri, options). + then(() => this). + catch(err => { + this.readyState = STATES.disconnected; + if (this.listeners('error').length > 0) { + immediate(() => this.emit('error', err)); + } + throw err; + }); + + for (const model of Object.values(this.models)) { + // Errors handled internally, so safe to ignore error + model.init().catch(function $modelInitNoop() {}); + } + + // `createConnection()` calls this `openUri()` function without + // awaiting on the result, so we set this option to rely on + // `asPromise()` to handle any errors. + if (_fireAndForget) { + return this; + } + + try { + await this.$initialConnection; + } catch (err) { + throw _handleConnectionErrors(err); + } + + return this; +}; + +/** + * Listen to events in the Connection + * + * @param {string} event The event to listen on + * @param {Function} callback + * @see Connection#readyState https://mongoosejs.com/docs/api/connection.html#Connection.prototype.readyState + * + * @method on + * @instance + * @memberOf Connection + * @api public + */ + +// Treat `on('error')` handlers as handling the initialConnection promise +// to avoid uncaught exceptions when using `on('error')`. See gh-14377. +Connection.prototype.on = function on(event, callback) { + if (event === 'error' && this.$initialConnection) { + this.$initialConnection.catch(() => {}); + } + return EventEmitter.prototype.on.call(this, event, callback); +}; + +/** + * Listen to a event once in the Connection + * + * @param {string} event The event to listen on + * @param {Function} callback + * @see Connection#readyState https://mongoosejs.com/docs/api/connection.html#Connection.prototype.readyState + * + * @method once + * @instance + * @memberOf Connection + * @api public + */ + +// Treat `on('error')` handlers as handling the initialConnection promise +// to avoid uncaught exceptions when using `on('error')`. See gh-14377. +Connection.prototype.once = function on(event, callback) { + if (event === 'error' && this.$initialConnection) { + this.$initialConnection.catch(() => {}); + } + return EventEmitter.prototype.once.call(this, event, callback); +}; + +/*! + * ignore + */ + +function _validateArgs(uri, options, callback) { + if (typeof options === 'function' && callback == null) { + throw new MongooseError('Connection.prototype.openUri() no longer accepts a callback'); + } else if (typeof callback === 'function') { + throw new MongooseError('Connection.prototype.openUri() no longer accepts a callback'); + } +} + +/*! + * ignore + */ + +function _handleConnectionErrors(err) { + if (err?.name === 'MongoServerSelectionError') { + const originalError = err; + err = new ServerSelectionError(); + err.assimilateError(originalError); + } + + return err; +} + +/** + * Destroy the connection. Similar to [`.close`](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.close()), + * but also removes the connection from Mongoose's `connections` list and prevents the + * connection from ever being re-opened. + * + * @param {boolean} [force] + * @returns {Promise} + */ + +Connection.prototype.destroy = async function destroy(force) { + if (typeof force === 'function' || (arguments.length === 2 && typeof arguments[1] === 'function')) { + throw new MongooseError('Connection.prototype.destroy() no longer accepts a callback'); + } + + if (force != null && typeof force === 'object') { + this.$wasForceClosed = !!force.force; + } else { + this.$wasForceClosed = !!force; + } + + return this._close(force, true); +}; + +/** + * Closes the connection + * + * @param {boolean} [force] optional + * @return {Promise} + * @api public + */ + +Connection.prototype.close = async function close(force) { + if (typeof force === 'function' || (arguments.length === 2 && typeof arguments[1] === 'function')) { + throw new MongooseError('Connection.prototype.close() no longer accepts a callback'); + } + + if (force != null && typeof force === 'object') { + this.$wasForceClosed = !!force.force; + } else { + this.$wasForceClosed = !!force; + } + + if (this._lastHeartbeatAt != null) { + this._lastHeartbeatAt = null; + } + + for (const model of Object.values(this.models)) { + // If manually disconnecting, make sure to clear each model's `$init` + // promise, so Mongoose knows to re-run `init()` in case the + // connection is re-opened. See gh-12047. + delete model.$init; + } + + return this._close(force, false); +}; + +/** + * Handles closing the connection + * + * @param {boolean} force + * @param {boolean} destroy + * @returns {Connection} this + * @api private + */ +Connection.prototype._close = async function _close(force, destroy) { + const _this = this; + const closeCalled = this._closeCalled; + this._closeCalled = true; + this._destroyCalled = destroy; + if (this.client != null) { + this.client._closeCalled = true; + this.client._destroyCalled = destroy; + } + + const conn = this; + switch (this.readyState) { + case STATES.disconnected: + if (destroy) { + const index = this.base.connections.indexOf(conn); + if (index !== -1) { + this.base.connections.splice(index, 1); + } + } + if (!closeCalled) { + await this.doClose(force); + this.onClose(force); + } + break; + + case STATES.connected: + this.readyState = STATES.disconnecting; + await this.doClose(force); + if (destroy) { + const index = _this.base.connections.indexOf(conn); + if (index !== -1) { + this.base.connections.splice(index, 1); + } + } + this.onClose(force); + + break; + case STATES.connecting: + return new Promise((resolve, reject) => { + const _rerunClose = () => { + this.removeListener('open', _rerunClose); + this.removeListener('error', _rerunClose); + if (destroy) { + this.destroy(force).then(resolve, reject); + } else { + this.close(force).then(resolve, reject); + } + }; + + this.once('open', _rerunClose); + this.once('error', _rerunClose); + }); + + case STATES.disconnecting: + return new Promise(resolve => { + this.once('close', () => { + if (destroy && this.base.connections.indexOf(conn) !== -1) { + this.base.connections.splice(this.base.connections.indexOf(conn), 1); + } + resolve(); + }); + }); + } + + return this; +}; + +/** + * Abstract method that drivers must implement. + * + * @api private + */ + +Connection.prototype.doClose = function doClose() { + throw new MongooseError('Connection#doClose unimplemented by driver'); +}; + +/** + * Called when the connection closes + * + * @emits "close" + * @api private + */ + +Connection.prototype.onClose = function onClose(force) { + this.readyState = STATES.disconnected; + + // avoid having the collection subscribe to our event emitter + // to prevent 0.3 warning + for (const i in this.collections) { + if (Object.hasOwn(this.collections, i)) { + this.collections[i].onClose(force); + } + } + + this.emit('close', force); + + const wasForceClosed = typeof force === 'object' && force !== null ? force.force : force; + + for (const db of this.otherDbs) { + this._destroyCalled ? db.destroy({ force: wasForceClosed, skipCloseClient: true }) : db.close({ force: wasForceClosed, skipCloseClient: true }); + } +}; + +/** + * Retrieves a raw collection instance, creating it if not cached. + * This method returns a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)). + * Using a Collection bypasses Mongoose middleware, validation, and casting, + * letting you use [MongoDB Node.js driver](https://mongodb.github.io/node-mongodb-native/) functionality directly. + * + * @param {string} name of the collection + * @param {object} [options] optional collection options + * @return {Collection} collection instance + * @api public + */ + +Connection.prototype.collection = function(name, options) { + const defaultOptions = { + autoIndex: this.config.autoIndex ?? this.base.options.autoIndex, + autoCreate: this.config.autoCreate ?? this.base.options.autoCreate, + autoSearchIndex: this.config.autoSearchIndex ?? this.base.options.autoSearchIndex + }; + options = Object.assign({}, defaultOptions, options ? clone(options) : {}); + options.$wasForceClosed = this.$wasForceClosed; + const Collection = this.base?.__driver?.Collection || driver.get().Collection; + if (!(name in this.collections)) { + this.collections[name] = new Collection(name, this, options); + } + return this.collections[name]; +}; + +/** + * Declares a plugin executed on all schemas you pass to `conn.model()` + * + * Equivalent to calling `.plugin(fn)` on each schema you create. + * + * #### Example: + * + * const db = mongoose.createConnection('mongodb://127.0.0.1:27017/mydb'); + * db.plugin(() => console.log('Applied')); + * db.plugins.length; // 1 + * + * db.model('Test', new Schema({})); // Prints "Applied" + * + * @param {Function} fn plugin callback + * @param {object} [opts] optional options + * @return {Connection} this + * @see plugins https://mongoosejs.com/docs/plugins.html + * @api public + */ + +Connection.prototype.plugin = function(fn, opts) { + this.plugins.push([fn, opts]); + return this; +}; + +/** + * Defines or retrieves a model. + * + * const mongoose = require('mongoose'); + * const db = mongoose.createConnection(..); + * db.model('Venue', new Schema(..)); + * const Ticket = db.model('Ticket', new Schema(..)); + * const Venue = db.model('Venue'); + * + * _When no `collection` argument is passed, Mongoose produces a collection name by passing the model `name` to the `utils.toCollectionName` method. This method pluralizes the name. If you don't like this behavior, either pass a collection name or set your schemas collection name option._ + * + * #### Example: + * + * const schema = new Schema({ name: String }, { collection: 'actor' }); + * + * // or + * + * schema.set('collection', 'actor'); + * + * // or + * + * const collectionName = 'actor' + * const M = conn.model('Actor', schema, collectionName) + * + * @param {string|Function} name the model name or class extending Model + * @param {Schema} [schema] a schema. necessary when defining a model + * @param {string} [collection] name of mongodb collection (optional) if not given it will be induced from model name + * @param {object} [options] + * @param {boolean} [options.overwriteModels=false] If true, overwrite existing models with the same name to avoid `OverwriteModelError` + * @see Mongoose#model https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.model() + * @return {Model} The compiled model + * @api public + */ + +Connection.prototype.model = function model(name, schema, collection, options) { + if (!(this instanceof Connection)) { + throw new MongooseError('`connection.model()` should not be run with ' + + '`new`. If you are doing `new db.model(foo)(bar)`, use ' + + '`db.model(foo)(bar)` instead'); + } + + let fn; + if (typeof name === 'function') { + fn = name; + name = fn.name; + } + + // collection name discovery + if (typeof schema === 'string') { + collection = schema; + schema = false; + } + + if (utils.isObject(schema)) { + if (!schema.instanceOfSchema) { + schema = new Schema(schema); + } else if (!(schema instanceof this.base.Schema)) { + schema = schema._clone(this.base.Schema); + } + } + if (schema && !schema.instanceOfSchema) { + throw new MongooseError('The 2nd parameter to `mongoose.model()` should be a ' + + 'schema or a POJO'); + } + + const defaultOptions = { cache: false, overwriteModels: this.base.options.overwriteModels }; + const opts = Object.assign(defaultOptions, options, { connection: this }); + if (this.models[name] && !collection && opts.overwriteModels !== true) { + // model exists but we are not subclassing with custom collection + if (schema?.instanceOfSchema && schema !== this.models[name].schema) { + throw new MongooseError.OverwriteModelError(name); + } + return this.models[name]; + } + + let model; + + if (schema?.instanceOfSchema) { + applyPlugins(schema, this.plugins, null, '$connectionPluginsApplied'); + + // compile a model + model = this.base._model(fn || name, schema, collection, opts); + + // only the first model with this name is cached to allow + // for one-offs with custom collection names etc. + if (!this.models[name]) { + this.models[name] = model; + } + + // Errors handled internally, so safe to ignore error + model.init().catch(function $modelInitNoop() {}); + + return model; + } + + if (this.models[name] && collection) { + // subclassing current model with alternate collection + model = this.models[name]; + schema = model.prototype.schema; + const sub = model.__subclass(this, schema, collection); + // do not cache the sub model + return sub; + } + + if (arguments.length === 1) { + model = this.models[name]; + if (!model) { + throw new MongooseError.MissingSchemaError(name); + } + return model; + } + + if (!model) { + throw new MongooseError.MissingSchemaError(name); + } + + if (this === model.prototype.db + && (!collection || collection === model.collection.name)) { + // model already uses this connection. + + // only the first model with this name is cached to allow + // for one-offs with custom collection names etc. + if (!this.models[name]) { + this.models[name] = model; + } + + return model; + } + this.models[name] = model.__subclass(this, schema, collection); + return this.models[name]; +}; + +/** + * Removes the model named `name` from this connection, if it exists. You can + * use this function to clean up any models you created in your tests to + * prevent OverwriteModelErrors. + * + * #### Example: + * + * conn.model('User', new Schema({ name: String })); + * console.log(conn.model('User')); // Model object + * conn.deleteModel('User'); + * console.log(conn.model('User')); // undefined + * + * // Usually useful in a Mocha `afterEach()` hook + * afterEach(function() { + * conn.deleteModel(/.+/); // Delete every model + * }); + * + * @api public + * @param {string|RegExp} name if string, the name of the model to remove. If regexp, removes all models whose name matches the regexp. + * @return {Connection} this + */ + +Connection.prototype.deleteModel = function deleteModel(name) { + if (typeof name === 'string') { + const model = this.model(name); + if (model == null) { + return this; + } + const collectionName = model.collection.name; + delete this.models[name]; + delete this.collections[collectionName]; + + this.emit('deleteModel', model); + } else if (name instanceof RegExp) { + const pattern = name; + const names = this.modelNames(); + for (const name of names) { + if (pattern.test(name)) { + this.deleteModel(name); + } + } + } else { + throw new MongooseError('First parameter to `deleteModel()` must be a string ' + + 'or regexp, got "' + name + '"'); + } + + return this; +}; + +/** + * Watches the entire underlying database for changes. Similar to + * [`Model.watch()`](https://mongoosejs.com/docs/api/model.html#Model.watch()). + * + * This function does **not** trigger any middleware. In particular, it + * does **not** trigger aggregate middleware. + * + * The ChangeStream object is an event emitter that emits the following events: + * + * - 'change': A change occurred, see below example + * - 'error': An unrecoverable error occurred. In particular, change streams currently error out if they lose connection to the replica set primary. Follow [this GitHub issue](https://github.com/Automattic/mongoose/issues/6799) for updates. + * - 'end': Emitted if the underlying stream is closed + * - 'close': Emitted if the underlying stream is closed + * + * #### Example: + * + * const User = conn.model('User', new Schema({ name: String })); + * + * const changeStream = conn.watch().on('change', data => console.log(data)); + * + * // Triggers a 'change' event on the change stream. + * await User.create({ name: 'test' }); + * + * @api public + * @param {Array} [pipeline] + * @param {object} [options] passed without changes to [the MongoDB driver's `Db#watch()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#watch) + * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter + */ + +Connection.prototype.watch = function watch(pipeline, options) { + const changeStreamPromise = this._waitForConnect().then(() => this.db.watch(pipeline, options)); + + return new ChangeStream(changeStreamPromise, pipeline, options); +}; + +/** + * Returns a promise that resolves when this connection + * successfully connects to MongoDB, or rejects if this connection failed + * to connect. + * + * #### Example: + * + * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/test'). + * asPromise(); + * conn.readyState; // 1, means Mongoose is connected + * + * @api public + * @return {Promise} + */ + +Connection.prototype.asPromise = async function asPromise() { + try { + await this.$initialConnection; + return this; + } catch (err) { + throw _handleConnectionErrors(err); + } +}; + +/** + * Returns an array of model names created on this connection. + * @api public + * @return {Array} + */ + +Connection.prototype.modelNames = function modelNames() { + return Object.keys(this.models); +}; + +/** + * Returns if the connection requires authentication after it is opened. Generally if a + * username and password are both provided than authentication is needed, but in some cases a + * password is not required. + * + * @api private + * @return {boolean} true if the connection should be authenticated after it is opened, otherwise false. + */ +Connection.prototype.shouldAuthenticate = function shouldAuthenticate() { + return this.user != null && + (this.pass != null || this.authMechanismDoesNotRequirePassword()); +}; + +/** + * Returns a boolean value that specifies if the current authentication mechanism needs a + * password to authenticate according to the auth objects passed into the openUri methods. + * + * @api private + * @return {boolean} true if the authentication mechanism specified in the options object requires + * a password, otherwise false. + */ +Connection.prototype.authMechanismDoesNotRequirePassword = function authMechanismDoesNotRequirePassword() { + if (this.options?.auth) { + return noPasswordAuthMechanisms.indexOf(this.options.auth.authMechanism) >= 0; + } + return true; +}; + +/** + * Returns a boolean value that specifies if the provided objects object provides enough + * data to authenticate with. Generally this is true if the username and password are both specified + * but in some authentication methods, a password is not required for authentication so only a username + * is required. + * + * @param {object} [options] the options object passed into the openUri methods. + * @api private + * @return {boolean} true if the provided options object provides enough data to authenticate with, + * otherwise false. + */ +Connection.prototype.optionsProvideAuthenticationData = function optionsProvideAuthenticationData(options) { + return (options) && + (options.user) && + ((options.pass) || this.authMechanismDoesNotRequirePassword()); +}; + +/** + * Returns the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance + * that this connection uses to talk to MongoDB. + * + * #### Example: + * + * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/test'). + * asPromise(); + * + * conn.getClient(); // MongoClient { ... } + * + * @api public + * @return {MongoClient} + */ + +Connection.prototype.getClient = function getClient() { + return this.client; +}; + +/** + * Set the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance + * that this connection uses to talk to MongoDB. This is useful if you already have a MongoClient instance, and want to + * reuse it. + * + * #### Example: + * + * const client = await mongodb.MongoClient.connect('mongodb://127.0.0.1:27017/test'); + * + * const conn = mongoose.createConnection().setClient(client); + * + * conn.getClient(); // MongoClient { ... } + * conn.readyState; // 1, means 'CONNECTED' + * + * @api public + * @param {MongClient} client The Client to set to be used. + * @return {Connection} this + */ + +Connection.prototype.setClient = function setClient() { + throw new MongooseError('Connection#setClient not implemented by driver'); +}; + +/*! + * Called internally by `openUri()` to create a MongoClient instance. + */ + +Connection.prototype.createClient = function createClient() { + throw new MongooseError('Connection#createClient not implemented by driver'); +}; + +/** + * Syncs all the indexes for the models registered with this connection. + * + * @param {object} [options] + * @param {boolean} [options.continueOnError] `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model. + * @return {Promise} Returns a Promise, when the Promise resolves the value is a list of the dropped indexes. + */ +Connection.prototype.syncIndexes = async function syncIndexes(options = {}) { + const result = {}; + const errorsMap = { }; + + const { continueOnError } = options; + delete options.continueOnError; + + for (const model of Object.values(this.models)) { + try { + result[model.modelName] = await model.syncIndexes(options); + } catch (err) { + if (!continueOnError) { + errorsMap[model.modelName] = err; + break; + } else { + result[model.modelName] = err; + } + } + } + + if (!continueOnError && utils.hasOwnKeys(errorsMap)) { + const message = Object.entries(errorsMap).map(([modelName, err]) => `${modelName}: ${err.message}`).join(', '); + const syncIndexesError = new SyncIndexesError(message, errorsMap); + throw syncIndexesError; + } + + return result; +}; + +/** + * Switches to a different database using the same [connection pool](https://mongoosejs.com/docs/connections.html#connection_pools). + * + * Returns a new connection object, with the new db. + * + * #### Example: + * + * // Connect to `initialdb` first + * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/initialdb').asPromise(); + * + * // Creates an un-cached connection to `mydb` + * const db = conn.useDb('mydb'); + * // Creates a cached connection to `mydb2`. All calls to `conn.useDb('mydb2', { useCache: true })` will return the same + * // connection instance as opposed to creating a new connection instance + * const db2 = conn.useDb('mydb2', { useCache: true }); + * + * @method useDb + * @memberOf Connection + * @param {string} name The database name + * @param {object} [options] + * @param {boolean} [options.useCache=false] If true, cache results so calling `useDb()` multiple times with the same name only creates 1 connection object. + * @return {Connection} New Connection Object + * @api public + */ + +/** + * Runs a [db-level aggregate()](https://www.mongodb.com/docs/manual/reference/method/db.aggregate/) on this connection's underlying `db` + * + * @method aggregate + * @memberOf Connection + * @param {Array} pipeline + * @param {object} [options] + * @param {boolean} [options.cursor=false] If true, make the Aggregate resolve to a Mongoose AggregationCursor rather than an array + * @return {Aggregate} Aggregation wrapper + * @api public + */ + +/** + * Removes the database connection with the given name created with with `useDb()`. + * + * Throws an error if the database connection was not found. + * + * #### Example: + * + * // Connect to `initialdb` first + * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/initialdb').asPromise(); + * + * // Creates an un-cached connection to `mydb` + * const db = conn.useDb('mydb'); + * + * // Closes `db`, and removes `db` from `conn.relatedDbs` and `conn.otherDbs` + * await conn.removeDb('mydb'); + * + * @method removeDb + * @memberOf Connection + * @param {string} name The database name + * @return {Connection} this + * @api public + */ + +/*! + * Module exports. + */ + +Connection.STATES = STATES; +module.exports = Connection; diff --git a/backend/node_modules/mongoose/lib/connectionState.js b/backend/node_modules/mongoose/lib/connectionState.js new file mode 100644 index 0000000..920f45b --- /dev/null +++ b/backend/node_modules/mongoose/lib/connectionState.js @@ -0,0 +1,26 @@ + +/*! + * Connection states + */ + +'use strict'; + +const STATES = module.exports = exports = Object.create(null); + +const disconnected = 'disconnected'; +const connected = 'connected'; +const connecting = 'connecting'; +const disconnecting = 'disconnecting'; +const uninitialized = 'uninitialized'; + +STATES[0] = disconnected; +STATES[1] = connected; +STATES[2] = connecting; +STATES[3] = disconnecting; +STATES[99] = uninitialized; + +STATES[disconnected] = 0; +STATES[connected] = 1; +STATES[connecting] = 2; +STATES[disconnecting] = 3; +STATES[uninitialized] = 99; diff --git a/backend/node_modules/mongoose/lib/constants.js b/backend/node_modules/mongoose/lib/constants.js new file mode 100644 index 0000000..3a03bd5 --- /dev/null +++ b/backend/node_modules/mongoose/lib/constants.js @@ -0,0 +1,73 @@ +'use strict'; + +/*! + * ignore + */ + +const queryOperations = Object.freeze([ + // Read + 'countDocuments', + 'distinct', + 'estimatedDocumentCount', + 'find', + 'findOne', + // Update + 'findOneAndReplace', + 'findOneAndUpdate', + 'replaceOne', + 'updateMany', + 'updateOne', + // Delete + 'deleteMany', + 'deleteOne', + 'findOneAndDelete' +]); + +exports.queryOperations = queryOperations; + +/*! + * ignore + */ + +const queryMiddlewareFunctions = queryOperations.concat([ + 'validate' +]); + +exports.queryMiddlewareFunctions = queryMiddlewareFunctions; + +/*! + * ignore + */ + +const aggregateMiddlewareFunctions = [ + 'aggregate' +]; + +exports.aggregateMiddlewareFunctions = aggregateMiddlewareFunctions; + +/*! + * ignore + */ + +const modelMiddlewareFunctions = [ + 'bulkWrite', + 'createCollection', + 'insertMany' +]; + +exports.modelMiddlewareFunctions = modelMiddlewareFunctions; + +/*! + * ignore + */ + +const documentMiddlewareFunctions = [ + 'validate', + 'save', + 'remove', + 'updateOne', + 'deleteOne', + 'init' +]; + +exports.documentMiddlewareFunctions = documentMiddlewareFunctions; diff --git a/backend/node_modules/mongoose/lib/cursor/aggregationCursor.js b/backend/node_modules/mongoose/lib/cursor/aggregationCursor.js new file mode 100644 index 0000000..4459874 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cursor/aggregationCursor.js @@ -0,0 +1,466 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('../error/mongooseError'); +const Readable = require('stream').Readable; +const eachAsync = require('../helpers/cursor/eachAsync'); +const immediate = require('../helpers/immediate'); +const kareem = require('kareem'); +const util = require('util'); + +/** + * An AggregationCursor is a concurrency primitive for processing aggregation + * results one document at a time. It is analogous to QueryCursor. + * + * An AggregationCursor fulfills the Node.js streams3 API, + * in addition to several other mechanisms for loading documents from MongoDB + * one at a time. + * + * Creating an AggregationCursor executes the model's pre aggregate hooks, + * but **not** the model's post aggregate hooks. + * + * Unless you're an advanced user, do **not** instantiate this class directly. + * Use [`Aggregate#cursor()`](https://mongoosejs.com/docs/api/aggregate.html#Aggregate.prototype.cursor()) instead. + * + * @param {Aggregate} agg + * @inherits Readable https://nodejs.org/api/stream.html#class-streamreadable + * @event `cursor`: Emitted when the cursor is created + * @event `error`: Emitted when an error occurred + * @event `data`: Emitted when the stream is flowing and the next doc is ready + * @event `end`: Emitted when the stream is exhausted + * @api public + */ + +function AggregationCursor(agg) { + // set autoDestroy=true because on node 12 it's by default false + // gh-10902 need autoDestroy to destroy correctly and emit 'close' event + Readable.call(this, { autoDestroy: true, objectMode: true }); + + this.cursor = null; + this.agg = agg; + this._transforms = []; + const connection = agg._connection; + const model = agg._model; + delete agg.options.cursor.useMongooseAggCursor; + this._mongooseOptions = {}; + + if (connection) { + this.cursor = connection.db.aggregate(agg._pipeline, agg.options || {}); + setImmediate(() => this.emit('cursor', this.cursor)); + } else { + _init(model, this, agg); + } +} + +util.inherits(AggregationCursor, Readable); + +/*! + * ignore + */ + +function _init(model, c, agg) { + if (!model.collection.buffer) { + model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err)); + } else { + model.collection.emitter.once('queue', function() { + model.hooks.execPre('aggregate', agg).then(() => onPreComplete(null), err => onPreComplete(err)); + }); + } + + function onPreComplete(err) { + if (err != null) { + _handlePreHookError(c, err); + return; + } + if (typeof agg.options?.cursor?.transform === 'function') { + c._transforms.push(agg.options.cursor.transform); + } + + c.cursor = model.collection.aggregate(agg._pipeline, agg.options || {}); + c.emit('cursor', c.cursor); + } +} + +/** +* Handles error emitted from pre middleware. In particular, checks for `skipWrappedFunction`, which allows skipping +* the actual aggregation and overwriting the function's return value. Because aggregation cursors don't return a value, +* we need to make sure the user doesn't accidentally set a value in skipWrappedFunction. +* +* @param {QueryCursor} queryCursor +* @param {Error} err +* @returns +*/ + +function _handlePreHookError(queryCursor, err) { + if (err instanceof kareem.skipWrappedFunction) { + const resultValue = err.args[0]; + if (resultValue != null && (!Array.isArray(resultValue) || resultValue.length)) { + const err = new MongooseError( + 'Cannot `skipMiddlewareFunction()` with a value when using ' + + '`.aggregate().cursor()`, value must be nullish or empty array, got "' + + util.inspect(resultValue) + + '".' + ); + queryCursor._markError(err); + queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err); + return; + } + queryCursor.emit('cursor', null); + return; + } + queryCursor._markError(err); + queryCursor.listeners('error').length > 0 && queryCursor.emit('error', err); +} + + +/** + * Necessary to satisfy the Readable API + * @method _read + * @memberOf AggregationCursor + * @instance + * @api private + */ + +AggregationCursor.prototype._read = function() { + const _this = this; + _next(this, function(error, doc) { + if (error) { + return _this.emit('error', error); + } + if (!doc) { + _this.push(null); + _this.cursor.close(function(error) { + if (error) { + return _this.emit('error', error); + } + }); + return; + } + _this.push(doc); + }); +}; + +if (Symbol.asyncIterator != null) { + const msg = 'Mongoose does not support using async iterators with an ' + + 'existing aggregation cursor. See https://bit.ly/mongoose-async-iterate-aggregation'; + + AggregationCursor.prototype[Symbol.asyncIterator] = function() { + throw new MongooseError(msg); + }; +} + +/** + * Registers a transform function which subsequently maps documents retrieved + * via the streams interface or `.next()` + * + * #### Example: + * + * // Map documents returned by `data` events + * Thing. + * find({ name: /^hello/ }). + * cursor(). + * map(function (doc) { + * doc.foo = "bar"; + * return doc; + * }) + * on('data', function(doc) { console.log(doc.foo); }); + * + * // Or map documents returned by `.next()` + * const cursor = Thing.find({ name: /^hello/ }). + * cursor(). + * map(function (doc) { + * doc.foo = "bar"; + * return doc; + * }); + * cursor.next(function(error, doc) { + * console.log(doc.foo); + * }); + * + * @param {Function} fn + * @return {AggregationCursor} + * @memberOf AggregationCursor + * @api public + * @method map + */ + +Object.defineProperty(AggregationCursor.prototype, 'map', { + value: function(fn) { + this._transforms.push(fn); + return this; + }, + enumerable: true, + configurable: true, + writable: true +}); + +/** + * Marks this cursor as errored + * @method _markError + * @instance + * @memberOf AggregationCursor + * @api private + */ + +AggregationCursor.prototype._markError = function(error) { + this._error = error; + return this; +}; + +/** + * Marks this cursor as closed. Will stop streaming and subsequent calls to + * `next()` will error. + * + * @return {Promise} + * @api public + * @method close + * @emits "close" + * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close + */ + +AggregationCursor.prototype.close = async function close() { + if (typeof arguments[0] === 'function') { + throw new MongooseError('AggregationCursor.prototype.close() no longer accepts a callback'); + } + try { + await this.cursor.close(); + } catch (error) { + this.listeners('error').length > 0 && this.emit('error', error); + throw error; + } + this.emit('close'); +}; + +/** + * Marks this cursor as destroyed. Will stop streaming and subsequent calls to + * `next()` will error. + * + * @return {this} + * @api private + * @method _destroy + */ + +AggregationCursor.prototype._destroy = function _destroy(_err, callback) { + let waitForCursor = null; + if (!this.cursor) { + waitForCursor = new Promise((resolve) => { + this.once('cursor', resolve); + }); + } else { + waitForCursor = Promise.resolve(); + } + + waitForCursor + .then(() => this.cursor.close()) + .then(() => { + this._closed = true; + callback(); + }) + .catch(error => { + callback(error); + }); + return this; +}; + +/** + * Get the next document from this cursor. Will return `null` when there are + * no documents left. + * + * @return {Promise} + * @api public + * @method next + */ + +AggregationCursor.prototype.next = async function next() { + if (typeof arguments[0] === 'function') { + throw new MongooseError('AggregationCursor.prototype.next() no longer accepts a callback'); + } + return new Promise((resolve, reject) => { + _next(this, (err, res) => { + if (err != null) { + return reject(err); + } + resolve(res); + }); + }); +}; + +/** + * Execute `fn` for every document in the cursor. If `fn` returns a promise, + * will wait for the promise to resolve before iterating on to the next one. + * Returns a promise that resolves when done. + * + * @param {Function} fn + * @param {object} [options] + * @param {number} [options.parallel] the number of promises to execute in parallel. Defaults to 1. + * @param {number} [options.batchSize=null] if set, Mongoose will call `fn` with an array of at most `batchSize` documents, instead of a single document + * @param {boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error. + * @return {Promise} + * @api public + * @method eachAsync + */ + +AggregationCursor.prototype.eachAsync = function(fn, opts) { + if (typeof arguments[2] === 'function') { + throw new MongooseError('AggregationCursor.prototype.eachAsync() no longer accepts a callback'); + } + const _this = this; + if (typeof opts === 'function') { + opts = {}; + } + opts = opts || {}; + + return eachAsync(function(cb) { return _next(_this, cb); }, fn, opts); +}; + +/** + * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js) + * You do not need to call this function explicitly, the JavaScript runtime + * will call it for you. + * + * #### Example: + * + * // Async iterator without explicitly calling `cursor()`. Mongoose still + * // creates an AggregationCursor instance internally. + * const agg = Model.aggregate([{ $match: { age: { $gte: 25 } } }]); + * for await (const doc of agg) { + * console.log(doc.name); + * } + * + * // You can also use an AggregationCursor instance for async iteration + * const cursor = Model.aggregate([{ $match: { age: { $gte: 25 } } }]).cursor(); + * for await (const doc of cursor) { + * console.log(doc.name); + * } + * + * Node.js 10.x supports async iterators natively without any flags. You can + * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187). + * + * **Note:** This function is not set if `Symbol.asyncIterator` is undefined. If + * `Symbol.asyncIterator` is undefined, that means your Node.js version does not + * support async iterators. + * + * @method [Symbol.asyncIterator] + * @memberOf AggregationCursor + * @instance + * @api public + */ + +if (Symbol.asyncIterator != null) { + AggregationCursor.prototype[Symbol.asyncIterator] = function() { + return this.transformNull()._transformForAsyncIterator(); + }; +} + +/*! + * ignore + */ + +AggregationCursor.prototype._transformForAsyncIterator = function() { + if (this._transforms.indexOf(_transformForAsyncIterator) === -1) { + this.map(_transformForAsyncIterator); + } + return this; +}; + +/*! + * ignore + */ + +AggregationCursor.prototype.transformNull = function(val) { + if (arguments.length === 0) { + val = true; + } + this._mongooseOptions.transformNull = val; + return this; +}; + +/*! + * ignore + */ + +function _transformForAsyncIterator(doc) { + return doc == null ? { done: true } : { value: doc, done: false }; +} + +/** + * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#addCursorFlag). + * Useful for setting the `noCursorTimeout` and `tailable` flags. + * + * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag + * @param {boolean} value + * @return {AggregationCursor} this + * @api public + * @method addCursorFlag + */ + +AggregationCursor.prototype.addCursorFlag = function(flag, value) { + const _this = this; + _waitForCursor(this, function() { + _this.cursor.addCursorFlag(flag, value); + }); + return this; +}; + +/*! + * ignore + */ + +function _waitForCursor(ctx, cb) { + if (ctx.cursor) { + return cb(); + } + ctx.once('cursor', function() { + cb(); + }); +} + +/** + * Get the next doc from the underlying cursor and mongooseify it + * (populate, etc.) + * @param {any} ctx + * @param {Function} cb + * @api private + */ + +function _next(ctx, cb) { + let callback = cb; + if (ctx._transforms.length) { + callback = function(err, doc) { + if (err || (doc === null && !ctx._mongooseOptions.transformNull)) { + return cb(err, doc); + } + cb(err, ctx._transforms.reduce(function(doc, fn) { + return fn(doc); + }, doc)); + }; + } + + if (ctx._error) { + return immediate(function() { + callback(ctx._error); + }); + } + + if (ctx.cursor) { + return ctx.cursor.next().then( + doc => { + if (!doc) { + return callback(null, null); + } + + callback(null, doc); + }, + err => callback(err) + ); + } else { + ctx.once('error', cb); + ctx.once('cursor', function() { + _next(ctx, cb); + }); + } +} + +module.exports = AggregationCursor; diff --git a/backend/node_modules/mongoose/lib/cursor/changeStream.js b/backend/node_modules/mongoose/lib/cursor/changeStream.js new file mode 100644 index 0000000..af0514d --- /dev/null +++ b/backend/node_modules/mongoose/lib/cursor/changeStream.js @@ -0,0 +1,218 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const EventEmitter = require('events').EventEmitter; +const MongooseError = require('../error/mongooseError'); + +/*! + * ignore + */ + +const driverChangeStreamEvents = ['close', 'change', 'end', 'error', 'resumeTokenChanged']; + +/*! + * ignore + */ + +class ChangeStream extends EventEmitter { + constructor(changeStreamPromise, pipeline, options) { + super(); + + this.driverChangeStream = null; + this.closed = false; + this.bindedEvents = false; + this.pipeline = pipeline; + this.options = options; + this.errored = false; + + if (options?.hydrate && !options.model) { + throw new Error( + 'Cannot create change stream with `hydrate: true` ' + + 'unless calling `Model.watch()`' + ); + } + + this.$driverChangeStreamPromise = changeStreamPromise.then( + driverChangeStream => { + this.driverChangeStream = driverChangeStream; + // Use setImmediate so the stream pump (_read) has a chance to run and + // the driver cursor initializes before 'ready' resolves. Without this, + // changes emitted immediately after 'ready' can be missed because the + // underlying cursor hasn't sent its initial aggregate to MongoDB yet. + setImmediate(() => this.emit('ready')); + return this; + }, + err => { + this.errored = true; + this.emit('error', err); + throw err; + } + ); + } + + _bindEvents() { + if (this.bindedEvents) { + return; + } + + this.bindedEvents = true; + + if (this.driverChangeStream == null) { + this.$driverChangeStreamPromise.then( + () => { + this.driverChangeStream.on('close', () => { + this.closed = true; + }); + + driverChangeStreamEvents.forEach(ev => { + this.driverChangeStream.on(ev, data => { + if (data?.fullDocument != null && this.options?.hydrate) { + data.fullDocument = this.options.model.hydrate(data.fullDocument); + } + this.emit(ev, data); + }); + }); + }, + () => {} // No need to register events if opening change stream failed + ); + + return; + } + + this.driverChangeStream.on('close', () => { + this.closed = true; + }); + + driverChangeStreamEvents.forEach(ev => { + this.driverChangeStream.on(ev, data => { + if (data?.fullDocument != null && this.options?.hydrate) { + data.fullDocument = this.options.model.hydrate(data.fullDocument); + } + this.emit(ev, data); + }); + }); + } + + hasNext(cb) { + if (this.errored) { + throw new MongooseError('Cannot call hasNext() on errored ChangeStream'); + } + + if (this.driverChangeStream != null) { + return this.driverChangeStream.hasNext(cb); + } + + return this.$driverChangeStreamPromise.then( + () => this.driverChangeStream.hasNext(cb), + err => { + if (cb != null) { + return cb(err); + } + throw err; + } + ); + } + + next(cb) { + if (this.errored) { + throw new MongooseError('Cannot call next() on errored ChangeStream'); + } + if (this.options?.hydrate) { + if (cb != null) { + const originalCb = cb; + cb = (err, data) => { + if (err != null) { + return originalCb(err); + } + if (data.fullDocument != null) { + data.fullDocument = this.options.model.hydrate(data.fullDocument); + } + return originalCb(null, data); + }; + } + + let maybePromise; + if (this.driverChangeStream != null) { + maybePromise = this.driverChangeStream.next(cb); + } else { + maybePromise = this.$driverChangeStreamPromise.then( + () => this.driverChangeStream.next(cb), + err => { + if (cb != null) { + return cb(err); + } + throw err; + } + ); + } + if (typeof maybePromise?.then === 'function') { + maybePromise = maybePromise.then(data => { + if (data.fullDocument != null) { + data.fullDocument = this.options.model.hydrate(data.fullDocument); + } + return data; + }); + } + return maybePromise; + } + + if (this.driverChangeStream != null) { + return this.driverChangeStream.next(cb); + } + + return this.$driverChangeStreamPromise.then( + () => this.driverChangeStream.next(cb), + err => { + if (cb != null) { + return cb(err); + } + throw err; + } + ); + } + + addListener(event, handler) { + if (this.errored) { + throw new MongooseError('Cannot call addListener() on errored ChangeStream'); + } + this._bindEvents(); + return super.addListener(event, handler); + } + + on(event, handler) { + if (this.errored) { + throw new MongooseError('Cannot call on() on errored ChangeStream'); + } + this._bindEvents(); + return super.on(event, handler); + } + + once(event, handler) { + if (this.errored) { + throw new MongooseError('Cannot call once() on errored ChangeStream'); + } + this._bindEvents(); + return super.once(event, handler); + } + + close() { + this.closed = true; + if (this.driverChangeStream) { + return this.driverChangeStream.close(); + } else { + return this.$driverChangeStreamPromise.then( + () => this.driverChangeStream.close(), + () => {} // No need to close if opening the change stream failed + ); + } + } +} + +/*! + * ignore + */ + +module.exports = ChangeStream; diff --git a/backend/node_modules/mongoose/lib/cursor/queryCursor.js b/backend/node_modules/mongoose/lib/cursor/queryCursor.js new file mode 100644 index 0000000..5949f86 --- /dev/null +++ b/backend/node_modules/mongoose/lib/cursor/queryCursor.js @@ -0,0 +1,625 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('../error/mongooseError'); +const Readable = require('stream').Readable; +const eachAsync = require('../helpers/cursor/eachAsync'); +const helpers = require('../queryHelpers'); +const kareem = require('kareem'); +const immediate = require('../helpers/immediate'); +const { once } = require('events'); +const util = require('util'); + +/** + * A QueryCursor is a concurrency primitive for processing query results + * one document at a time. A QueryCursor fulfills the Node.js streams3 API, + * in addition to several other mechanisms for loading documents from MongoDB + * one at a time. + * + * QueryCursors execute the model's pre `find` hooks before loading any documents + * from MongoDB, and the model's post `find` hooks after loading each document. + * + * Unless you're an advanced user, do **not** instantiate this class directly. + * Use [`Query#cursor()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.cursor()) instead. + * + * @param {Query} query + * @param {object} options query options passed to `.find()` + * @inherits Readable https://nodejs.org/api/stream.html#class-streamreadable + * @event `cursor`: Emitted when the cursor is created + * @event `error`: Emitted when an error occurred + * @event `data`: Emitted when the stream is flowing and the next doc is ready + * @event `end`: Emitted when the stream is exhausted + * @api public + */ + +function QueryCursor(query) { + // set autoDestroy=true because on node 12 it's by default false + // gh-10902 need autoDestroy to destroy correctly and emit 'close' event + Readable.call(this, { autoDestroy: true, objectMode: true }); + + this.cursor = null; + this.skipped = false; + this.query = query; + this._closed = false; + const model = query.model; + this._mongooseOptions = {}; + this._transforms = []; + this.model = model; + this.options = {}; + + const onPreComplete = (err) => { + if (err != null) { + if (err instanceof kareem.skipWrappedFunction) { + const resultValue = err.args[0]; + if (resultValue != null && (!Array.isArray(resultValue) || resultValue.length)) { + const err = new MongooseError( + 'Cannot `skipMiddlewareFunction()` with a value when using ' + + '`.find().cursor()`, value must be nullish or empty array, got "' + + util.inspect(resultValue) + + '".' + ); + this._markError(err); + this.listeners('error').length > 0 && this.emit('error', err); + return; + } + this.skipped = true; + this.emit('cursor', null); + return; + } + this._markError(err); + this.listeners('error').length > 0 && this.emit('error', err); + return; + } + Object.assign(this.options, query._optionsForExec()); + this._transforms = this._transforms.concat(query._transforms.slice()); + if (this.options.transform) { + this._transforms.push(this.options.transform); + } + // Re: gh-8039, you need to set the `cursor.batchSize` option, top-level + // `batchSize` option doesn't work. + if (this.options.batchSize) { + // Max out the number of documents we'll populate in parallel at 5000. + this.options._populateBatchSize = Math.min(this.options.batchSize, 5000); + } + if (query._mongooseOptions._asyncIterator) { + this._mongooseOptions._asyncIterator = true; + } + + if (model.collection._shouldBufferCommands() && model.collection.buffer) { + model.collection.queue.push([ + () => _getRawCursor(query, this) + ]); + } else { + _getRawCursor(query, this); + } + }; + + model.hooks.execPre('find', query).then(() => onPreComplete(null), err => onPreComplete(err)); +} + +util.inherits(QueryCursor, Readable); + +/*! + * ignore + */ + +function _getRawCursor(query, queryCursor) { + try { + const cursor = query.model.collection.find(query._conditions, queryCursor.options); + queryCursor.cursor = cursor; + queryCursor.emit('cursor', cursor); + } catch (err) { + queryCursor._markError(err); + queryCursor.listeners('error').length > 0 && queryCursor.emit('error', queryCursor._error); + } +} + +/** + * Necessary to satisfy the Readable API + * @method _read + * @memberOf QueryCursor + * @instance + * @api private + */ + +QueryCursor.prototype._read = function() { + _next(this, (error, doc) => { + if (error) { + return this.emit('error', error); + } + if (!doc) { + this.push(null); + this.cursor.close((error) => { + if (error) { + return this.emit('error', error); + } + }); + return; + } + this.push(doc); + }); +}; + +/** + * Returns the underlying cursor from the MongoDB Node driver that this cursor uses. + * + * @method getDriverCursor + * @memberOf QueryCursor + * @returns {Cursor} MongoDB Node driver cursor instance + * @instance + * @api public + */ + +QueryCursor.prototype.getDriverCursor = async function getDriverCursor() { + if (this.cursor) { + return this.cursor; + } + + await once(this, 'cursor'); + return this.cursor; +}; + +/** + * Registers a transform function which subsequently maps documents retrieved + * via the streams interface or `.next()` + * + * #### Example: + * + * // Map documents returned by `data` events + * Thing. + * find({ name: /^hello/ }). + * cursor(). + * map(function (doc) { + * doc.foo = "bar"; + * return doc; + * }) + * on('data', function(doc) { console.log(doc.foo); }); + * + * // Or map documents returned by `.next()` + * const cursor = Thing.find({ name: /^hello/ }). + * cursor(). + * map(function (doc) { + * doc.foo = "bar"; + * return doc; + * }); + * cursor.next(function(error, doc) { + * console.log(doc.foo); + * }); + * + * @param {Function} fn + * @return {QueryCursor} + * @memberOf QueryCursor + * @api public + * @method map + */ + +Object.defineProperty(QueryCursor.prototype, 'map', { + value: function(fn) { + this._transforms.push(fn); + return this; + }, + enumerable: true, + configurable: true, + writable: true +}); + +/** + * Marks this cursor as errored + * @method _markError + * @memberOf QueryCursor + * @instance + * @api private + */ + +QueryCursor.prototype._markError = function(error) { + this._error = error; + return this; +}; + +/** + * Marks this cursor as closed. Will stop streaming and subsequent calls to + * `next()` will error. + * + * @return {Promise} + * @api public + * @method close + * @emits close + * @see AggregationCursor.close https://mongodb.github.io/node-mongodb-native/7.0/classes/AggregationCursor.html#close + */ + +QueryCursor.prototype.close = async function close() { + if (typeof arguments[0] === 'function') { + throw new MongooseError('QueryCursor.prototype.close() no longer accepts a callback'); + } + try { + await this.cursor.close(); + this._closed = true; + this.emit('close'); + } catch (error) { + this.listeners('error').length > 0 && this.emit('error', error); + throw error; + } +}; + +/** + * Marks this cursor as destroyed. Will stop streaming and subsequent calls to + * `next()` will error. + * + * @return {this} + * @api private + * @method _destroy + */ + +QueryCursor.prototype._destroy = function _destroy(_err, callback) { + let waitForCursor = null; + if (!this.cursor) { + waitForCursor = new Promise((resolve) => { + this.once('cursor', resolve); + }); + } else { + waitForCursor = Promise.resolve(); + } + + waitForCursor + .then(() => { + this.cursor.close(); + }) + .then(() => { + this._closed = true; + callback(); + }) + .catch(error => { + callback(error); + }); + return this; +}; + +/** + * Rewind this cursor to its uninitialized state. Any options that are present on the cursor will + * remain in effect. Iterating this cursor will cause new queries to be sent to the server, even + * if the resultant data has already been retrieved by this cursor. + * + * @return {AggregationCursor} this + * @api public + * @method rewind + */ + +QueryCursor.prototype.rewind = function() { + _waitForCursor(this, () => { + this.cursor.rewind(); + }); + return this; +}; + +/** + * Get the next document from this cursor. Will return `null` when there are + * no documents left. + * + * @return {Promise} + * @api public + * @method next + */ + +QueryCursor.prototype.next = async function next() { + if (typeof arguments[0] === 'function') { + throw new MongooseError('QueryCursor.prototype.next() no longer accepts a callback'); + } + if (this._closed) { + throw new MongooseError('Cannot call `next()` on a closed cursor'); + } + return new Promise((resolve, reject) => { + _next(this, function(error, doc) { + if (error) { + return reject(error); + } + resolve(doc); + }); + }); +}; + +/** + * Execute `fn` for every document in the cursor. If `fn` returns a promise, + * will wait for the promise to resolve before iterating on to the next one. + * Returns a promise that resolves when done. + * + * #### Example: + * + * // Iterate over documents asynchronously + * Thing. + * find({ name: /^hello/ }). + * cursor(). + * eachAsync(async function (doc, i) { + * doc.foo = doc.bar + i; + * await doc.save(); + * }) + * + * @param {Function} fn + * @param {object} [options] + * @param {number} [options.parallel] the number of promises to execute in parallel. Defaults to 1. + * @param {number} [options.batchSize] if set, will call `fn()` with arrays of documents with length at most `batchSize` + * @param {boolean} [options.continueOnError=false] if true, `eachAsync()` iterates through all docs even if `fn` throws an error. If false, `eachAsync()` throws an error immediately if the given function `fn()` throws an error. + * @return {Promise} + * @api public + * @method eachAsync + */ + +QueryCursor.prototype.eachAsync = function(fn, opts) { + if (typeof arguments[2] === 'function') { + throw new MongooseError('QueryCursor.prototype.eachAsync() no longer accepts a callback'); + } + if (typeof opts === 'function') { + opts = {}; + } + opts = opts || {}; + + return eachAsync((cb) => _next(this, cb), fn, opts); +}; + +/** + * The `options` passed in to the `QueryCursor` constructor. + * + * @api public + * @property options + */ + +QueryCursor.prototype.options; + +/** + * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html#addCursorFlag). + * Useful for setting the `noCursorTimeout` and `tailable` flags. + * + * @param {'tailable'|'oplogReplay'|'noCursorTimeout'|'awaitData'|'partial'} flag + * @param {boolean} value + * @return {AggregationCursor} this + * @api public + * @method addCursorFlag + */ + +QueryCursor.prototype.addCursorFlag = function(flag, value) { + _waitForCursor(this, () => { + this.cursor.addCursorFlag(flag, value); + }); + return this; +}; + +/** + * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js). + * You do not need to call this function explicitly, the JavaScript runtime + * will call it for you. + * + * #### Example: + * + * // Works without using `cursor()` + * for await (const doc of Model.find([{ $sort: { name: 1 } }])) { + * console.log(doc.name); + * } + * + * // Can also use `cursor()` + * for await (const doc of Model.find([{ $sort: { name: 1 } }]).cursor()) { + * console.log(doc.name); + * } + * + * Node.js 10.x supports async iterators natively without any flags. You can + * enable async iterators in Node.js 8.x using the [`--harmony_async_iteration` flag](https://github.com/tc39/proposal-async-iteration/issues/117#issuecomment-346695187). + * + * **Note:** This function is not if `Symbol.asyncIterator` is undefined. If + * `Symbol.asyncIterator` is undefined, that means your Node.js version does not + * support async iterators. + * + * @method [Symbol.asyncIterator] + * @memberOf QueryCursor + * @instance + * @api public + */ + +if (Symbol.asyncIterator != null) { + QueryCursor.prototype[Symbol.asyncIterator] = function queryCursorAsyncIterator() { + // Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax + this._mongooseOptions._asyncIterator = true; + return this; + }; +} + +/** + * Get the next doc from the underlying cursor and mongooseify it + * (populate, etc.) + * @param {any} ctx + * @param {Function} cb + * @api private + */ + +function _next(ctx, cb) { + let callback = cb; + + // Create a custom callback to handle transforms, async iterator, and transformNull + callback = function(err, doc) { + if (err) { + return cb(err); + } + + // Handle null documents - if asyncIterator, we need to return `done: true`, otherwise just + // skip. In either case, avoid transforms. + if (doc === null) { + if (ctx._mongooseOptions._asyncIterator) { + return cb(null, { done: true }); + } else { + return cb(null, null); + } + } + + // Apply transforms + if (ctx._transforms.length && doc !== null) { + doc = ctx._transforms.reduce(function(doc, fn) { + return fn.call(ctx, doc); + }, doc); + } + + // This option is set in `Symbol.asyncIterator` code paths. + // For async iterator, we need to convert to {value, done} format + if (ctx._mongooseOptions._asyncIterator) { + return cb(null, { value: doc, done: false }); + } + + return cb(null, doc); + }; + + if (ctx._error) { + return immediate(function() { + callback(ctx._error); + }); + } + if (ctx.skipped) { + return immediate(() => callback(null, null)); + } + + if (ctx.cursor) { + if (ctx.query._mongooseOptions.populate && !ctx._pop) { + ctx._pop = helpers.preparePopulationOptionsMQ(ctx.query, + ctx.query._mongooseOptions); + ctx._pop.__noPromise = true; + } + if (ctx.query._mongooseOptions.populate && ctx.options._populateBatchSize > 1) { + if (ctx._batchDocs && ctx._batchDocs.length) { + // Return a cached populated doc + return _nextDoc(ctx, ctx._batchDocs.shift(), ctx._pop, callback); + } else if (ctx._batchExhausted) { + // Internal cursor reported no more docs. Act the same here + return callback(null, null); + } else { + // Request as many docs as batchSize, to populate them also in batch + ctx._batchDocs = []; + ctx.cursor.next().then( + res => { _onNext.call({ ctx, callback }, null, res); }, + err => { _onNext.call({ ctx, callback }, err); } + ); + return; + } + } else { + return ctx.cursor.next().then( + doc => { + if (!doc) { + callback(null, null); + return; + } + + if (!ctx.query._mongooseOptions.populate) { + return _nextDoc(ctx, doc, null, callback); + } + + _nextDoc(ctx, doc, ctx._pop, (err, doc) => { + if (err != null) { + return callback(err); + } + ctx.query.model.populate(doc, ctx._pop).then( + doc => callback(null, doc), + err => callback(err) + ); + }); + }, + error => { + callback(error); + } + ); + } + } else { + ctx.once('error', cb); + + ctx.once('cursor', function(cursor) { + ctx.removeListener('error', cb); + if (cursor == null) { + if (ctx.skipped) { + return cb(null, null); + } + return; + } + _next(ctx, cb); + }); + } +} + +/*! + * ignore + */ + +function _onNext(error, doc) { + if (error) { + return this.callback(error); + } + if (!doc) { + this.ctx._batchExhausted = true; + return _populateBatch.call(this); + } + + this.ctx._batchDocs.push(doc); + + if (this.ctx._batchDocs.length < this.ctx.options._populateBatchSize) { + // If both `batchSize` and `_populateBatchSize` are huge, calling `next()` repeatedly may + // cause a stack overflow. So make sure we clear the stack. + immediate(() => this.ctx.cursor.next().then( + res => { _onNext.call(this, null, res); }, + err => { _onNext.call(this, err); } + )); + } else { + _populateBatch.call(this); + } +} + +/*! + * ignore + */ + +function _populateBatch() { + if (!this.ctx._batchDocs.length) { + return this.callback(null, null); + } + this.ctx.query.model.populate(this.ctx._batchDocs, this.ctx._pop).then( + () => { + _nextDoc(this.ctx, this.ctx._batchDocs.shift(), this.ctx._pop, this.callback); + }, + err => { + this.callback(err); + } + ); +} + +/*! + * ignore + */ + +function _nextDoc(ctx, doc, pop, callback) { + if (ctx.query._mongooseOptions.lean) { + return ctx.model.hooks.execPost('find', ctx.query, [[doc]]).then(() => callback(null, doc), err => callback(err)); + } + + const { model, _fields, _userProvidedFields, options } = ctx.query; + helpers.createModelAndInit(model, doc, _fields, _userProvidedFields, options, pop, (err, doc) => { + if (err != null) { + return callback(err); + } + if (options.session != null) { + doc.$session(options.session); + } + ctx.model.hooks.execPost('find', ctx.query, [[doc]]).then(() => callback(null, doc), err => callback(err)); + }); +} + +/*! + * ignore + */ + +function _waitForCursor(ctx, cb) { + if (ctx.cursor) { + return cb(); + } + ctx.once('cursor', function(cursor) { + if (cursor == null) { + return; + } + cb(); + }); +} + +module.exports = QueryCursor; diff --git a/backend/node_modules/mongoose/lib/document.js b/backend/node_modules/mongoose/lib/document.js new file mode 100644 index 0000000..eda7441 --- /dev/null +++ b/backend/node_modules/mongoose/lib/document.js @@ -0,0 +1,5599 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const DivergentArrayError = require('./error/divergentArray'); +const EventEmitter = require('events').EventEmitter; +const InternalCache = require('./internal'); +const MongooseBuffer = require('./types/buffer'); +const MongooseError = require('./error/index'); +const MixedSchema = require('./schema/mixed'); +const ModifiedPathsSnapshot = require('./modifiedPathsSnapshot'); +const ObjectExpectedError = require('./error/objectExpected'); +const ObjectParameterError = require('./error/objectParameter'); +const ParallelValidateError = require('./error/parallelValidate'); +const Schema = require('./schema'); +const StrictModeError = require('./error/strict'); +const ValidationError = require('./error/validation'); +const ValidatorError = require('./error/validator'); +const $__hasIncludedChildren = require('./helpers/projection/hasIncludedChildren'); +const applyDefaults = require('./helpers/document/applyDefaults'); +const cleanModifiedSubpaths = require('./helpers/document/cleanModifiedSubpaths'); +const clone = require('./helpers/clone'); +const isInPathsToSave = require('./helpers/document/isInPathsToSave'); +const compile = require('./helpers/document/compile').compile; +const defineKey = require('./helpers/document/compile').defineKey; +const firstKey = require('./helpers/firstKey'); +const flatten = require('./helpers/common').flatten; +const getEmbeddedDiscriminatorPath = require('./helpers/document/getEmbeddedDiscriminatorPath'); +const getKeysInSchemaOrder = require('./helpers/schema/getKeysInSchemaOrder'); +const getSubdocumentStrictValue = require('./helpers/schema/getSubdocumentStrictValue'); +const handleSpreadDoc = require('./helpers/document/handleSpreadDoc'); +const isBsonType = require('./helpers/isBsonType'); +const isDefiningProjection = require('./helpers/projection/isDefiningProjection'); +const isExclusive = require('./helpers/projection/isExclusive'); +const isPathExcluded = require('./helpers/projection/isPathExcluded'); +const inspect = require('util').inspect; +const internalToObjectOptions = require('./options').internalToObjectOptions; +const markArraySubdocsPopulated = require('./helpers/populate/markArraySubdocsPopulated'); +const minimize = require('./helpers/minimize'); +const mpath = require('mpath'); +const parentPaths = require('./helpers/path/parentPaths'); +const queryhelpers = require('./queryHelpers'); +const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter'); +const utils = require('./utils'); +const isPromise = require('./helpers/isPromise'); + +const deepEqual = utils.deepEqual; +const isMongooseObject = utils.isMongooseObject; + +const arrayAtomicsBackupSymbol = require('./helpers/symbols').arrayAtomicsBackupSymbol; +const arrayAtomicsSymbol = require('./helpers/symbols').arrayAtomicsSymbol; +const documentArrayParent = require('./helpers/symbols').documentArrayParent; +const documentIsModified = require('./helpers/symbols').documentIsModified; +const documentModifiedPaths = require('./helpers/symbols').documentModifiedPaths; +const documentSchemaSymbol = require('./helpers/symbols').documentSchemaSymbol; +const getSymbol = require('./helpers/symbols').getSymbol; +const modelSymbol = require('./helpers/symbols').modelSymbol; +const populateModelSymbol = require('./helpers/symbols').populateModelSymbol; +const scopeSymbol = require('./helpers/symbols').scopeSymbol; +const schemaMixedSymbol = require('./schema/symbols').schemaMixedSymbol; +const getDeepestSubdocumentForPath = require('./helpers/document/getDeepestSubdocumentForPath'); +const sessionNewDocuments = require('./helpers/symbols').sessionNewDocuments; + +let DocumentArray; +let Embedded; + +const specialProperties = utils.specialProperties; + +const VERSION_WHERE = 1; +const VERSION_INC = 2; +const VERSION_ALL = VERSION_WHERE | VERSION_INC; + +/** + * The core Mongoose document constructor. You should not call this directly, + * the Mongoose [Model constructor](./api/model.html#Model) calls this for you. + * + * @param {object} obj the values to set + * @param {object} [fields] optional object containing the fields which were selected in the query returning this document and any populated paths data + * @param {object} [options] various configuration options for the document + * @param {boolean} [options.defaults=true] if `false`, skip applying default values to this document. + * @param {boolean} [options.skipId=false] By default, Mongoose document if one is not provided and the document's schema does not override Mongoose's default `_id`. Set `skipId` to `true` to skip this generation step. + * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#class-eventemitter + * @event `init`: Emitted on a document after it has been retrieved from the db and fully hydrated by Mongoose. + * @event `save`: Emitted when the document is successfully saved + * @api private + */ + +function Document(obj, fields, options) { + if (typeof options === 'boolean') { + throw new Error('The skipId parameter has been removed. Use { skipId: true } in the options parameter instead.'); + } + options = Object.assign({}, options); + let skipId = options.skipId; + + this.$__ = new InternalCache(); + + // Support `browserDocument.js` syntax + if (this.$__schema == null) { + const _schema = utils.isObject(fields) && !fields.instanceOfSchema ? + new Schema(fields) : + fields; + + this.$__setSchema(_schema); + fields = options; + skipId = options.skipId; + } + + // Avoid setting `isNew` to `true`, because it is `true` by default + if (options.isNew != null && options.isNew !== true) { + this.$isNew = options.isNew; + } + + if (options.priorDoc != null) { + this.$__.priorDoc = options.priorDoc; + } + + if (skipId) { + this.$__.skipId = skipId; + } + + if (obj != null && typeof obj !== 'object') { + throw new ObjectParameterError(obj, 'obj', 'Document'); + } + + let defaults = true; + if (options.defaults !== undefined) { + this.$__.defaults = options.defaults; + defaults = options.defaults; + } + + const schema = this.$__schema; + + if (typeof fields === 'boolean' || fields === 'throw') { + if (fields !== true) { + this.$__.strictMode = fields; + } + fields = undefined; + } else if (options.strict !== undefined) { + this.$__.strictMode = options.strict; + } else if (schema.options.strict !== true) { + this.$__.strictMode = schema.options.strict; + } + + const requiredPaths = schema.requiredPaths(true); + for (const path of requiredPaths) { + this.$__.activePaths.require(path); + } + + let exclude = null; + + // determine if this doc is a result of a query with + // excluded fields + if (utils.isPOJO(fields) && utils.hasOwnKeys(fields)) { + exclude = isExclusive(fields); + this.$__.selected = fields; + this.$__.exclude = exclude; + } + + const hasIncludedChildren = exclude === false && fields ? + $__hasIncludedChildren(fields) : + null; + + if (this._doc == null) { + this.$__buildDoc(obj, fields, skipId, exclude, hasIncludedChildren, false); + + // By default, defaults get applied **before** setting initial values + // Re: gh-6155 + if (defaults) { + applyDefaults(this, fields, exclude, hasIncludedChildren, true, null, { + skipParentChangeTracking: true + }); + } + } + if (obj) { + // Skip set hooks + if (this.$__original_set) { + this.$__original_set(obj, undefined, true, options); + } else { + this.$set(obj, undefined, true, options); + } + + if (obj instanceof Document) { + this.$isNew = obj.$isNew; + } + } + + // Function defaults get applied **after** setting initial values so they + // see the full doc rather than an empty one, unless they opt out. + // Re: gh-3781, gh-6155 + if (options.willInit && defaults) { + if (options.skipDefaults) { + this.$__.skipDefaults = options.skipDefaults; + } + } else if (defaults) { + applyDefaults(this, fields, exclude, hasIncludedChildren, false, options.skipDefaults); + } + + if (!this.$__.strictMode && obj) { + const _this = this; + const keys = Object.keys(this._doc); + + keys.forEach(function(key) { + // Avoid methods, virtuals, existing fields, and `$` keys. The latter is to avoid overwriting + // Mongoose internals. + if (!(key in schema.tree) && !(key in schema.methods) && !(key in schema.virtuals) && !key.startsWith('$')) { + defineKey({ prop: key, subprops: null, prototype: _this }); + } + }); + } + + applyQueue(this); +} + +Document.prototype.$isMongooseDocumentPrototype = true; + +/** + * Boolean flag specifying if the document is new. If you create a document + * using `new`, this document will be considered "new". `$isNew` is how + * Mongoose determines whether `save()` should use `insertOne()` to create + * a new document or `updateOne()` to update an existing document. + * + * #### Example: + * + * const user = new User({ name: 'John Smith' }); + * user.$isNew; // true + * + * await user.save(); // Sends an `insertOne` to MongoDB + * + * On the other hand, if you load an existing document from the database + * using `findOne()` or another [query operation](https://mongoosejs.com/docs/queries.html), + * `$isNew` will be false. + * + * #### Example: + * + * const user = await User.findOne({ name: 'John Smith' }); + * user.$isNew; // false + * + * Mongoose sets `$isNew` to `false` immediately after `save()` succeeds. + * That means Mongoose sets `$isNew` to false **before** `post('save')` hooks run. + * In `post('save')` hooks, `$isNew` will be `false` if `save()` succeeded. + * + * #### Example: + * + * userSchema.post('save', function() { + * this.$isNew; // false + * }); + * await User.create({ name: 'John Smith' }); + * + * For subdocuments, `$isNew` is true if either the parent has `$isNew` set, + * or if you create a new subdocument. + * + * #### Example: + * + * // Assume `Group` has a document array `users` + * const group = await Group.findOne(); + * group.users[0].$isNew; // false + * + * group.users.push({ name: 'John Smith' }); + * group.users[1].$isNew; // true + * + * @api public + * @property $isNew + * @memberOf Document + * @instance + */ + +Object.defineProperty(Document.prototype, 'isNew', { + get: function() { + return this.$isNew; + }, + set: function(value) { + this.$isNew = value; + } +}); + +/** + * Hash containing current validation errors. + * + * @api public + * @property errors + * @memberOf Document + * @instance + */ + +Object.defineProperty(Document.prototype, 'errors', { + get: function() { + return this.$errors; + }, + set: function(value) { + this.$errors = value; + } +}); + +/*! + * ignore + */ + +Document.prototype.$isNew = true; + +/*! + * Document exposes the NodeJS event emitter API, so you can use + * `on`, `once`, etc. + */ +utils.each( + ['on', 'once', 'emit', 'listeners', 'removeListener', 'setMaxListeners', + 'removeAllListeners', 'addListener'], + function(emitterFn) { + Document.prototype[emitterFn] = function() { + // Delay creating emitter until necessary because emitters take up a lot of memory, + // especially for subdocuments. + if (!this.$__.emitter) { + if (emitterFn === 'emit') { + return; + } + this.$__.emitter = new EventEmitter(); + this.$__.emitter.setMaxListeners(0); + } + return this.$__.emitter[emitterFn].apply(this.$__.emitter, arguments); + }; + Document.prototype[`$${emitterFn}`] = Document.prototype[emitterFn]; + }); + +Document.prototype.constructor = Document; + +for (const i in EventEmitter.prototype) { + Document[i] = EventEmitter.prototype[i]; +} + +/** + * The document's internal schema. + * + * @api private + * @property schema + * @memberOf Document + * @instance + */ + +Document.prototype.$__schema; + +/** + * The document's schema. + * + * @api public + * @property schema + * @memberOf Document + * @instance + */ + +Document.prototype.schema; + +/** + * Empty object that you can use for storing properties on the document. This + * is handy for passing data to middleware without conflicting with Mongoose + * internals. + * + * #### Example: + * + * schema.pre('save', function() { + * // Mongoose will set `isNew` to `false` if `save()` succeeds + * this.$locals.wasNew = this.isNew; + * }); + * + * schema.post('save', function() { + * // Prints true if `isNew` was set before `save()` + * console.log(this.$locals.wasNew); + * }); + * + * @api public + * @property $locals + * @memberOf Document + * @instance + */ + +Object.defineProperty(Document.prototype, '$locals', { + configurable: false, + enumerable: false, + get: function() { + if (this.$__.locals == null) { + this.$__.locals = {}; + } + return this.$__.locals; + }, + set: function(v) { + this.$__.locals = v; + } +}); + +/** + * Legacy alias for `$isNew`. + * + * @api public + * @property isNew + * @memberOf Document + * @see $isNew https://mongoosejs.com/docs/api/document.html#Document.prototype.$isNew + * @instance + */ + +Document.prototype.isNew; + +/** + * Set this property to add additional query filters when Mongoose saves this document and `isNew` is false. + * + * #### Example: + * + * // Make sure `save()` never updates a soft deleted document. + * schema.pre('save', function() { + * this.$where = { isDeleted: false }; + * }); + * + * @api public + * @property $where + * @memberOf Document + * @instance + */ + +Object.defineProperty(Document.prototype, '$where', { + configurable: false, + enumerable: false, + writable: true +}); + +/** + * The string version of this documents _id. + * + * #### Note: + * + * This getter exists on all documents by default. The getter can be disabled by setting the `id` [option](https://mongoosejs.com/docs/guide.html#id) of its `Schema` to false at construction time. + * + * new Schema({ name: String }, { id: false }); + * + * @api public + * @see Schema options https://mongoosejs.com/docs/guide.html#options + * @property id + * @memberOf Document + * @instance + */ + +Document.prototype.id; + +/** + * Hash containing current validation $errors. + * + * @api public + * @property $errors + * @memberOf Document + * @instance + */ + +Document.prototype.$errors; + +/** + * A string containing the current operation that Mongoose is executing + * on this document. May be `null`, `'save'`, `'validate'`, or `'remove'`. + * + * #### Example: + * + * const doc = new Model({ name: 'test' }); + * doc.$op; // null + * + * const promise = doc.save(); + * doc.$op; // 'save' + * + * await promise; + * doc.$op; // null + * + * @api public + * @property $op + * @memberOf Document + * @instance + */ + +Object.defineProperty(Document.prototype, '$op', { + get: function() { + return this.$__.op || null; + }, + set: function(value) { + this.$__.op = value; + } +}); + +/*! + * ignore + */ + +function $applyDefaultsToNested(val, path, doc) { + if (val == null) { + return; + } + + const paths = Object.keys(doc.$__schema.paths); + const plen = paths.length; + + const pathPieces = path.indexOf('.') === -1 ? [path] : path.split('.'); + + for (let i = 0; i < plen; ++i) { + let curPath = ''; + const p = paths[i]; + + if (!p.startsWith(path + '.')) { + continue; + } + + const type = doc.$__schema.paths[p]; + const pieces = type.splitPath().slice(pathPieces.length); + const len = pieces.length; + + if (type.defaultValue === void 0) { + continue; + } + + let cur = val; + + for (let j = 0; j < len; ++j) { + if (cur == null) { + break; + } + + const piece = pieces[j]; + + if (j === len - 1) { + if (cur[piece] !== void 0) { + break; + } + + try { + const def = type.getDefault(doc, false); + if (def !== void 0) { + cur[piece] = def; + } + } catch (err) { + doc.invalidate(path + '.' + curPath, err); + break; + } + + break; + } + + curPath += (!curPath.length ? '' : '.') + piece; + + cur[piece] = cur[piece] || {}; + cur = cur[piece]; + } + } +} + +/** + * Builds the default doc structure + * + * @param {object} obj + * @param {object} [fields] + * @param {boolean} [skipId] + * @param {boolean} [exclude] + * @param {object} [hasIncludedChildren] + * @api private + * @method $__buildDoc + * @memberOf Document + * @instance + */ + +Document.prototype.$__buildDoc = function(obj, fields, skipId, exclude, hasIncludedChildren) { + const doc = {}; + + const paths = Object.keys(this.$__schema.paths). + // Don't build up any paths that are underneath a map, we don't know + // what the keys will be + filter(p => !p.includes('$*')); + const plen = paths.length; + let ii = 0; + + for (; ii < plen; ++ii) { + const p = paths[ii]; + + if (p === '_id') { + if (skipId) { + continue; + } + if (obj && '_id' in obj) { + continue; + } + } + + const path = this.$__schema.paths[p].splitPath(); + const len = path.length; + const last = len - 1; + let curPath = ''; + let doc_ = doc; + let included = false; + + for (let i = 0; i < len; ++i) { + const piece = path[i]; + + if (!curPath.length) { + curPath = piece; + } else { + curPath += '.' + piece; + } + + // support excluding intermediary levels + if (exclude === true) { + if (curPath in fields) { + break; + } + } else if (exclude === false && fields && !included) { + if (curPath in fields) { + included = true; + } else if (!hasIncludedChildren[curPath]) { + break; + } + } + + if (i < last) { + doc_ = doc_[piece] || (doc_[piece] = {}); + } + } + } + + this._doc = doc; +}; + +/*! + * Converts to POJO when you use the document for querying + */ + +Document.prototype.toBSON = function() { + return this.toObject(internalToObjectOptions); +}; + +/** + * Hydrates this document with the data in `doc`. Does not run setters or mark any paths modified. + * + * Called internally after a document is returned from MongoDB. Normally, + * you do **not** need to call this function on your own. + * + * This function triggers `init` [middleware](https://mongoosejs.com/docs/middleware.html). + * Note that `init` hooks are [synchronous](https://mongoosejs.com/docs/middleware.html#synchronous). + * + * @param {object} doc raw document returned by mongo + * @param {object} [opts] + * @param {boolean} [opts.hydratedPopulatedDocs=false] If true, hydrate and mark as populated any paths that are populated in the raw document + * @param {Function} [fn] + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.init = function(doc, opts, fn) { + if (typeof opts === 'function') { + fn = opts; + opts = null; + } + + if (doc == null) { + throw new ObjectParameterError(doc, 'doc', 'init'); + } + + this.$__init(doc, opts); + + if (fn) { + fn(null, this); + } + + return this; +}; + +/** + * Alias for [`.init`](https://mongoosejs.com/docs/api/document.html#Document.prototype.init()) + * + * @api public + */ + +Document.prototype.$init = function() { + return this.constructor.prototype.init.apply(this, arguments); +}; + +/** + * Internal "init" function + * + * @param {Document} doc + * @param {object} [opts] + * @returns {Document} this + * @api private + */ + +Document.prototype.$__init = function(doc, opts) { + if (doc == null) { + throw new ObjectParameterError(doc, 'doc', 'init'); + } + this.$isNew = false; + opts = opts || {}; + + // handle docs with populated paths + // If doc._id is not null or undefined + if (doc._id != null && opts.populated && opts.populated.length) { + const id = String(doc._id); + for (const item of opts.populated) { + if (item.isVirtual) { + this.$populated(item.path, utils.getValue(item.path, doc), item); + } else { + this.$populated(item.path, item._docs[id], item); + } + + if (item._childDocs == null) { + continue; + } + for (const child of item._childDocs) { + if (child?.$__ == null) { + continue; + } + child.$__.parent = this; + } + item._childDocs = []; + } + } + + init(this, doc, this._doc, opts); + + markArraySubdocsPopulated(this, opts.populated); + this.$emit('init', this); + this.constructor.emit('init', this); + + const hasIncludedChildren = this.$__.exclude === false && this.$__.selected ? + $__hasIncludedChildren(this.$__.selected) : + null; + + applyDefaults(this, this.$__.selected, this.$__.exclude, hasIncludedChildren, false, this.$__.skipDefaults); + return this; +}; + +/** + * Init helper. + * + * @param {object} self document instance + * @param {object} obj raw mongodb doc + * @param {object} doc object we are initializing + * @param {object} [opts] Optional Options + * @param {boolean} [opts.setters] Call `applySetters` instead of `cast` + * @param {string} [prefix] Prefix to add to each path + * @api private + */ + +function init(self, obj, doc, opts, prefix) { + prefix = prefix || ''; + + if (typeof obj !== 'object' || Array.isArray(obj)) { + throw new ObjectExpectedError(self.$basePath, obj); + } + + if (obj.$__ != null) { + obj = obj._doc; + } + const keys = Object.keys(obj); + const len = keys.length; + let schemaType; + let path; + let i; + const strict = self.$__.strictMode; + const docSchema = self.$__schema; + + for (let index = 0; index < len; ++index) { + i = keys[index]; + // avoid prototype pollution + if (specialProperties.has(i)) { + continue; + } + path = prefix ? prefix + i : i; + schemaType = docSchema.path(path); + // Should still work if not a model-level discriminator, but should not be + // necessary. This is *only* to catch the case where we queried using the + // base model and the discriminated model has a projection + if (docSchema.$isRootDiscriminator && !self.$__isSelected(path)) { + continue; + } + + const value = obj[i]; + if (!schemaType && utils.isPOJO(value)) { + // assume nested object + if (!doc[i]) { + doc[i] = {}; + if (!strict && !(i in docSchema.tree) && !(i in docSchema.methods) && !(i in docSchema.virtuals)) { + self[i] = doc[i]; + } else if (opts?.virtuals && (i in docSchema.virtuals)) { + self[i] = doc[i]; + } + } + init(self, value, doc[i], opts, path + '.'); + } else if (!schemaType) { + doc[i] = value; + if (!strict && !prefix) { + self[i] = value; + } else if (opts?.virtuals && (i in docSchema.virtuals)) { + self[i] = value; + } else if (opts?.strict === 'throw') { + // Only use strict: 'throw' semantics if explicit `strict: 'throw'` option + // passed in, like via `MyModel.hydrate(obj, null, { strict: 'throw' })` + // This is for backwards compatibility - strict: 'throw' at the schema level + // does not apply to documents loaded from the db. + throw new StrictModeError(i); + } + } else { + // Retain order when overwriting defaults + if (Object.hasOwn(doc, i) && value !== void 0 && !opts.hydratedPopulatedDocs) { + delete doc[i]; + } + if (value === null) { + doc[i] = schemaType._castNullish(null); + } else if (value !== undefined) { + const wasPopulated = value.$__ == null ? null : value.$__.wasPopulated; + + if (schemaType && !wasPopulated && !opts.hydratedPopulatedDocs) { + try { + if (opts?.setters) { + // Call applySetters with `init = false` because otherwise setters are a noop + const overrideInit = false; + doc[i] = schemaType.applySetters(value, self, overrideInit, null, opts); + } else { + doc[i] = schemaType.cast(value, self, true, undefined, opts); + } + } catch (e) { + self.invalidate(e.path, new ValidatorError({ + path: e.path, + message: e.message, + type: 'cast', + value: e.value, + reason: e + })); + } + } else if (schemaType && opts.hydratedPopulatedDocs) { + doc[i] = schemaType.cast(value, self, true, undefined, { hydratedPopulatedDocs: true }); + + if (doc[i]?.$__?.wasPopulated) { + self.$populated(path, doc[i].$__.wasPopulated.value, doc[i].$__.wasPopulated.options); + } else if (Array.isArray(doc[i]) && doc[i].length && doc[i][0]?.$__?.wasPopulated) { + self.$populated(path, doc[i].map(populatedDoc => populatedDoc?.$__?.wasPopulated?.value).filter(val => val != null), doc[i][0].$__.wasPopulated.options); + } + } else { + doc[i] = value; + } + } + // mark as hydrated + if (!self.$isModified(path)) { + self.$__.activePaths.init(path); + } + } + } +} + +/** + * Sends an updateOne command with this document `_id` as the query selector. + * + * #### Example: + * + * weirdCar.updateOne({$inc: {wheels:1}}, { w: 1 }); + * + * #### Valid options: + * + * - same as in [Model.updateOne](https://mongoosejs.com/docs/api/model.html#Model.updateOne) + * + * @see Model.updateOne https://mongoosejs.com/docs/api/model.html#Model.updateOne + * @param {object} update + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and the [Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {Query} + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.updateOne = function updateOne(update, options) { + const query = this.constructor.updateOne(); + const self = this; + query.pre(async function queryPreUpdateOne() { + const res = await self._execDocumentPreHooks('updateOne', options, [self, update, options]); + // `self` is passed to pre hooks as argument for backwards compatibility, but that + // isn't the actual arguments passed to the wrapped function. + if (res[0] !== self || res[1] !== update || res[2] !== options) { + throw new Error('Document updateOne pre hooks cannot overwrite arguments'); + } + query.updateOne({ _id: self._doc._id }, update, options); + // Apply custom where conditions _after_ document updateOne middleware for + // consistency with save() - sharding plugin needs to set $where + if (self.$where != null) { + this.where(self.$where); + } + if (self.$session() != null) { + if (!('session' in query.options)) { + query.options.session = self.$session(); + } + } + return res; + }); + query.post(function queryPostUpdateOne() { + return self._execDocumentPostHooks('updateOne', options); + }); + + return query; +}; + +/** + * Sends a replaceOne command with this document `_id` as the query selector. + * + * #### Valid options: + * + * - same as in [Model.replaceOne](https://mongoosejs.com/docs/api/model.html#Model.replaceOne()) + * + * @see Model.replaceOne https://mongoosejs.com/docs/api/model.html#Model.replaceOne() + * @param {object} doc + * @param {object} [options] + * @param {Function} [callback] + * @return {Query} + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.replaceOne = function replaceOne() { + const args = [...arguments]; + args.unshift({ _id: this._doc._id }); + return this.constructor.replaceOne.apply(this.constructor, args); +}; + +/** + * Getter/setter around the session associated with this document. Used to + * automatically set `session` if you `save()` a doc that you got from a + * query with an associated session. + * + * #### Example: + * + * const session = MyModel.startSession(); + * const doc = await MyModel.findOne().session(session); + * doc.$session() === session; // true + * doc.$session(null); + * doc.$session() === null; // true + * + * If this is a top-level document, setting the session propagates to all child + * docs. + * + * @param {ClientSession} [session] overwrite the current session + * @return {ClientSession} + * @method $session + * @api public + * @memberOf Document + */ + +Document.prototype.$session = function $session(session) { + if (arguments.length === 0) { + if (this.$__.session?.hasEnded) { + this.$__.session = null; + return null; + } + return this.$__.session; + } + + if (session?.hasEnded) { + throw new MongooseError('Cannot set a document\'s session to a session that has ended. Make sure you haven\'t ' + + 'called `endSession()` on the session you are passing to `$session()`.'); + } + + if (session == null && this.$__.session == null) { + return; + } + + this.$__.session = session; + + if (!this.$isSubdocument) { + const subdocs = this.$getAllSubdocs(); + for (const child of subdocs) { + child.$session(session); + } + } + + return session; +}; + +/** + * Getter/setter around whether this document will apply timestamps by + * default when using `save()` and `bulkSave()`. + * + * #### Example: + * + * const TestModel = mongoose.model('Test', new Schema({ name: String }, { timestamps: true })); + * const doc = new TestModel({ name: 'John Smith' }); + * + * doc.$timestamps(); // true + * + * doc.$timestamps(false); + * await doc.save(); // Does **not** apply timestamps + * + * @param {boolean} [value] overwrite the current session + * @return {Document|boolean|undefined} When used as a getter (no argument), a boolean will be returned indicating the timestamps option state or if unset "undefined" will be used, otherwise will return "this" + * @method $timestamps + * @api public + * @memberOf Document + */ + +Document.prototype.$timestamps = function $timestamps(value) { + if (arguments.length === 0) { + if (this.$__.timestamps != null) { + return this.$__.timestamps; + } + + if (this.$__schema) { + return this.$__schema.options.timestamps; + } + + return undefined; + } + + const currentValue = this.$timestamps(); + if (value !== currentValue) { + this.$__.timestamps = value; + } + + return this; +}; + +/** + * Overwrite all values in this document with the values of `obj`, except + * for immutable properties. Behaves similarly to `set()`, except for it + * unsets all properties that aren't in `obj`. + * + * @param {object} obj the object to overwrite this document with + * @method overwrite + * @memberOf Document + * @instance + * @api public + * @return {Document} this + */ + +Document.prototype.overwrite = function overwrite(obj) { + const keys = new Set(Object.keys(this._doc)); + for (const key of Object.keys(obj)) { + keys.add(key); + } + + const schemaOptions = this.$__schema.options; + for (const key of keys) { + if (key === '_id') { + continue; + } + // Explicitly skip version key + if (schemaOptions.versionKey && key === schemaOptions.versionKey) { + continue; + } + if (schemaOptions.discriminatorKey && key === schemaOptions.discriminatorKey) { + continue; + } + this.$set(key, obj[key]); + } + + return this; +}; + +/** + * Alias for `set()`, used internally to avoid conflicts + * + * @param {string|object} path path or object of key/vals to set + * @param {any} val the value to set + * @param {Schema|string|number|Buffer|any} [type] optionally specify a type for "on-the-fly" attributes + * @param {object} [options] optionally specify options that modify the behavior of the set + * @param {boolean} [options.merge=false] if true, setting a [nested path](https://mongoosejs.com/docs/subdocs.html#subdocuments-versus-nested-paths) will merge existing values rather than overwrite the whole object. So `doc.set('nested', { a: 1, b: 2 })` becomes `doc.set('nested.a', 1); doc.set('nested.b', 2);` + * @return {Document} this + * @method $set + * @memberOf Document + * @instance + * @api public + */ + +Document.prototype.$set = function $set(path, val, type, options) { + if (utils.isPOJO(type)) { + options = type; + type = undefined; + } + + const merge = options?.merge; + const adhoc = type && type !== true; + const constructing = type === true; + let adhocs; + let keys; + let i = 0; + let pathtype; + let key; + let prefix; + + const userSpecifiedStrict = options && 'strict' in options; + let strict = userSpecifiedStrict + ? options.strict + : this.$__.strictMode; + + if (adhoc) { + adhocs = this.$__.adhocPaths || (this.$__.adhocPaths = {}); + adhocs[path] = this.$__schema.interpretAsType(path, type, this.$__schema.options); + } + + if (path == null) { + [path, val] = [val, path]; + } else if (typeof path !== 'string') { + // new Document({ key: val }) + if (path instanceof Document) { + if (path.$__isNested) { + path = path.toObject(); + } else { + // This ternary is to support gh-7898 (copying virtuals if same schema) + // while not breaking gh-10819, which for some reason breaks if we use toObject() + path = path.$__schema === this.$__schema + ? applyVirtuals(path, { ...path._doc }) + : path._doc; + } + } + if (path == null) { + [path, val] = [val, path]; + } + + prefix = val ? val + '.' : ''; + keys = getKeysInSchemaOrder(this.$__schema, path); + + const len = keys.length; + + // `_skipMinimizeTopLevel` is because we may have deleted the top-level + // nested key to ensure key order. + const _skipMinimizeTopLevel = options?._skipMinimizeTopLevel || false; + if (len === 0 && _skipMinimizeTopLevel) { + delete options._skipMinimizeTopLevel; + if (val) { + this.$set(val, {}); + } + return this; + } + + options = Object.assign({}, options, { _skipMinimizeTopLevel: false }); + + for (let i = 0; i < len; ++i) { + key = keys[i]; + const pathName = prefix ? prefix + key : key; + pathtype = this.$__schema.pathType(pathName); + const valForKey = path[key]; + + // On initial set, delete any nested keys if we're going to overwrite + // them to ensure we keep the user's key order. + if (type === true && + !prefix && + valForKey != null && + pathtype === 'nested' && + this._doc[key] != null) { + delete this._doc[key]; + } + + if (utils.isNonBuiltinObject(valForKey) && pathtype === 'nested') { + this.$set(pathName, valForKey, constructing, options); + $applyDefaultsToNested(this.$get(pathName), pathName, this); + continue; + } else if (strict) { + // Don't overwrite defaults with undefined keys (gh-3981) (gh-9039) + if (constructing && valForKey === void 0 && + this.$get(pathName) !== void 0) { + continue; + } + + if (pathtype === 'adhocOrUndefined') { + pathtype = getEmbeddedDiscriminatorPath(this, pathName, { typeOnly: true }); + } + + if (pathtype === 'real' || pathtype === 'virtual') { + this.$set(pathName, valForKey, constructing, options); + } else if (pathtype === 'nested' && valForKey instanceof Document) { + this.$set(pathName, + valForKey.toObject({ transform: false }), constructing, options); + } else if (strict === 'throw') { + if (pathtype === 'nested') { + throw new ObjectExpectedError(key, valForKey); + } else { + throw new StrictModeError(key); + } + } else if (pathtype === 'nested' && valForKey == null) { + this.$set(pathName, valForKey, constructing, options); + } + } else { + this.$set(pathName, valForKey, constructing, options); + } + } + + // Ensure all properties are in correct order + const orderedDoc = {}; + const orderedKeys = Object.keys(this.$__schema.tree); + for (let i = 0, len = orderedKeys.length; i < len; ++i) { + (key = orderedKeys[i]) && + (Object.hasOwn(this._doc, key)) && + (orderedDoc[key] = undefined); + } + this._doc = Object.assign(orderedDoc, this._doc); + + return this; + } + + let pathType = this.$__schema.pathType(path); + let parts = null; + if (pathType === 'adhocOrUndefined') { + parts = path.indexOf('.') === -1 ? [path] : path.split('.'); + pathType = getEmbeddedDiscriminatorPath(this, parts, { typeOnly: true }); + } + if (pathType === 'adhocOrUndefined' && !userSpecifiedStrict) { + // May be path underneath non-strict schema + if (parts == null) { + parts = path.indexOf('.') === -1 ? [path] : path.split('.'); + } + const subdocStrict = getSubdocumentStrictValue(this.$__schema, parts); + if (subdocStrict !== undefined) { + strict = subdocStrict; + } + } + + // Assume this is a Mongoose document that was copied into a POJO using + // `Object.assign()` or `{...doc}` + val = handleSpreadDoc(val, true); + + // if this doc is being constructed we should not trigger getters + const priorVal = (() => { + if (this.$__.priorDoc != null) { + return this.$__.priorDoc.$__getValue(path); + } + if (constructing) { + return void 0; + } + return this.$__getValue(path); + })(); + + if (pathType === 'nested' && val) { + if (typeof val === 'object' && val != null) { + if (val.$__ != null) { + val = val.toObject(internalToObjectOptions); + } + if (val == null) { + this.invalidate(path, new MongooseError.CastError('Object', val, path)); + return this; + } + const wasModified = this.$isModified(path); + const hasInitialVal = this.$__.savedState != null && Object.hasOwn(this.$__.savedState, path); + if (this.$__.savedState != null && !this.$isNew && !Object.hasOwn(this.$__.savedState, path)) { + const initialVal = this.$__getValue(path); + this.$__.savedState[path] = initialVal; + + const keys = Object.keys(initialVal || {}); + for (const key of keys) { + this.$__.savedState[path + '.' + key] = initialVal[key]; + } + } + + if (!merge) { + this.$__setValue(path, null); + cleanModifiedSubpaths(this, path); + } else { + return this.$set(val, path, constructing, options); + } + + const keys = getKeysInSchemaOrder(this.$__schema, val, path); + + this.$__setValue(path, {}); + for (const key of keys) { + this.$set(path + '.' + key, val[key], constructing, { ...options, _skipMarkModified: true }); + } + if (priorVal != null && + (!wasModified || hasInitialVal) && + utils.deepEqual(hasInitialVal ? this.$__.savedState[path] : priorVal, val)) { + this.unmarkModified(path); + } else { + this.markModified(path); + } + return this; + } + this.invalidate(path, new MongooseError.CastError('Object', val, path)); + return this; + } + + let schema; + if (parts == null) { + parts = path.indexOf('.') === -1 ? [path] : path.split('.'); + } + + // Might need to change path for top-level alias + if (typeof this.$__schema.aliases[parts[0]] === 'string') { + parts[0] = this.$__schema.aliases[parts[0]]; + } + + if (pathType === 'adhocOrUndefined' && strict) { + // check for roots that are Mixed types + let mixed; + + for (i = 0; i < parts.length; ++i) { + const subpath = parts.slice(0, i + 1).join('.'); + + // If path is underneath a virtual, bypass everything and just set it. + if (i + 1 < parts.length && this.$__schema.pathType(subpath) === 'virtual') { + mpath.set(path, val, this); + return this; + } + + schema = this.$__schema.path(subpath); + if (schema == null) { + continue; + } + + if (schema instanceof MixedSchema) { + // allow changes to sub paths of mixed types + mixed = true; + break; + } else if (schema.$isSchemaMap && schema.$__schemaType instanceof MixedSchema && i < parts.length - 1) { + // Map of mixed and not the last element in the path resolves to mixed + mixed = true; + schema = schema.$__schemaType; + break; + } + } + + if (schema == null) { + // Check for embedded discriminators + schema = getEmbeddedDiscriminatorPath(this, path); + } + + if (!mixed && !schema) { + if (strict === 'throw') { + throw new StrictModeError(path); + } + return this; + } + } else if (pathType === 'virtual') { + schema = this.$__schema.virtualpath(path); + schema.applySetters(val, this); + return this; + } else { + schema = this.$__path(path); + } + + // gh-4578, if setting a deeply nested path that doesn't exist yet, create it + let cur = this._doc; + let curPath = ''; + for (i = 0; i < parts.length - 1; ++i) { + cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; + curPath += (curPath.length !== 0 ? '.' : '') + parts[i]; + if (!cur) { + this.$set(curPath, {}); + // Hack re: gh-5800. If nested field is not selected, it probably exists + // so `MongoServerError: cannot use the part (nested of nested.num) to + // traverse the element ({nested: null})` is not likely. If user gets + // that error, its their fault for now. We should reconsider disallowing + // modifying not selected paths for 6.x + if (!this.$__isSelected(curPath)) { + this.unmarkModified(curPath); + } + cur = this.$__getValue(curPath); + } + } + + let pathToMark; + + // When using the $set operator the path to the field must already exist. + // Else mongodb throws: "LEFT_SUBFIELD only supports Object" + + if (parts.length <= 1) { + pathToMark = path; + } else { + const len = parts.length; + for (i = 0; i < len; ++i) { + const subpath = parts.slice(0, i + 1).join('.'); + if (this.$get(subpath, null, { getters: false }) === null) { + pathToMark = subpath; + break; + } + } + + if (!pathToMark) { + pathToMark = path; + } + } + + if (!schema) { + this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); + + if (pathType === 'nested' && val == null) { + cleanModifiedSubpaths(this, path); + } + return this; + } + + // If overwriting a subdocument path, make sure to clear out + // any errors _before_ setting, so new errors that happen + // get persisted. Re: #9080 + if (schema.$isSingleNested || schema.$isMongooseArray) { + _markValidSubpaths(this, path); + } + + if (val != null && merge && schema.$isSingleNested) { + if (val instanceof Document) { + val = val.toObject({ virtuals: false, transform: false }); + } + const keys = Object.keys(val); + for (const key of keys) { + this.$set(path + '.' + key, val[key], constructing, options); + } + + return this; + } + + let shouldSet = true; + try { + // If the user is trying to set a ref path to a document with + // the correct model name, treat it as populated + const refMatches = (() => { + if (schema.options == null) { + return false; + } + if (!(val instanceof Document)) { + return false; + } + const model = val.constructor; + + // Check ref + const refOpt = typeof schema.options.ref === 'function' && !schema.options.ref[modelSymbol] ? schema.options.ref.call(this, this) : schema.options.ref; + + const ref = refOpt?.modelName || refOpt; + if (ref != null && (ref === model.modelName || ref === model.baseModelName)) { + return true; + } + + // Check refPath + let refPath = schema.options.refPath; + if (refPath == null) { + return false; + } + + if (typeof refPath === 'function' && !refPath[modelSymbol]) { + let fullPath = path; + const fullPathWithIndexes = this.$__fullPathWithIndexes?.(); + if (fullPathWithIndexes?.length) { + fullPath = fullPathWithIndexes + '.' + path; + } + refPath = refPath.call(this, this, fullPath); + } + + if (typeof refPath !== 'string') { + throw new MongooseError('`refPath` must be a string or a function that returns a string, got ' + inspect(refPath)); + } + + const modelName = this.ownerDocument().get(refPath); + return modelName === model.modelName || modelName === model.baseModelName; + })(); + + let didPopulate = false; + if (refMatches && val instanceof Document && (!val.$__.wasPopulated || utils.deepEqual(val.$__.wasPopulated.value, val._doc._id))) { + const unpopulatedValue = schema?.$isSingleNested ? schema.cast(val, this) : val._doc._id; + this.$populated(path, unpopulatedValue, { [populateModelSymbol]: val.constructor }); + val.$__.wasPopulated = { value: unpopulatedValue }; + didPopulate = true; + } + + let popOpts; + const typeKey = this.$__schema.options.typeKey; + if (schema.options && + Array.isArray(schema.options[typeKey]) && + schema.options[typeKey].length && + schema.options[typeKey][0] && + schema.options[typeKey][0].ref && + _isManuallyPopulatedArray(val, schema.options[typeKey][0].ref)) { + popOpts = { [populateModelSymbol]: val[0].constructor }; + this.$populated(path, val.map(function(v) { return v._doc._id; }), popOpts); + + for (const doc of val) { + doc.$__.wasPopulated = { value: doc._doc._id }; + } + didPopulate = true; + } + + if (!refMatches || !schema.$isSingleNested || !val.$__) { + // If this path is underneath a single nested schema, we'll call the setter + // later in `$__set()` because we don't take `_doc` when we iterate through + // a single nested doc. That's to make sure we get the correct context. + // Otherwise we would double-call the setter, see gh-7196. + let setterContext = this; + if (this.$__schema.singleNestedPaths[path] != null && parts.length > 1) { + setterContext = getDeepestSubdocumentForPath(this, parts, this.schema); + } + if (options?.overwriteImmutable) { + val = schema.applySetters(val, setterContext, false, priorVal, { path, overwriteImmutable: true }); + } else { + val = schema.applySetters(val, setterContext, false, priorVal, { path }); + } + } + + if (Array.isArray(val) && + !Array.isArray(schema) && + schema.$isMongooseDocumentArray && + val.length !== 0 && + val[0]?.$__?.populated != null) { + const populatedPaths = Object.keys(val[0].$__.populated); + for (const populatedPath of populatedPaths) { + this.$populated(path + '.' + populatedPath, + val.map(v => v.$populated(populatedPath)), + val[0].$__.populated[populatedPath].options); + } + didPopulate = true; + } + + if (!didPopulate && this.$__.populated) { + // If this array partially contains populated documents, convert them + // all to ObjectIds re: #8443 + if (Array.isArray(val) && this.$__.populated[path]) { + for (let i = 0; i < val.length; ++i) { + if (val[i] instanceof Document) { + val.set(i, val[i]._doc._id, true); + } + } + } + delete this.$__.populated[path]; + } + + if (val != null && schema.$isSingleNested) { + _checkImmutableSubpaths(val, schema, priorVal); + } + + this.$markValid(path); + } catch (e) { + if (e instanceof MongooseError.StrictModeError && e.isImmutableError) { + this.invalidate(path, e); + } else if (e instanceof MongooseError.CastError) { + this.invalidate(e.path, e); + if (e.$originalErrorPath) { + this.invalidate(path, + new MongooseError.CastError(schema.instance, val, path, e.$originalErrorPath)); + } + } else { + this.invalidate(path, + new MongooseError.CastError(schema.instance, val, path, e)); + } + shouldSet = false; + } + + if (shouldSet) { + let savedState = null; + let savedStatePath = null; + if (!constructing) { + const doc = this.$isSubdocument ? this.ownerDocument() : this; + savedState = doc.$__.savedState; + savedStatePath = this.$isSubdocument ? this.$__.fullPath + '.' + path : path; + doc.$__saveInitialState(savedStatePath); + } + + this.$__set(pathToMark, path, options, constructing, parts, schema, val, priorVal); + + const isInTransaction = !!this.$__.session?.transaction; + const isModifiedWithinTransaction = this.$__.session && + this.$__.session[sessionNewDocuments] && + this.$__.session[sessionNewDocuments].has(this) && + this.$__.session[sessionNewDocuments].get(this).modifiedPaths && + !this.$__.session[sessionNewDocuments].get(this).modifiedPaths.has(savedStatePath); + if (savedState != null && + Object.hasOwn(savedState, savedStatePath) && + (!isInTransaction || isModifiedWithinTransaction) && + utils.deepEqual(val, savedState[savedStatePath])) { + this.unmarkModified(path); + } + } + + if (schema.$isSingleNested && (this.isDirectModified(path) || val == null)) { + cleanModifiedSubpaths(this, path); + } else if (schema.$isSchemaMap && val == null) { + cleanModifiedSubpaths(this, path); + } + + return this; +}; + +/*! + * ignore + */ + +function _isManuallyPopulatedArray(val, ref) { + if (!Array.isArray(val)) { + return false; + } + if (val.length === 0) { + return false; + } + + for (const el of val) { + if (!(el instanceof Document)) { + return false; + } + const modelName = el.constructor.modelName; + if (modelName == null) { + return false; + } + if (el.constructor.modelName != ref && el.constructor.baseModelName != ref) { + return false; + } + } + + return true; +} + +/** + * Sets the value of a path, or many paths. + * Alias for [`.$set`](https://mongoosejs.com/docs/api/document.html#Document.prototype.$set()). + * + * #### Example: + * + * // path, value + * doc.set(path, value) + * + * // object + * doc.set({ + * path : value + * , path2 : { + * path : value + * } + * }) + * + * // on-the-fly cast to number + * doc.set(path, value, Number) + * + * // on-the-fly cast to string + * doc.set(path, value, String) + * + * // changing strict mode behavior + * doc.set(path, value, { strict: false }); + * + * @param {string|object} path path or object of key/vals to set + * @param {any} val the value to set + * @param {Schema|string|number|Buffer|any} [type] optionally specify a type for "on-the-fly" attributes + * @param {object} [options] optionally specify options that modify the behavior of the set + * @return {Document} this + * @api public + * @method set + * @memberOf Document + * @instance + */ + +Document.prototype.set = Document.prototype.$set; + +/** + * Determine if we should mark this change as modified. + * + * @param {never} pathToMark UNUSED + * @param {string|symbol} path + * @param {object} options + * @param {any} constructing + * @param {never} parts UNUSED + * @param {Schema} schema + * @param {any} val + * @param {any} priorVal + * @return {boolean} + * @api private + * @method $__shouldModify + * @memberOf Document + * @instance + */ + +Document.prototype.$__shouldModify = function(pathToMark, path, options, constructing, parts, schema, val, priorVal) { + if (options?._skipMarkModified) { + return false; + } + if (this.$isNew) { + return true; + } + // Is path already modified? If so, always modify. We may unmark modified later. + if (path in this.$__.activePaths.getStatePaths('modify')) { + return true; + } + + if (val === void 0 && !this.$__isSelected(path)) { + // when a path is not selected in a query, its initial + // value will be undefined. + return true; + } + + if (val === void 0 && path in this.$__.activePaths.getStatePaths('default')) { + // we're just unsetting the default value which was never saved + return false; + } + + // gh-3992: if setting a populated field to a doc, don't mark modified + // if they have the same _id + if (this.$populated(path) && + val instanceof Document && + deepEqual(val._doc._id, priorVal)) { + return false; + } + + if (!deepEqual(val, priorVal !== undefined ? priorVal : utils.getValue(path, this))) { + return true; + } + + if (!constructing && + val !== null && + val !== undefined && + path in this.$__.activePaths.getStatePaths('default') && + deepEqual(val, schema.getDefault(this, constructing))) { + // a path with a default was $unset on the server + // and the user is setting it to the same value again + return true; + } + return false; +}; + +/** + * Handles the actual setting of the value and marking the path modified if appropriate. + * + * @param {string} pathToMark + * @param {string|symbol} path + * @param {object} options + * @param {any} constructing + * @param {Array} parts + * @param {Schema} schema + * @param {any} val + * @param {any} priorVal + * @api private + * @method $__set + * @memberOf Document + * @instance + */ + +Document.prototype.$__set = function(pathToMark, path, options, constructing, parts, schema, val, priorVal) { + Embedded = Embedded || require('./types/arraySubdocument'); + + const shouldModify = this.$isNew && !options?._skipMarkModified ? + true : + this.$__shouldModify(pathToMark, path, options, constructing, parts, schema, val, priorVal); + + if (shouldModify) { + if (this.$__.primitiveAtomics?.[path]) { + delete this.$__.primitiveAtomics[path]; + if (utils.hasOwnKeys(this.$__.primitiveAtomics) === false) { + delete this.$__.primitiveAtomics; + } + } + this.markModified(pathToMark); + + // handle directly setting arrays (gh-1126) + if (val && utils.isMongooseArray(val)) { + val._registerAtomic('$set', val); + + // Update embedded document parent references (gh-5189) + if (utils.isMongooseDocumentArray(val)) { + val.forEach(function(item) { + item && item.__parentArray && (item.__parentArray = val); + }); + } + } + } else if (Array.isArray(val) && Array.isArray(priorVal) && utils.isMongooseArray(val) && utils.isMongooseArray(priorVal)) { + val[arrayAtomicsSymbol] = priorVal[arrayAtomicsSymbol]; + val[arrayAtomicsBackupSymbol] = priorVal[arrayAtomicsBackupSymbol]; + if (utils.isMongooseDocumentArray(val)) { + val.forEach(doc => { + if (doc != null) { + doc.$isNew = false; + } + }); + } + } + + let obj = this._doc; + let i = 0; + const l = parts.length; + let cur = ''; + + for (; i < l; i++) { + const next = i + 1; + const last = next === l; + cur += (cur ? '.' + parts[i] : parts[i]); + if (specialProperties.has(parts[i])) { + continue; + } + + if (last) { + if (obj instanceof Map) { + obj.set(parts[i], val); + } else if (obj.$isSingleNested) { + if (!(parts[i] in obj)) { + obj[parts[i]] = val; + obj._doc[parts[i]] = val; + } else { + obj._doc[parts[i]] = val; + } + if (shouldModify) { + obj.markModified(parts[i]); + } + } else { + obj[parts[i]] = val; + } + } else { + const isMap = obj instanceof Map; + let value = isMap ? obj.get(parts[i]) : obj[parts[i]]; + if (utils.isPOJO(value)) { + obj = value; + } else if (value && value instanceof Embedded) { + obj = value; + } else if (value && !Array.isArray(value) && value.$isSingleNested) { + obj = value; + } else if (value && Array.isArray(value)) { + obj = value; + } else if (value == null) { + value = {}; + if (isMap) { + obj.set(parts[i], value); + } else { + obj[parts[i]] = value; + } + obj = value; + } else { + obj = value; + } + } + } +}; + +/** + * Gets a raw value from a path (no getters) + * + * @param {string} path + * @return {any} Returns the value from the given `path`. + * @api private + */ + +Document.prototype.$__getValue = function(path) { + if (typeof path !== 'string' && !Array.isArray(path)) { + throw new TypeError( + `Invalid \`path\`. Must be either string or array. Got "${path}" (type ${typeof path})` + ); + } + return utils.getValue(path, this._doc); +}; + +/** + * Increments the numeric value at `path` by the given `val`. + * When you call `save()` on this document, Mongoose will send a + * [`$inc`](https://www.mongodb.com/docs/manual/reference/operator/update/inc/) + * as opposed to a `$set`. + * + * #### Example: + * + * const schema = new Schema({ counter: Number }); + * const Test = db.model('Test', schema); + * + * const doc = await Test.create({ counter: 0 }); + * doc.$inc('counter', 2); + * await doc.save(); // Sends a `{ $inc: { counter: 2 } }` to MongoDB + * doc.counter; // 2 + * + * doc.counter += 2; + * await doc.save(); // Sends a `{ $set: { counter: 2 } }` to MongoDB + * + * @param {string|Array} path path or paths to update + * @param {number} val increment `path` by this value + * @return {Document} this + */ + +Document.prototype.$inc = function $inc(path, val) { + if (val == null) { + val = 1; + } + + if (Array.isArray(path)) { + path.forEach((p) => this.$inc(p, val)); + return this; + } + + const schemaType = this.$__path(path); + if (schemaType == null) { + if (this.$__.strictMode === 'throw') { + throw new StrictModeError(path); + } else if (this.$__.strictMode === true) { + return this; + } + } else if (schemaType.instance !== 'Number') { + this.invalidate(path, new MongooseError.CastError(schemaType.instance, val, path)); + return this; + } + + const currentValue = this.$__getValue(path) || 0; + let shouldSet = false; + let valToSet = null; + let valToInc = val; + + try { + val = schemaType.cast(val); + valToSet = schemaType.applySetters(currentValue + val, this); + valToInc = valToSet - currentValue; + shouldSet = true; + } catch (err) { + this.invalidate(path, new MongooseError.CastError('number', val, path, err)); + } + + if (shouldSet) { + this.$__.primitiveAtomics = this.$__.primitiveAtomics || {}; + if (this.$__.primitiveAtomics[path] == null) { + this.$__.primitiveAtomics[path] = { $inc: valToInc }; + } else { + this.$__.primitiveAtomics[path].$inc += valToInc; + } + this.markModified(path); + this.$__setValue(path, valToSet); + } + + return this; +}; + +/** + * Sets a raw value for a path (no casting, setters, transformations) + * + * @param {string} path + * @param {object} value + * @return {Document} this + * @api private + */ + +Document.prototype.$__setValue = function(path, val) { + utils.setValue(path, val, this._doc); + return this; +}; + +/** + * Returns the value of a path. + * + * #### Example: + * + * // path + * doc.get('age') // 47 + * + * // dynamic casting to a string + * doc.get('age', String) // "47" + * + * @param {string} path + * @param {Schema|string|number|Buffer|any} [type] optionally specify a type for on-the-fly attributes + * @param {object} [options] + * @param {boolean} [options.virtuals=false] Apply virtuals before getting this path + * @param {boolean} [options.getters=true] If false, skip applying getters and just get the raw value + * @return {any} + * @api public + */ + +Document.prototype.get = function(path, type, options) { + let adhoc; + if (options == null) { + options = {}; + } + if (type) { + adhoc = this.$__schema.interpretAsType(path, type, this.$__schema.options); + } + const noDottedPath = options.noDottedPath; + + // Fast path if we know we're just accessing top-level path on the document: + // just get the schema path, avoid `$__path()` because that does string manipulation + let schema = noDottedPath ? this.$__schema.paths[path] : this.$__path(path); + if (schema == null) { + schema = this.$__schema.virtualpath(path); + + if (schema != null) { + return schema.applyGetters(void 0, this); + } + } + + if (noDottedPath) { + let obj = this._doc[path]; + if (adhoc) { + obj = adhoc.cast(obj); + } + if (schema != null && options.getters !== false) { + return schema.applyGetters(obj, this); + } + return obj; + } + + if (schema?.instance === 'Mixed') { + const virtual = this.$__schema.virtualpath(path); + if (virtual != null) { + schema = virtual; + } + } + + const hasDot = path.indexOf('.') !== -1; + let obj = this._doc; + + const pieces = hasDot ? path.split('.') : [path]; + // Might need to change path for top-level alias + if (typeof this.$__schema.aliases[pieces[0]] === 'string') { + pieces[0] = this.$__schema.aliases[pieces[0]]; + } + + for (let i = 0, l = pieces.length; i < l; i++) { + if (obj?._doc) { + obj = obj._doc; + } + + if (obj == null) { + obj = void 0; + } else if (obj instanceof Map) { + obj = obj.get(pieces[i], { getters: false }); + } else if (i === l - 1) { + obj = utils.getValue(pieces[i], obj); + } else { + obj = obj[pieces[i]]; + } + } + + if (adhoc) { + obj = adhoc.cast(obj); + } + + if (schema != null && options.getters !== false) { + obj = schema.applyGetters(obj, this); + } else if (this.$__schema.nested[path] && options.virtuals) { + // Might need to apply virtuals if this is a nested path + return applyVirtuals(this, clone(obj) || {}, { path: path }); + } + + return obj; +}; + +/*! + * ignore + */ + +Document.prototype[getSymbol] = Document.prototype.get; +Document.prototype.$get = Document.prototype.get; + +/** + * Returns the schematype for the given `path`. + * + * @param {string} path + * @return {SchemaPath} + * @api private + * @method $__path + * @memberOf Document + * @instance + */ + +Document.prototype.$__path = function(path) { + const adhocs = this.$__.adhocPaths; + const adhocType = adhocs && Object.hasOwn(adhocs, path) ? adhocs[path] : null; + + if (adhocType) { + return adhocType; + } + return this.$__schema.path(path); +}; + +/** + * Marks the path as having pending changes to write to the db. + * + * _Very helpful when using [Mixed](https://mongoosejs.com/docs/schematypes.html#mixed) types._ + * + * #### Example: + * + * doc.mixed.type = 'changed'; + * doc.markModified('mixed.type'); + * doc.save() // changes to mixed.type are now persisted + * + * @param {string} path the path to mark modified + * @param {Document} [scope] the scope to run validators with + * @api public + */ + +Document.prototype.markModified = function(path, scope) { + this.$__saveInitialState(path); + + this.$__.activePaths.modify(path); + if (scope != null && !this.$isSubdocument) { + this.$__.pathsToScopes = this.$__pathsToScopes || {}; + this.$__.pathsToScopes[path] = scope; + } +}; + +/*! + * ignore + */ + +Document.prototype.$__saveInitialState = function $__saveInitialState(path) { + const savedState = this.$__.savedState; + const savedStatePath = path; + if (savedState != null) { + const firstDot = savedStatePath.indexOf('.'); + const topLevelPath = firstDot === -1 ? savedStatePath : savedStatePath.slice(0, firstDot); + if (!Object.hasOwn(savedState, topLevelPath)) { + savedState[topLevelPath] = clone(this.$__getValue(topLevelPath)); + } + } +}; + +/** + * Clears the modified state on the specified path. + * + * #### Example: + * + * doc.foo = 'bar'; + * doc.unmarkModified('foo'); + * doc.save(); // changes to foo will not be persisted + * + * @param {string} path the path to unmark modified + * @api public + */ + +Document.prototype.unmarkModified = function(path) { + this.$__.activePaths.init(path); + if (this.$__.pathsToScopes != null) { + delete this.$__.pathsToScopes[path]; + } +}; + +/** + * Don't run validation on this path or persist changes to this path. + * + * #### Example: + * + * doc.foo = null; + * doc.$ignore('foo'); + * doc.save(); // changes to foo will not be persisted and validators won't be run + * + * @memberOf Document + * @instance + * @method $ignore + * @param {string} path the path to ignore + * @api public + */ + +Document.prototype.$ignore = function(path) { + this.$__.activePaths.ignore(path); +}; + +/** + * Returns the list of paths that have been directly modified. A direct + * modified path is a path that you explicitly set, whether via `doc.foo = 'bar'`, + * `Object.assign(doc, { foo: 'bar' })`, or `doc.set('foo', 'bar')`. + * + * A path `a` may be in `modifiedPaths()` but not in `directModifiedPaths()` + * because a child of `a` was directly modified. + * + * #### Example: + * + * const schema = new Schema({ foo: String, nested: { bar: String } }); + * const Model = mongoose.model('Test', schema); + * await Model.create({ foo: 'original', nested: { bar: 'original' } }); + * + * const doc = await Model.findOne(); + * doc.nested.bar = 'modified'; + * doc.directModifiedPaths(); // ['nested.bar'] + * doc.modifiedPaths(); // ['nested', 'nested.bar'] + * + * @return {string[]} + * @api public + */ + +Document.prototype.directModifiedPaths = function() { + return Object.keys(this.$__.activePaths.getStatePaths('modify')); +}; + +/** + * Returns true if the given path is nullish or only contains empty objects. + * Useful for determining whether this subdoc will get stripped out by the + * [minimize option](https://mongoosejs.com/docs/guide.html#minimize). + * + * #### Example: + * + * const schema = new Schema({ nested: { foo: String } }); + * const Model = mongoose.model('Test', schema); + * const doc = new Model({}); + * doc.$isEmpty('nested'); // true + * doc.nested.$isEmpty(); // true + * + * doc.nested.foo = 'bar'; + * doc.$isEmpty('nested'); // false + * doc.nested.$isEmpty(); // false + * + * @param {string} [path] + * @memberOf Document + * @instance + * @api public + * @method $isEmpty + * @return {boolean} + */ + +Document.prototype.$isEmpty = function(path) { + const isEmptyOptions = { + minimize: true, + virtuals: false, + getters: false, + transform: false + }; + + if (arguments.length !== 0) { + const v = this.$get(path); + if (v == null) { + return true; + } + if (typeof v !== 'object') { + return false; + } + if (utils.isPOJO(v)) { + return _isEmpty(v); + } + return Object.keys(v.toObject(isEmptyOptions)).length === 0; + } + + return Object.keys(this.toObject(isEmptyOptions)).length === 0; +}; + +/*! + * ignore + */ + +function _isEmpty(v) { + if (v == null) { + return true; + } + if (typeof v !== 'object' || Array.isArray(v)) { + return false; + } + for (const key of Object.keys(v)) { + if (!_isEmpty(v[key])) { + return false; + } + } + return true; +} + +/** + * Returns the list of paths that have been modified. + * + * @param {object} [options] + * @param {boolean} [options.includeChildren=false] if true, returns children of modified paths as well. For example, if false, the list of modified paths for `doc.colors = { primary: 'blue' };` will **not** contain `colors.primary`. If true, `modifiedPaths()` will return an array that contains `colors.primary`. + * @return {string[]} + * @api public + */ + +Document.prototype.modifiedPaths = function(options) { + options = options || {}; + + const directModifiedPaths = Object.keys(this.$__.activePaths.getStatePaths('modify')); + const result = new Set(); + + let i = 0; + let j = 0; + const len = directModifiedPaths.length; + + for (i = 0; i < len; ++i) { + const path = directModifiedPaths[i]; + const parts = parentPaths(path); + const pLen = parts.length; + + for (j = 0; j < pLen; ++j) { + result.add(parts[j]); + } + + if (!options.includeChildren) { + continue; + } + + let ii = 0; + let cur = this.$get(path); + if (typeof cur === 'object' && cur !== null) { + if (cur._doc) { + cur = cur._doc; + } + const len = cur.length; + if (Array.isArray(cur)) { + for (ii = 0; ii < len; ++ii) { + const subPath = path + '.' + ii; + if (!result.has(subPath)) { + result.add(subPath); + if (cur[ii] != null && cur[ii].$__) { + const modified = cur[ii].modifiedPaths(); + let iii = 0; + const iiiLen = modified.length; + for (iii = 0; iii < iiiLen; ++iii) { + result.add(subPath + '.' + modified[iii]); + } + } + } + } + } else { + const keys = Object.keys(cur); + let ii = 0; + const len = keys.length; + for (ii = 0; ii < len; ++ii) { + result.add(path + '.' + keys[ii]); + } + } + } + } + return Array.from(result); +}; + +Document.prototype[documentModifiedPaths] = Document.prototype.modifiedPaths; + +/** + * Returns true if any of the given paths is modified, else false. If no arguments, returns `true` if any path + * in this document is modified. + * + * If `path` is given, checks if a path or any full path containing `path` as part of its path chain has been modified. + * + * #### Example: + * + * doc.set('documents.0.title', 'changed'); + * doc.isModified() // true + * doc.isModified('documents') // true + * doc.isModified('documents.0.title') // true + * doc.isModified('documents otherProp') // true + * doc.isDirectModified('documents') // false + * + * @param {string} [path] optional + * @param {object} [options] + * @param {boolean} [options.ignoreAtomics=false] If true, doesn't return true if path is underneath an array that was modified with atomic operations like `push()` + * @return {boolean} + * @api public + */ + +Document.prototype.isModified = function(paths, options, modifiedPaths) { + if (paths) { + const ignoreAtomics = options?.ignoreAtomics; + const directModifiedPathsObj = this.$__.activePaths.states.modify; + if (directModifiedPathsObj == null) { + return false; + } + + if (typeof paths === 'string') { + paths = paths.indexOf(' ') === -1 ? [paths] : paths.split(' '); + } + + for (const path of paths) { + if (directModifiedPathsObj[path] != null) { + return true; + } + } + + const modified = modifiedPaths || this[documentModifiedPaths](); + const isModifiedChild = paths.some(function(path) { + return !!~modified.indexOf(path); + }); + + let directModifiedPaths = Object.keys(directModifiedPathsObj); + if (ignoreAtomics) { + directModifiedPaths = directModifiedPaths.filter(path => { + const value = this.$__getValue(path); + if (value?.[arrayAtomicsSymbol] != null && value[arrayAtomicsSymbol].$set === undefined) { + return false; + } + return true; + }); + } + return isModifiedChild || paths.some(function(path) { + return directModifiedPaths.some(function(mod) { + return mod === path || path.startsWith(mod + '.'); + }); + }); + } + + return this.$__.activePaths.some('modify'); +}; + +/** + * Alias of [`.isModified`](https://mongoosejs.com/docs/api/document.html#Document.prototype.isModified()) + * + * @method $isModified + * @memberOf Document + * @api public + */ + +Document.prototype.$isModified = Document.prototype.isModified; + +Document.prototype[documentIsModified] = Document.prototype.isModified; + +/** + * Checks if a path is set to its default. + * + * #### Example: + * + * MyModel = mongoose.model('test', { name: { type: String, default: 'Val '} }); + * const m = new MyModel(); + * m.$isDefault('name'); // true + * + * @memberOf Document + * @instance + * @method $isDefault + * @param {string} [path] + * @return {boolean} + * @api public + */ + +Document.prototype.$isDefault = function(path) { + if (path == null) { + return this.$__.activePaths.some('default'); + } + + if (typeof path === 'string' && path.indexOf(' ') === -1) { + return Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path); + } + + let paths = path; + if (!Array.isArray(paths)) { + paths = paths.split(' '); + } + + return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('default'), path)); +}; + +/** + * Getter/setter, determines whether the document was deleted. The `Model.prototype.deleteOne()` method sets `$isDeleted` if the delete operation succeeded. + * + * #### Example: + * + * const product = await product.deleteOne(); + * product.$isDeleted(); // true + * product.deleteOne(); // no-op, doesn't send anything to the db + * + * product.$isDeleted(false); + * product.$isDeleted(); // false + * product.deleteOne(); // will execute a remove against the db + * + * + * @param {boolean} [val] optional, overrides whether mongoose thinks the doc is deleted + * @return {boolean|Document} whether mongoose thinks this doc is deleted. + * @method $isDeleted + * @memberOf Document + * @instance + * @api public + */ + +Document.prototype.$isDeleted = function(val) { + if (arguments.length === 0) { + return !!this.$__.isDeleted; + } + + this.$__.isDeleted = !!val; + return this; +}; + +/** + * Returns true if `path` was directly set and modified, else false. + * + * #### Example: + * + * doc.set('documents.0.title', 'changed'); + * doc.isDirectModified('documents.0.title') // true + * doc.isDirectModified('documents') // false + * + * @param {string|string[]} [path] + * @return {boolean} + * @api public + */ + +Document.prototype.isDirectModified = function(path) { + if (path == null) { + return this.$__.activePaths.some('modify'); + } + + if (typeof path === 'string' && path.indexOf(' ') === -1) { + const res = Object.hasOwn(this.$__.activePaths.getStatePaths('modify'), path); + if (res || path.indexOf('.') === -1) { + return res; + } + + const pieces = path.split('.'); + for (let i = 0; i < pieces.length - 1; ++i) { + const subpath = pieces.slice(0, i + 1).join('.'); + const subdoc = this.$get(subpath); + if (subdoc?.$__ != null && subdoc.isDirectModified(pieces.slice(i + 1).join('.'))) { + return true; + } + } + + return false; + } + + let paths = path; + if (typeof paths === 'string') { + paths = paths.split(' '); + } + + return paths.some(path => this.isDirectModified(path)); +}; + +/** + * Checks if `path` is in the `init` state, that is, it was set by `Document#init()` and not modified since. + * + * @param {string} [path] + * @return {boolean} + * @api public + */ + +Document.prototype.isInit = function(path) { + if (path == null) { + return this.$__.activePaths.some('init'); + } + + if (typeof path === 'string' && path.indexOf(' ') === -1) { + return Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path); + } + + let paths = path; + if (!Array.isArray(paths)) { + paths = paths.split(' '); + } + + return paths.some(path => Object.hasOwn(this.$__.activePaths.getStatePaths('init'), path)); +}; + +/** + * Checks if `path` was selected in the source query which initialized this document. + * + * #### Example: + * + * const doc = await Thing.findOne().select('name'); + * doc.isSelected('name') // true + * doc.isSelected('age') // false + * + * @param {string|string[]} path + * @return {boolean} + * @api public + */ + +Document.prototype.isSelected = function isSelected(path) { + if (this.$__.selected == null) { + return true; + } + if (!path) { + return false; + } + if (path === '_id') { + return this.$__.selected._id !== 0; + } + + if (path.indexOf(' ') !== -1) { + path = path.split(' '); + } + if (Array.isArray(path)) { + return path.some(p => this.$__isSelected(p)); + } + + const paths = Object.keys(this.$__.selected); + let inclusive = null; + + if (paths.length === 1 && paths[0] === '_id') { + // only _id was selected. + return this.$__.selected._id === 0; + } + + for (const cur of paths) { + if (cur === '_id') { + continue; + } + if (!isDefiningProjection(this.$__.selected[cur])) { + continue; + } + inclusive = !!this.$__.selected[cur]; + break; + } + + if (inclusive === null) { + return true; + } + + if (path in this.$__.selected) { + return inclusive; + } + + const pathDot = path + '.'; + + for (const cur of paths) { + if (cur === '_id') { + continue; + } + + if (cur.startsWith(pathDot)) { + return inclusive || cur !== pathDot; + } + + if (pathDot.startsWith(cur + '.')) { + return inclusive; + } + } + return !inclusive; +}; + +Document.prototype.$__isSelected = Document.prototype.isSelected; + +/** + * Checks if `path` was explicitly selected. If no projection, always returns + * true. + * + * #### Example: + * + * Thing.findOne().select('nested.name').exec(function (err, doc) { + * doc.isDirectSelected('nested.name') // true + * doc.isDirectSelected('nested.otherName') // false + * doc.isDirectSelected('nested') // false + * }) + * + * @param {string} path + * @return {boolean} + * @api public + */ + +Document.prototype.isDirectSelected = function isDirectSelected(path) { + if (this.$__.selected == null) { + return true; + } + + if (path === '_id') { + return this.$__.selected._id !== 0; + } + + if (path.indexOf(' ') !== -1) { + path = path.split(' '); + } + if (Array.isArray(path)) { + return path.some(p => this.isDirectSelected(p)); + } + + const paths = Object.keys(this.$__.selected); + let inclusive = null; + + if (paths.length === 1 && paths[0] === '_id') { + // only _id was selected. + return this.$__.selected._id === 0; + } + + for (const cur of paths) { + if (cur === '_id') { + continue; + } + if (!isDefiningProjection(this.$__.selected[cur])) { + continue; + } + inclusive = !!this.$__.selected[cur]; + break; + } + + if (inclusive === null) { + return true; + } + + if (Object.hasOwn(this.$__.selected, path)) { + return inclusive; + } + + return !inclusive; +}; + +/** + * Executes registered validation rules for this document. + * + * #### Note: + * + * This method is called `pre` save and if a validation rule is violated, [save](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) is aborted and the error is thrown. + * + * #### Example: + * + * await doc.validate({ validateModifiedOnly: false, pathsToSkip: ['name', 'email']}); + * + * @param {Array|string} [pathsToValidate] list of paths to validate. If set, Mongoose will validate only the modified paths that are in the given list. + * @param {object} [options] internal options + * @param {boolean} [options.validateModifiedOnly=false] if `true` mongoose validates only modified paths. + * @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {Promise} Returns a Promise. + * @api public + */ + +Document.prototype.validate = async function validate(pathsToValidate, options) { + if (typeof pathsToValidate === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Document.prototype.validate() no longer accepts a callback'); + } + this.$op = 'validate'; + + if (arguments.length === 1) { + if (typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { + options = arguments[0]; + pathsToValidate = null; + } + } + if (options && typeof options.pathsToSkip === 'string') { + const isOnePathOnly = options.pathsToSkip.indexOf(' ') === -1; + options.pathsToSkip = isOnePathOnly ? [options.pathsToSkip] : options.pathsToSkip.split(' '); + } + const _skipParallelValidateCheck = options?._skipParallelValidateCheck; + + if (this.$isSubdocument != null) { + // Skip parallel validate check for subdocuments + } else if (this.$__.validating && !_skipParallelValidateCheck) { + throw new ParallelValidateError(this); + } else if (!_skipParallelValidateCheck) { + this.$__.validating = true; + } + + try { + try { + [options] = await this._execDocumentPreHooks('validate', options, [options]); + } catch (error) { + await this._execDocumentPostHooks('validate', options, error); + return; + } + + if (this.$__.saveOptions && this.$__.saveOptions.pathsToSave && !pathsToValidate) { + pathsToValidate = [...this.$__.saveOptions.pathsToSave]; + } + + const hasValidateModifiedOnlyOption = options && + (typeof options === 'object') && + ('validateModifiedOnly' in options); + + const pathsToSkip = options?.pathsToSkip || null; + + let shouldValidateModifiedOnly; + if (hasValidateModifiedOnlyOption) { + shouldValidateModifiedOnly = !!options.validateModifiedOnly; + } else { + shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; + } + this.$__.validateModifiedOnly = shouldValidateModifiedOnly; + + const validateAllPaths = options?.validateAllPaths; + if (validateAllPaths) { + if (pathsToSkip) { + throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); + } + if (pathsToValidate) { + throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); + } + if (hasValidateModifiedOnlyOption && shouldValidateModifiedOnly) { + throw new TypeError('Cannot set both `validateAllPaths` and `validateModifiedOnly`'); + } + } + + const _this = this; + + // only validate required fields when necessary + let paths; + let doValidateOptionsByPath; + if (validateAllPaths) { + paths = new Set(Object.keys(this.$__schema.paths)); + // gh-661: if a whole array is modified, make sure to run validation on all + // the children as well + for (const path of paths) { + const schemaType = this.$__schema.path(path); + if (!schemaType?.$isMongooseArray) { + continue; + } + const val = this.$__getValue(path); + if (!val) { + continue; + } + _pushNestedArrayPaths(val, paths, path); + } + paths = [...paths]; + doValidateOptionsByPath = {}; + } else { + const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip, options?._nestedValidate); + paths = shouldValidateModifiedOnly ? + pathDetails[0].filter((path) => this.$isModified(path)) : + pathDetails[0]; + doValidateOptionsByPath = pathDetails[1]; + } + + if (typeof pathsToValidate === 'string') { + pathsToValidate = pathsToValidate.split(' '); + } + + if (paths.length === 0) { + const error = _completeValidate(this); + + await this._execDocumentPostHooks('validate', options, error); + return; + } + + const validated = {}; + + const pathsToSave = Array.isArray(this.$__.saveOptions?.pathsToSave) ? + new Set(this.$__.saveOptions.pathsToSave) : + null; + const promises = []; + for (let i = 0; i < paths.length; ++i) { + const path = paths[i]; + if (pathsToSave != null && !pathsToSave.has(path)) { + promises.push(Promise.resolve(null)); + continue; + } + if (path == null || validated[path]) { + promises.push(Promise.resolve(null)); + continue; + } + + validated[path] = true; + const schemaType = _this.$__schema.path(path); + + if (!schemaType) { + promises.push(Promise.resolve(null)); + continue; + } + + // If user marked as invalid or there was a cast error, don't validate + if (!_this.$isValid(path)) { + promises.push(Promise.resolve(null)); + continue; + } + + // If setting a path under a mixed path, avoid using the mixed path validator (gh-10141) + if (schemaType[schemaMixedSymbol] != null && path !== schemaType.path) { + promises.push(Promise.resolve(null)); + continue; + } + + let val = _this.$__getValue(path); + + // If you `populate()` and get back a null value, required validators + // shouldn't fail (gh-8018). We should always fall back to the populated + // value. + let pop; + if ((pop = _this.$populated(path))) { + val = pop; + } else if (val?.$__?.wasPopulated) { + // Array paths, like `somearray.1`, do not show up as populated with `$populated()`, + // so in that case pull out the document's id + val = val._doc._id; + } + const scope = _this.$__.pathsToScopes != null && path in _this.$__.pathsToScopes ? + _this.$__.pathsToScopes[path] : + _this; + + const doValidateOptions = { + ...doValidateOptionsByPath[path], + path: path, + validateAllPaths, + _nestedValidate: true + }; + + promises.push( + schemaType.doValidate(val, scope, doValidateOptions).then( + () => null, + err => { + const isSubdoc = schemaType.$isSingleNested || + schemaType.$isArraySubdocument || + schemaType.$isMongooseDocumentArray; + if (isSubdoc && err instanceof ValidationError) { + return null; + } + + return err; + } + ) + ); + } + const validationErrors = await Promise.all(promises); + for (let i = 0; i < validationErrors.length; ++i) { + const validationError = validationErrors[i]; + if (validationError == null) { + continue; + } + if (this.$__.validationError == null) { + this.$__.validationError = new ValidationError(this); + } + _this.invalidate(paths[i], validationError, undefined, true); + } + + const error = _completeValidate(this); + + await this._execDocumentPostHooks('validate', options, error); + } finally { + delete this.$__.validateModifiedOnly; + this.$op = null; + this.$__.validating = null; + } +}; + +/** + * Alias of [`.validate`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) + * + * @method $validate + * @memberOf Document + * @api public + */ + +Document.prototype.$validate = Document.prototype.validate; + +/*! + * Event emitting and error persisting logic that needs to happen in both the `paths.length === 0` + * and `paths.length > 0` cases in `Document.prototype.validate()`. + * + * @param {Document} doc + * @return {Error} + * @private + */ + +function _completeValidate(doc) { + let error = doc.$__.validationError; + doc.$__.validationError = null; + doc.$__.validating = null; + + if (doc.$__.validateModifiedOnly && error != null) { + // Remove any validation errors that aren't from modified paths + const errors = Object.keys(error.errors); + for (const errPath of errors) { + if (!doc.$isModified(errPath)) { + delete error.errors[errPath]; + } + } + if (utils.hasOwnKeys(error.errors) === false) { + error = void 0; + } + } + + doc.$__.cachedRequired = {}; + doc.$emit('validate', doc); + doc.constructor.emit('validate', doc); + + if (error) { + for (const key in error.errors) { + // Make sure cast errors persist + if (!doc[documentArrayParent] && + error.errors[key] instanceof MongooseError.CastError) { + doc.invalidate(key, error.errors[key]); + } + } + } + + return error; +} + +function _evaluateRequiredFunctions(doc) { + const requiredFields = Object.keys(doc.$__.activePaths.getStatePaths('require')); + let i = 0; + const len = requiredFields.length; + for (i = 0; i < len; ++i) { + const path = requiredFields[i]; + + const p = doc.$__schema.path(path); + + if (typeof p?.originalRequiredValue === 'function') { + doc.$__.cachedRequired = doc.$__.cachedRequired || {}; + try { + doc.$__.cachedRequired[path] = p.originalRequiredValue.call(doc, doc); + } catch (err) { + doc.invalidate(path, err); + } + } + } +} + +/*! + * ignore + */ + +function _getPathsToValidate(doc, pathsToValidate, pathsToSkip, isNestedValidate) { + const doValidateOptions = {}; + + _evaluateRequiredFunctions(doc); + // only validate required fields when necessary + let paths = new Set(Object.keys(doc.$__.activePaths.getStatePaths('require')).filter(function(path) { + if (!doc.$__isSelected(path) && !doc.$isModified(path)) { + return false; + } + if (path.endsWith('.$*')) { + // Skip $* paths - they represent map schemas, not actual document paths + return false; + } + if (doc.$__.cachedRequired != null && path in doc.$__.cachedRequired) { + return doc.$__.cachedRequired[path]; + } + return true; + })); + + Object.keys(doc.$__.activePaths.getStatePaths('init')).forEach(addToPaths); + Object.keys(doc.$__.activePaths.getStatePaths('modify')).forEach(addToPaths); + Object.keys(doc.$__.activePaths.getStatePaths('default')).forEach(addToPaths); + function addToPaths(p) { + if (p.endsWith('.$*')) { + // Skip $* paths - they represent map schemas, not actual document paths + return; + } + paths.add(p); + } + + if (!isNestedValidate) { + // If we're validating a subdocument, all this logic will run anyway on the top-level document, so skip for subdocuments. + // But only run for top-level subdocuments, because we're looking for subdocuments that are not modified at top-level but + // have a modified path. If that is the case, we will run validation on the top-level subdocument, and via that run validation + // on any subdocuments down to the modified path. + const topLevelSubdocs = []; + for (const path of Object.keys(doc.$__schema.paths)) { + const schemaType = doc.$__schema.path(path); + if (schemaType.$isSingleNested) { + const subdoc = doc.$get(path); + if (subdoc) { + topLevelSubdocs.push(subdoc); + } + } else if (schemaType.$isMongooseDocumentArray) { + const arr = doc.$get(path); + if (arr?.length) { + for (const subdoc of arr) { + if (subdoc) { + topLevelSubdocs.push(subdoc); + } + } + } + } + } + const modifiedPaths = doc.modifiedPaths(); + for (const subdoc of topLevelSubdocs) { + if (subdoc.$basePath) { + const fullPathToSubdoc = subdoc.$__pathRelativeToParent(); + + // Remove child paths for now, because we'll be validating the whole + // subdoc. + // The following is a faster take on looping through every path in `paths` + // and checking if the path starts with `fullPathToSubdoc` re: gh-13191 + for (const modifiedPath of subdoc.modifiedPaths()) { + paths.delete(fullPathToSubdoc + '.' + modifiedPath); + } + + const subdocParent = subdoc.$parent(); + if (subdocParent == null) { + throw new Error('Cannot validate subdocument that does not have a parent'); + } + if (doc.$isModified(fullPathToSubdoc, null, modifiedPaths) && + // Avoid using isDirectModified() here because that does additional checks on whether the parent path + // is direct modified, which can cause performance issues re: gh-14897 + !Object.hasOwn(subdocParent.$__.activePaths.getStatePaths('modify'), fullPathToSubdoc) && + !subdocParent.$isDefault(fullPathToSubdoc)) { + paths.add(fullPathToSubdoc); + + if (doc.$__.pathsToScopes == null) { + doc.$__.pathsToScopes = {}; + } + doc.$__.pathsToScopes[fullPathToSubdoc] = subdoc.$isDocumentArrayElement ? + subdoc.__parentArray : + subdoc.$parent(); + + doValidateOptions[fullPathToSubdoc] = { skipSchemaValidators: true }; + if (subdoc.$isDocumentArrayElement && subdoc.__index != null) { + doValidateOptions[fullPathToSubdoc].index = subdoc.__index; + } + } + } + } + } + + for (const path of paths) { + const _pathType = doc.$__schema.path(path); + if (!_pathType) { + continue; + } + + if (_pathType.$isMongooseDocumentArray) { + for (const p of paths) { + if (p == null || p.startsWith(_pathType.path + '.')) { + paths.delete(p); + } + } + } + + // Optimization: if primitive path with no validators, or array of primitives + // with no validators, skip validating this path entirely. + if (!_pathType.schema && + !_pathType.embeddedSchemaType && + _pathType.validators.length === 0 && + !_pathType.$parentSchemaDocArray && + // gh-15957: skip this optimization for SchemaMap as maps can contain subdocuments + // that need validation even if the map itself has no validators + !_pathType.$isSchemaMap) { + paths.delete(path); + } else if (_pathType.$isMongooseArray && + !_pathType.$isMongooseDocumentArray && // Skip document arrays... + !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays + _pathType.validators.length === 0 && // and arrays with top-level validators + _pathType.embeddedSchemaType.validators.length === 0) { + paths.delete(path); + } + } + + + if (Array.isArray(pathsToValidate)) { + paths = _handlePathsToValidate(paths, pathsToValidate); + } else if (Array.isArray(pathsToSkip)) { + paths = _handlePathsToSkip(paths, pathsToSkip); + } + + // from here on we're not removing items from paths + + // gh-661: if a whole array is modified, make sure to run validation on all + // the children as well + _addArrayPathsToValidate(doc, paths); + + const flattenOptions = { skipArrays: true }; + for (const pathToCheck of paths) { + if (doc.$__schema.nested[pathToCheck]) { + let _v = doc.$__getValue(pathToCheck); + if (isMongooseObject(_v)) { + _v = _v.toObject({ transform: false }); + } + const flat = flatten(_v, pathToCheck, flattenOptions, doc.$__schema); + // Single nested paths (paths embedded under single nested subdocs) will + // be validated on their own when we call `validate()` on the subdoc itself. + // Re: gh-8468 + const singleNestedPaths = doc.$__schema.singleNestedPaths; + for (const path of Object.keys(flat)) { + if (!Object.hasOwn(singleNestedPaths, path)) { + addToPaths(path); + } + } + } + } + + for (const path of paths) { + const _pathType = doc.$__schema.path(path); + + if (!_pathType) { + continue; + } + + // If underneath a document array, may need to re-validate the parent + // array re: gh-6818. Do this _after_ adding subpaths, because + // we don't want to add every array subpath. + if (_pathType.$parentSchemaDocArray && typeof _pathType.$parentSchemaDocArray.path === 'string') { + paths.add(_pathType.$parentSchemaDocArray.path); + } + + if (!_pathType.$isSchemaMap) { + continue; + } + + const val = doc.$__getValue(path); + if (val == null) { + continue; + } + for (const key of val.keys()) { + paths.add(path + '.' + key); + } + } + + paths = Array.from(paths); + return [paths, doValidateOptions]; +} + +function _addArrayPathsToValidate(doc, paths) { + for (const path of paths) { + const _pathType = doc.$__schema.path(path); + if (!_pathType) { + continue; + } + + if (!_pathType.$isMongooseArray || + // To avoid potential performance issues, skip doc arrays whose children + // are not required. `getPositionalPathType()` may be slow, so avoid + // it unless we have a case of #6364 + (!Array.isArray(_pathType) && + _pathType.$isMongooseDocumentArray && + !_pathType?.schemaOptions?.required)) { + continue; + } + + // gh-11380: optimization. If the array isn't a document array and there's no validators + // on the array type, there's no need to run validation on the individual array elements. + if (_pathType.$isMongooseArray && + !_pathType.$isMongooseDocumentArray && // Skip document arrays... + !_pathType.embeddedSchemaType.$isMongooseArray && // and arrays of arrays + _pathType.embeddedSchemaType.validators.length === 0) { + continue; + } + + const val = doc.$__getValue(path); + _pushNestedArrayPaths(val, paths, path); + } +} + +function _pushNestedArrayPaths(val, paths, path) { + if (val != null) { + const numElements = val.length; + for (let j = 0; j < numElements; ++j) { + if (Array.isArray(val[j])) { + _pushNestedArrayPaths(val[j], paths, path + '.' + j); + } else { + paths.add(path + '.' + j); + } + } + } +} + +/*! + * ignore + */ + +Document.prototype._execDocumentPreHooks = async function _execDocumentPreHooks(opName, options, argsForHooks) { + const filter = buildMiddlewareFilter(options, 'pre'); + return this.$__middleware.execPre(opName, this, argsForHooks || [], { filter }); +}; + +/*! + * ignore + */ + +Document.prototype._execDocumentPostHooks = async function _execDocumentPostHooks(opName, options, error) { + const filter = buildMiddlewareFilter(options, 'post'); + return this.$__middleware.execPost(opName, this, [this], { error, filter }); +}; + +/*! + * ignore + */ + +function _handlePathsToValidate(paths, pathsToValidate) { + const _pathsToValidate = new Set(pathsToValidate); + const parentPaths = new Map([]); + for (const path of pathsToValidate) { + if (path.indexOf('.') === -1) { + continue; + } + const pieces = path.split('.'); + let cur = pieces[0]; + for (let i = 1; i < pieces.length; ++i) { + // Since we skip subpaths under single nested subdocs to + // avoid double validation, we need to add back the + // single nested subpath if the user asked for it (gh-8626) + parentPaths.set(cur, path); + cur = cur + '.' + pieces[i]; + } + } + + const ret = new Set(); + for (const path of paths) { + if (_pathsToValidate.has(path)) { + ret.add(path); + } else if (parentPaths.has(path)) { + ret.add(parentPaths.get(path)); + } + } + return ret; +} + +/*! + * ignore + */ + +function _handlePathsToSkip(paths, pathsToSkip) { + pathsToSkip = new Set(pathsToSkip); + paths = Array.from(paths).filter(p => !pathsToSkip.has(p)); + return new Set(paths); +} + +/** + * Executes registered validation rules (skipping asynchronous validators) for this document. + * + * #### Note: + * + * This method is useful if you need synchronous validation. + * + * #### Example: + * + * const err = doc.validateSync(); + * if (err) { + * handleError(err); + * } else { + * // validation passed + * } + * + * @param {Array|string} [pathsToValidate] only validate the given paths + * @param {object} [options] options for validation + * @param {boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths. + * @param {Array|string} [options.pathsToSkip] list of paths to skip. If set, Mongoose will validate every modified path that is not in this list. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {ValidationError|undefined} ValidationError if there are errors during validation, or undefined if there is no error. + * @api public + */ + +Document.prototype.validateSync = function(pathsToValidate, options) { + const _this = this; + + if (arguments.length === 1 && typeof arguments[0] === 'object' && !Array.isArray(arguments[0])) { + options = arguments[0]; + pathsToValidate = null; + } + + const hasValidateModifiedOnlyOption = options && + (typeof options === 'object') && + ('validateModifiedOnly' in options); + + let shouldValidateModifiedOnly; + if (hasValidateModifiedOnlyOption) { + shouldValidateModifiedOnly = !!options.validateModifiedOnly; + } else { + shouldValidateModifiedOnly = this.$__schema.options.validateModifiedOnly; + } + + let pathsToSkip = options?.pathsToSkip; + + const validateAllPaths = options?.validateAllPaths; + if (validateAllPaths) { + if (pathsToSkip) { + throw new TypeError('Cannot set both `validateAllPaths` and `pathsToSkip`'); + } + if (pathsToValidate) { + throw new TypeError('Cannot set both `validateAllPaths` and `pathsToValidate`'); + } + } + + if (typeof pathsToValidate === 'string') { + const isOnePathOnly = pathsToValidate.indexOf(' ') === -1; + pathsToValidate = isOnePathOnly ? [pathsToValidate] : pathsToValidate.split(' '); + } else if (typeof pathsToSkip === 'string' && pathsToSkip.indexOf(' ') !== -1) { + pathsToSkip = pathsToSkip.split(' '); + } + + // only validate required fields when necessary + let paths; + let skipSchemaValidators; + if (validateAllPaths) { + paths = new Set(Object.keys(this.$__schema.paths)); + // gh-661: if a whole array is modified, make sure to run validation on all + // the children as well + for (const path of paths) { + const schemaType = this.$__schema.path(path); + if (!schemaType?.$isMongooseArray) { + continue; + } + const val = this.$__getValue(path); + if (!val) { + continue; + } + _pushNestedArrayPaths(val, paths, path); + } + paths = [...paths]; + skipSchemaValidators = {}; + } else { + const pathDetails = _getPathsToValidate(this, pathsToValidate, pathsToSkip); + paths = shouldValidateModifiedOnly ? + pathDetails[0].filter((path) => this.$isModified(path)) : + pathDetails[0]; + skipSchemaValidators = pathDetails[1]; + } + + const validating = {}; + + for (let i = 0, len = paths.length; i < len; ++i) { + const path = paths[i]; + + if (validating[path]) { + continue; + } + + validating[path] = true; + + const p = _this.$__schema.path(path); + if (!p) { + continue; + } + if (!_this.$isValid(path)) { + continue; + } + + const val = _this.$__getValue(path); + const err = p.doValidateSync(val, _this, { + skipSchemaValidators: skipSchemaValidators[path], + path: path, + validateModifiedOnly: shouldValidateModifiedOnly, + validateAllPaths + }); + if (err) { + const isSubdoc = p.$isSingleNested || + p.$isArraySubdocument || + p.$isMongooseDocumentArray; + if (isSubdoc && err instanceof ValidationError) { + continue; + } + _this.invalidate(path, err, undefined, true); + } + } + + const err = _this.$__.validationError; + _this.$__.validationError = undefined; + _this.$emit('validate', _this); + _this.constructor.emit('validate', _this); + + if (err) { + for (const key in err.errors) { + // Make sure cast errors persist + if (err.errors[key] instanceof MongooseError.CastError) { + _this.invalidate(key, err.errors[key]); + } + } + } + + return err; +}; + +/** + * Marks a path as invalid, causing validation to fail. + * + * The `errorMsg` argument will become the message of the `ValidationError`. + * + * The `value` argument (if passed) will be available through the `ValidationError.value` property. + * + * doc.invalidate('size', 'must be less than 20', 14); + * + * doc.validate(function (err) { + * console.log(err) + * // prints + * { message: 'Validation failed', + * name: 'ValidationError', + * errors: + * { size: + * { message: 'must be less than 20', + * name: 'ValidatorError', + * path: 'size', + * type: 'user defined', + * value: 14 } } } + * }) + * + * @param {string} path the field to invalidate. For array elements, use the `array.i.field` syntax, where `i` is the 0-based index in the array. + * @param {string|Error} err the error which states the reason `path` was invalid + * @param {object|string|number|any} val optional invalid value + * @param {string} [kind] optional `kind` property for the error + * @return {ValidationError} the current ValidationError, with all currently invalidated paths + * @api public + */ + +Document.prototype.invalidate = function(path, err, val, kind) { + if (!this.$__.validationError) { + this.$__.validationError = new ValidationError(this); + } + + if (this.$__.validationError.errors[path]) { + return; + } + + if (!err || typeof err === 'string') { + err = new ValidatorError({ + path: path, + message: err, + type: kind || 'user defined', + value: val + }); + } + + if (this.$__.validationError === err) { + return this.$__.validationError; + } + + this.$__.validationError.addError(path, err); + return this.$__.validationError; +}; + +/** + * Marks a path as valid, removing existing validation errors. + * + * @param {string} path the field to mark as valid + * @api public + * @memberOf Document + * @instance + * @method $markValid + */ + +Document.prototype.$markValid = function(path) { + if (!this.$__.validationError?.errors[path]) { + return; + } + + delete this.$__.validationError.errors[path]; + if (utils.hasOwnKeys(this.$__.validationError.errors) === false) { + this.$__.validationError = null; + } +}; + +/*! + * ignore + */ + +function _markValidSubpaths(doc, path) { + if (!doc.$__.validationError) { + return; + } + + const keys = Object.keys(doc.$__.validationError.errors); + for (const key of keys) { + if (key.startsWith(path + '.')) { + delete doc.$__.validationError.errors[key]; + } + } + if (utils.hasOwnKeys(doc.$__.validationError.errors) === false) { + doc.$__.validationError = null; + } +} + +/*! + * ignore + */ + +function _checkImmutableSubpaths(subdoc, schematype, priorVal) { + const schema = schematype.schema; + if (schema == null) { + return; + } + + for (const key of Object.keys(schema.paths)) { + const path = schema.paths[key]; + if (path.$immutableSetter == null) { + continue; + } + const oldVal = priorVal == null ? void 0 : priorVal.$__getValue(key); + // Calling immutableSetter with `oldVal` even though it expects `newVal` + // is intentional. That's because `$immutableSetter` compares its param + // to the current value. + path.$immutableSetter.call(subdoc, oldVal); + } +} + +/** + * Saves this document by inserting a new document into the database if [document.isNew](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew()) is `true`, + * or sends an [updateOne](https://mongoosejs.com/docs/api/document.html#Document.prototype.updateOne()) operation **only** with the modifications to the database, it does not replace the whole document in the latter case. + * + * #### Example: + * + * product.sold = Date.now(); + * product = await product.save(); + * + * If save is successful, the returned promise will fulfill with the document + * saved. + * + * #### Example: + * + * const newProduct = await product.save(); + * newProduct === product; // true + * + * @param {object} [options] options optional options + * @param {Session} [options.session=null] the [session](https://www.mongodb.com/docs/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](https://mongoosejs.com/docs/api/document.html#Document.prototype.$session()). + * @param {object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com/docs/guide.html#safe). Use the `w` option instead. + * @param {boolean} [options.validateBeforeSave] set to false to save without validating. + * @param {boolean} [options.validateModifiedOnly=false] If `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths. + * @param {number|string} [options.w] set the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {number} [options.wtimeout] sets a [timeout for the write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern). + * @param {boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://www.mongodb.com/docs/manual/reference/limits/#Restrictions-on-Field-Names) + * @param {boolean} [options.timestamps=true] if `false` and [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this `save()`. + * @method save + * @memberOf Document + * @instance + * @throws {DocumentNotFoundError} if this [save updates an existing document](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew()) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating). + * @return {Promise} + * @api public + * @see middleware https://mongoosejs.com/docs/middleware.html + */ + +/** + * Checks if a path is invalid + * + * @param {string|string[]} [path] the field to check. If unset will always return "false" + * @method $isValid + * @memberOf Document + * @instance + * @api private + */ + +Document.prototype.$isValid = function(path) { + if (this.$__.validationError == null || utils.hasOwnKeys(this.$__.validationError.errors) === false) { + return true; + } + if (path == null) { + return false; + } + + if (path.indexOf(' ') !== -1) { + path = path.split(' '); + } + if (Array.isArray(path)) { + return path.some(p => this.$__.validationError.errors[p] == null); + } + + return this.$__.validationError.errors[path] == null; +}; + +/** + * Resets the internal modified state of this document. + * + * @api private + * @return {Document} this + * @method $__reset + * @memberOf Document + * @instance + */ + +Document.prototype.$__reset = function reset() { + let _this = this; + + // Skip for subdocuments + const subdocs = !this.$isSubdocument ? this.$getAllSubdocs({ useCache: true }) : null; + if (subdocs?.length > 0) { + for (const subdoc of subdocs) { + subdoc.$__reset(); + } + } + + // clear atomics + this.$__dirty().forEach(function(dirt) { + const type = dirt.value; + + if (type && typeof type.clearAtomics === 'function') { + type.clearAtomics(); + } else if (type && type[arrayAtomicsSymbol]) { + type[arrayAtomicsBackupSymbol] = type[arrayAtomicsSymbol]; + type[arrayAtomicsSymbol] = {}; + } + }); + + this.$__.backup = {}; + this.$__.backup.activePaths = { + modify: Object.assign({}, this.$__.activePaths.getStatePaths('modify')), + default: Object.assign({}, this.$__.activePaths.getStatePaths('default')) + }; + this.$__.backup.validationError = this.$__.validationError; + this.$__.backup.errors = this.$errors; + + // Clear 'dirty' cache + this.$__.activePaths.clear('modify'); + this.$__.activePaths.clear('default'); + this.$__.validationError = undefined; + this.$errors = undefined; + _this = this; + this.$__schema.requiredPaths().forEach(function(path) { + _this.$__.activePaths.require(path); + }); + + return this; +}; + +/*! + * ignore + */ + +Document.prototype.$__undoReset = function $__undoReset() { + if (this.$__.backup?.activePaths == null) { + return; + } + + this.$__.activePaths.states.modify = this.$__.backup.activePaths.modify; + this.$__.activePaths.states.default = this.$__.backup.activePaths.default; + + this.$__.validationError = this.$__.backup.validationError; + this.$errors = this.$__.backup.errors; + + for (const dirt of this.$__dirty()) { + const type = dirt.value; + + if (type?.[arrayAtomicsSymbol] && type[arrayAtomicsBackupSymbol]) { + type[arrayAtomicsSymbol] = type[arrayAtomicsBackupSymbol]; + } + } + + if (!this.$isSubdocument) { + for (const subdoc of this.$getAllSubdocs()) { + subdoc.$__undoReset(); + } + } +}; + +/** + * Returns this documents dirty paths / vals. + * + * @return {Array} + * @api private + * @method $__dirty + * @memberOf Document + * @instance + */ + +Document.prototype.$__dirty = function() { + const _this = this; + let all = this.$__.activePaths.map('modify', function(path) { + return { + path: path, + value: _this.$__getValue(path), + schema: _this.$__path(path) + }; + }); + + // gh-2558: if we had to set a default and the value is not undefined, + // we have to save as well + all = all.concat(this.$__.activePaths.map('default', function(path) { + if (path === '_id' || _this.$__getValue(path) == null) { + return; + } + return { + path: path, + value: _this.$__getValue(path), + schema: _this.$__path(path) + }; + })); + + const allPaths = new Map(all.filter((el) => el != null).map((el) => [el.path, el.value])); + // Ignore "foo.a" if "foo" is dirty already. + const minimal = []; + + all.forEach(function(item) { + if (!item) { + return; + } + + let top = null; + + const array = parentPaths(item.path); + for (let i = 0; i < array.length - 1; i++) { + if (allPaths.has(array[i])) { + top = allPaths.get(array[i]); + break; + } + } + if (top == null) { + minimal.push(item); + } else if (top != null && + top[arrayAtomicsSymbol] != null && + top.hasAtomics()) { + // special case for top level MongooseArrays + // the `top` array itself and a sub path of `top` are being set. + // the only way to honor all of both modifications is through a $set + // of entire array. + top[arrayAtomicsSymbol] = {}; + top[arrayAtomicsSymbol].$set = top; + } + }); + return minimal; +}; + +/** + * Assigns/compiles `schema` into this documents prototype. + * + * @param {Schema} schema + * @api private + * @method $__setSchema + * @memberOf Document + * @instance + */ + +Document.prototype.$__setSchema = function(schema) { + compile(schema.tree, this, undefined, schema.options); + + // Apply default getters if virtual doesn't have any (gh-6262) + for (const key of Object.keys(schema.virtuals)) { + schema.virtuals[key]._applyDefaultGetters(); + } + if (schema.path('schema') == null) { + this.schema = schema; + } + this.$__schema = schema; + this.$__middleware = schema._getDocumentMiddleware(); + this[documentSchemaSymbol] = schema; +}; + + +/** + * Get active path that were changed and are arrays + * + * @return {Array} + * @api private + * @method $__getArrayPathsToValidate + * @memberOf Document + * @instance + */ + +Document.prototype.$__getArrayPathsToValidate = function() { + DocumentArray || (DocumentArray = require('./types/documentArray')); + + // validate all document arrays. + return this.$__.activePaths + .map('init', 'modify', function(i) { + return this.$__getValue(i); + }.bind(this)) + .filter(function(val) { + return val && Array.isArray(val) && utils.isMongooseDocumentArray(val) && val.length; + }).reduce(function(seed, array) { + return seed.concat(array); + }, []) + .filter(function(doc) { + return doc; + }); +}; + + +/** + * Get all subdocs (by bfs) + * + * @param {object} [options] options. Currently for internal use. + * @return {Array} + * @api public + * @method $getAllSubdocs + * @memberOf Document + * @instance + */ + +Document.prototype.$getAllSubdocs = function(options) { + if (options?.useCache && this.$__.saveOptions?.__subdocs) { + return this.$__.saveOptions.__subdocs; + } + + DocumentArray || (DocumentArray = require('./types/documentArray')); + Embedded = Embedded || require('./types/arraySubdocument'); + + const subDocs = []; + function getSubdocs(doc) { + const newSubdocs = []; + + for (const { model } of doc.$__schema.childSchemas) { + // Avoid using `childSchemas.path` to avoid compatibility versions with pre-8.8 versions of Mongoose + const val = doc.$__getValue(model.path); + if (val == null) { + continue; + } + if (val.$__) { + newSubdocs.push(val); + } + if (Array.isArray(val)) { + for (const el of val) { + if (el?.$__) { + newSubdocs.push(el); + } + } + } + if (val instanceof Map) { + for (const el of val.values()) { + if (el?.$__) { + newSubdocs.push(el); + } + } + } + } + + for (const subdoc of newSubdocs) { + getSubdocs(subdoc); + } + subDocs.push(...newSubdocs); + } + + getSubdocs(this); + + if (this.$__.saveOptions) { + this.$__.saveOptions.__subdocs = subDocs; + } + + return subDocs; +}; + +/*! + * Runs queued functions + */ + +function applyQueue(doc) { + const q = doc.$__schema && doc.$__schema.callQueue; + if (!q.length) { + return; + } + + for (const pair of q) { + if (pair[0] !== 'pre' && pair[0] !== 'post' && pair[0] !== 'on') { + doc[pair[0]].apply(doc, pair[1]); + } + } +} + +/*! + * ignore + */ + +Document.prototype.$__handleReject = function handleReject(err) { + // emit on the Model if listening + if (this.$listeners('error').length) { + this.$emit('error', err); + } else if (this.constructor.listeners?.('error').length) { + this.constructor.emit('error', err); + } +}; + +/** + * Internal common logic for toObject() and toJSON() + * + * @return {object} + * @api private + * @method $toObject + * @memberOf Document + * @instance + */ + +Document.prototype.$toObject = function(options, json) { + const defaultOptions = this.$__schema._defaultToObjectOptions(json); + + const hasOnlyPrimitiveValues = this.$__hasOnlyPrimitiveValues(); + + // If options do not exist or is not an object, set it to empty object + options = utils.isPOJO(options) ? { ...options } : {}; + options._calledWithOptions = options._calledWithOptions || { ...options }; + + let _minimize; + if (options._calledWithOptions.minimize != null) { + _minimize = options.minimize; + } else if (this.$__schemaTypeOptions?.minimize != null) { + _minimize = this.$__schemaTypeOptions.minimize; + } else if (defaultOptions?.minimize != null) { + _minimize = defaultOptions.minimize; + } else { + _minimize = this.$__schema.options.minimize; + } + + options.minimize = _minimize; + if (!hasOnlyPrimitiveValues) { + options._seen = options._seen || new Map(); + } + + const depopulate = options._calledWithOptions.depopulate + ?? defaultOptions?.depopulate + ?? options.depopulate + ?? false; + // _isNested will only be true if this is not the top level document, we + // should never depopulate the top-level document + if (depopulate && options._isNested && this.$__.wasPopulated) { + return clone(this.$__.wasPopulated.value || this._doc._id, options); + } + if (depopulate) { + options.depopulate = true; + } + + // merge default options with input options. + if (defaultOptions != null) { + for (const key of Object.keys(defaultOptions)) { + if (options[key] == null) { + options[key] = defaultOptions[key]; + } + } + } + options._isNested = true; + options.json = json; + options.minimize = _minimize; + + const parentOptions = options._parentOptions; + // Parent options should only bubble down for subdocuments, not populated docs + options._parentOptions = this.$isSubdocument ? options : null; + + const schemaFieldsOnly = options._calledWithOptions.schemaFieldsOnly + ?? options.schemaFieldsOnly + ?? defaultOptions.schemaFieldsOnly + ?? false; + + let ret; + if (hasOnlyPrimitiveValues && !options.flattenObjectIds) { + // Fast path: if we don't have any nested objects or arrays, we only need a + // shallow clone. + ret = this.$__toObjectShallow(schemaFieldsOnly); + } else if (schemaFieldsOnly) { + ret = {}; + for (const path of Object.keys(this.$__schema.paths)) { + const value = this.$__getValue(path); + if (value === undefined) { + continue; + } + let pathToSet = path; + let objToSet = ret; + if (path.indexOf('.') !== -1) { + const segments = path.split('.'); + pathToSet = segments[segments.length - 1]; + for (let i = 0; i < segments.length - 1; ++i) { + objToSet[segments[i]] = objToSet[segments[i]] ?? {}; + objToSet = objToSet[segments[i]]; + } + } + if (value === null) { + objToSet[pathToSet] = null; + continue; + } + objToSet[pathToSet] = clone(value, options); + } + } else { + ret = clone(this._doc, options) || {}; + } + + const getters = options._calledWithOptions.getters + ?? options.getters + ?? defaultOptions.getters + ?? false; + + if (getters) { + applyGetters(this, ret); + + if (options.minimize) { + ret = minimize(ret) || {}; + } + } + + const virtuals = options._calledWithOptions.virtuals + ?? defaultOptions.virtuals + ?? parentOptions?.virtuals + ?? undefined; + + if (virtuals || (getters && virtuals !== false)) { + applyVirtuals(this, ret, options, options); + } + + if (options.versionKey === false && this.$__schema.options.versionKey) { + delete ret[this.$__schema.options.versionKey]; + } + + const transform = options._calledWithOptions.transform ?? true; + let transformFunction = undefined; + if (transform === true) { + transformFunction = defaultOptions.transform; + } else if (typeof transform === 'function') { + transformFunction = transform; + } + + // In the case where a subdocument has its own transform function, we need to + // check and see if the parent has a transform (options.transform) and if the + // child schema has a transform (this.schema.options.toObject) In this case, + // we need to adjust options.transform to be the child schema's transform and + // not the parent schema's + if (transform) { + applySchemaTypeTransforms(this, ret); + } + + if (options.useProjection) { + omitDeselectedFields(this, ret); + } + + if (typeof transformFunction === 'function') { + const xformed = transformFunction(this, ret, options); + if (typeof xformed !== 'undefined') { + ret = xformed; + } + } + + return ret; +}; + +/*! + * Internal shallow clone alternative to `$toObject()`: much faster, no options processing + */ + +Document.prototype.$__toObjectShallow = function $__toObjectShallow(schemaFieldsOnly) { + const ret = {}; + if (this._doc != null) { + const keys = schemaFieldsOnly ? Object.keys(this.$__schema.paths) : Object.keys(this._doc); + for (const key of keys) { + // Safe to do this even in the schemaFieldsOnly case because we assume there's no nested paths + const value = this._doc[key]; + if (value instanceof Date) { + ret[key] = new Date(value); + } else if (value !== undefined) { + ret[key] = value; + } + } + } + + return ret; +}; + +/** + * Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). + * + * Buffers are converted to instances of [mongodb.Binary](https://mongodb.github.io/node-mongodb-native/7.0/classes/BSON.Binary.html) for proper storage. + * + * #### Getters/Virtuals + * + * Example of only applying path getters + * + * doc.toObject({ getters: true, virtuals: false }) + * + * Example of only applying virtual getters + * + * doc.toObject({ virtuals: true }) + * + * Example of applying both path and virtual getters + * + * doc.toObject({ getters: true }) + * + * To apply these options to every document of your schema by default, set your [schemas](https://mongoosejs.com/docs/api/schema.html#Schema()) `toObject` option to the same argument. + * + * schema.set('toObject', { virtuals: true }) + * + * #### Transform: + * + * We may need to perform a transformation of the resulting object based on some criteria, say to remove some sensitive information or return a custom object. In this case we set the optional `transform` function. + * + * Transform functions receive three arguments + * + * function (doc, ret, options) {} + * + * - `doc` The mongoose document which is being converted + * - `ret` The plain object representation which has been converted + * - `options` The options in use (either schema options or the options passed inline) + * + * #### Example: + * + * // specify the transform schema option + * if (!schema.options.toObject) schema.options.toObject = {}; + * schema.options.toObject.transform = function (doc, ret, options) { + * // remove the _id of every document before returning the result + * delete ret._id; + * return ret; + * } + * + * // without the transformation in the schema + * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' } + * + * // with the transformation + * doc.toObject(); // { name: 'Wreck-it Ralph' } + * + * With transformations we can do a lot more than remove properties. We can even return completely new customized objects: + * + * if (!schema.options.toObject) schema.options.toObject = {}; + * schema.options.toObject.transform = function (doc, ret, options) { + * return { movie: ret.name } + * } + * + * // without the transformation in the schema + * doc.toObject(); // { _id: 'anId', name: 'Wreck-it Ralph' } + * + * // with the transformation + * doc.toObject(); // { movie: 'Wreck-it Ralph' } + * + * _Note: if a transform function returns `undefined`, the return value will be ignored._ + * + * Transformations may also be applied inline, overridding any transform set in the schema options. + * Any transform function specified in `toObject` options also propagates to any subdocuments. + * + * function deleteId(doc, ret, options) { + * delete ret._id; + * return ret; + * } + * + * const schema = mongoose.Schema({ name: String, docArr: [{ name: String }] }); + * const TestModel = mongoose.model('Test', schema); + * + * const doc = new TestModel({ name: 'test', docArr: [{ name: 'test' }] }); + * + * // pass the transform as an inline option. Deletes `_id` property + * // from both the top-level document and the subdocument. + * const obj = doc.toObject({ transform: deleteId }); + * obj._id; // undefined + * obj.docArr[0]._id; // undefined + * + * If you want to skip transformations, use `transform: false`: + * + * schema.options.toObject = { + * hide: '_id', + * transform: function(doc, ret, options) { + * if (options.hide) { + * options.hide.split(' ').forEach(function(prop) { + * delete ret[prop]; + * }); + * } + * return ret; + * } + * }; + * + * const doc = new Doc({ _id: 'anId', secret: 47, name: 'Wreck-it Ralph' }); + * doc.toObject(); // { secret: 47, name: 'Wreck-it Ralph' } + * doc.toObject({ hide: 'secret _id', transform: false });// { _id: 'anId', secret: 47, name: 'Wreck-it Ralph' } + * doc.toObject({ hide: 'secret _id', transform: true }); // { name: 'Wreck-it Ralph' } + * + * If you pass a transform in `toObject()` options, Mongoose will apply the transform + * to [subdocuments](https://mongoosejs.com/docs/subdocs.html) in addition to the top-level document. + * Similarly, `transform: false` skips transforms for all subdocuments. + * Note that this behavior is different for transforms defined in the schema: + * if you define a transform in `schema.options.toObject.transform`, that transform + * will **not** apply to subdocuments. + * + * const memberSchema = new Schema({ name: String, email: String }); + * const groupSchema = new Schema({ members: [memberSchema], name: String, email }); + * const Group = mongoose.model('Group', groupSchema); + * + * const doc = new Group({ + * name: 'Engineering', + * email: 'dev@mongoosejs.io', + * members: [{ name: 'Val', email: 'val@mongoosejs.io' }] + * }); + * + * // Removes `email` from both top-level document **and** array elements + * // { name: 'Engineering', members: [{ name: 'Val' }] } + * doc.toObject({ transform: (doc, ret) => { delete ret.email; return ret; } }); + * + * Transforms, like all of these options, are also available for `toJSON`. See [this guide to `JSON.stringify()`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html) to learn why `toJSON()` and `toObject()` are separate functions. + * + * See [schema options](https://mongoosejs.com/docs/guide.html#toObject) for some more details. + * + * _During save, no custom options are applied to the document before being sent to the database._ + * + * @param {object} [options] + * @param {boolean} [options.getters=false] if true, apply all getters, including virtuals + * @param {boolean|object} [options.virtuals=false] if true, apply virtuals, including aliases. Use `{ getters: true, virtuals: false }` to just apply getters, not virtuals. An object of the form `{ pathsToSkip: ['someVirtual'] }` may also be used to omit specific virtuals. + * @param {boolean} [options.aliases=true] if `options.virtuals = true`, you can set `options.aliases = false` to skip applying aliases. This option is a no-op if `options.virtuals = false`. + * @param {boolean} [options.minimize=true] if true, omit any empty objects from the output + * @param {Function|null} [options.transform=null] if set, mongoose will call this function to allow you to transform the returned object + * @param {boolean} [options.depopulate=false] if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths. + * @param {boolean} [options.versionKey=true] if false, exclude the version key (`__v` by default) from the output + * @param {boolean} [options.flattenMaps=false] if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`. + * @param {boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings. + * @param {boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format. + * @param {boolean} [options.useProjection=false] - If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema. + * @param {boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toObject()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema. + * @return {object} document as a plain old JavaScript object (POJO). This object may contain ObjectIds, Maps, Dates, mongodb.Binary, Buffers, and other non-POJO values. + * @see mongodb.Binary https://mongodb.github.io/node-mongodb-native/7.0/classes/BSON.Binary.html + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.toObject = function(options) { + return this.$toObject(options); +}; + +/*! + * Applies virtuals properties to `json`. + */ + +function applyVirtuals(self, json, options, toObjectOptions) { + const schema = self.$__schema; + const virtuals = schema.virtuals; + const paths = Object.keys(virtuals); + let i = paths.length; + const numPaths = i; + let path; + let assignPath; + let cur = self._doc; + let v; + const aliases = typeof toObjectOptions?.aliases === 'boolean' + ? toObjectOptions.aliases + : true; + + options = options || {}; + let virtualsToApply = null; + if (Array.isArray(options.virtuals)) { + virtualsToApply = new Set(options.virtuals); + } else if (options.virtuals?.pathsToSkip) { + virtualsToApply = new Set(paths); + for (let i = 0; i < options.virtuals.pathsToSkip.length; i++) { + if (virtualsToApply.has(options.virtuals.pathsToSkip[i])) { + virtualsToApply.delete(options.virtuals.pathsToSkip[i]); + } + } + } + + if (!cur) { + return json; + } + + for (i = 0; i < numPaths; ++i) { + path = paths[i]; + + if (virtualsToApply != null && !virtualsToApply.has(path)) { + continue; + } + + // Allow skipping aliases with `toObject({ virtuals: true, aliases: false })` + if (!aliases && Object.hasOwn(schema.aliases, path)) { + continue; + } + + // We may be applying virtuals to a nested object, for example if calling + // `doc.nestedProp.toJSON()`. If so, the path we assign to, `assignPath`, + // will be a trailing substring of the `path`. + assignPath = path; + if (options.path != null) { + if (!path.startsWith(options.path + '.')) { + continue; + } + assignPath = path.substring(options.path.length + 1); + } + if (assignPath.indexOf('.') === -1 && assignPath === path) { + v = virtuals[path].applyGetters(void 0, self); + if (v === void 0) { + continue; + } + v = clone(v, options); + json[assignPath] = v; + continue; + } + const parts = assignPath.split('.'); + v = clone(self.get(path), options); + if (v === void 0) { + continue; + } + const plen = parts.length; + cur = json; + for (let j = 0; j < plen - 1; ++j) { + cur[parts[j]] = cur[parts[j]] || {}; + cur = cur[parts[j]]; + } + cur[parts[plen - 1]] = v; + } + + return json; +} + + +/** + * Applies virtuals properties to `json`. + * + * @param {Document} self + * @param {object} json + * @return {object} `json` + * @api private + */ + +function applyGetters(self, json) { + const schema = self.$__schema; + const paths = Object.keys(schema.paths); + let i = paths.length; + let path; + let cur = self._doc; + let v; + + if (!cur) { + return json; + } + + while (i--) { + path = paths[i]; + + const parts = path.split('.'); + + const plen = parts.length; + const last = plen - 1; + let branch = json; + let part; + cur = self._doc; + + if (!self.$__isSelected(path)) { + continue; + } + + for (let ii = 0; ii < plen; ++ii) { + part = parts[ii]; + v = cur[part]; + // If we've reached a non-object part of the branch, continuing would + // cause "Cannot create property 'foo' on string 'bar'" error. + // Necessary for mongoose-intl plugin re: gh-14446 + if (branch != null && typeof branch !== 'object') { + break; + } else if (ii === last) { + branch[part] = schema.paths[path].applyGetters( + branch[part], + self + ); + if (Array.isArray(branch[part]) && schema.paths[path].embeddedSchemaType) { + for (let i = 0; i < branch[part].length; ++i) { + branch[part][i] = schema.paths[path].embeddedSchemaType.applyGetters( + branch[part][i], + self + ); + } + } + } else if (v == null) { + if (part in cur) { + branch[part] = v; + } + break; + } else { + branch = branch[part] || (branch[part] = {}); + } + cur = v; + } + } + + return json; +} + +/** + * Applies schema type transforms to `json`. + * + * @param {Document} self + * @param {object} json + * @return {object} `json` + * @api private + */ + +function applySchemaTypeTransforms(self, json) { + const schema = self.$__schema; + const paths = Object.keys(schema.paths || {}); + const cur = self._doc; + + if (!cur) { + return json; + } + + for (const path of paths) { + const schematype = schema.paths[path]; + const topLevelTransformFunction = schematype.options.transform ?? schematype.constructor?.defaultOptions?.transform; + const embeddedSchemaTypeTransformFunction = schematype.embeddedSchemaType?.options?.transform + ?? schematype.embeddedSchemaType?.constructor?.defaultOptions?.transform; + if (typeof topLevelTransformFunction === 'function') { + const val = self.$get(path); + if (val === undefined) { + continue; + } + const transformedValue = topLevelTransformFunction.call(self, val); + throwErrorIfPromise(path, transformedValue); + utils.setValue(path, transformedValue, json); + } else if (typeof embeddedSchemaTypeTransformFunction === 'function') { + const val = self.$get(path); + if (val === undefined) { + continue; + } + const vals = [].concat(val); + for (let i = 0; i < vals.length; ++i) { + const transformedValue = embeddedSchemaTypeTransformFunction.call(self, vals[i]); + vals[i] = transformedValue; + throwErrorIfPromise(path, transformedValue); + } + + json[path] = vals; + } + } + + return json; +} + +function throwErrorIfPromise(path, transformedValue) { + if (isPromise(transformedValue)) { + throw new Error('`transform` function must be synchronous, but the transform on path `' + path + '` returned a promise.'); + } +} + +/*! + * ignore + */ + +function omitDeselectedFields(self, json) { + const schema = self.$__schema; + const paths = Object.keys(schema.paths || {}); + const cur = self._doc; + + if (!cur) { + return json; + } + + let selected = self.$__.selected; + if (selected === void 0) { + selected = {}; + queryhelpers.applyPaths(selected, schema); + } + if (selected == null || utils.hasOwnKeys(selected) === false) { + return json; + } + + for (const path of paths) { + if (selected[path] != null && !selected[path]) { + delete json[path]; + } + } + + return json; +} + +/** + * The return value of this method is used in calls to [`JSON.stringify(doc)`](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript#the-tojson-function). + * + * This method accepts the same options as [Document#toObject](https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()). To apply the options to every document of your schema by default, set your [schemas](https://mongoosejs.com/docs/api/schema.html#Schema()) `toJSON` option to the same argument. + * + * schema.set('toJSON', { virtuals: true }); + * + * There is one difference between `toJSON()` and `toObject()` options. + * When you call `toJSON()`, the [`flattenMaps` option](https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()) defaults to `true`, because `JSON.stringify()` doesn't convert maps to objects by default. + * When you call `toObject()`, the `flattenMaps` option is `false` by default. + * + * See [schema options](https://mongoosejs.com/docs/guide.html#toJSON) for more information on setting `toJSON` option defaults. + * + * @param {object} options + * @param {boolean} [options.flattenMaps=true] if true, convert Maps to [POJOs](https://masteringjs.io/tutorials/fundamentals/pojo). Useful if you want to `JSON.stringify()` the result. + * @param {boolean} [options.flattenObjectIds=false] if true, convert any ObjectIds in the result to 24 character hex strings. + * @param {boolean} [options.flattenUUIDs=false] if true, convert any UUIDs in the result to 36-character UUID strings in 8-4-4-4-12 format. + * @param {boolean} [options.schemaFieldsOnly=false] - If true, the resulting object will only have fields that are defined in the document's schema. By default, `toJSON()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema. + * @return {object} + * @see Document#toObject https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject() + * @see JSON.stringify() in JavaScript https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript.html + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.toJSON = function(options) { + return this.$toObject(options, true); +}; + +/*! + * ignore + */ + +Document.prototype.ownerDocument = function() { + return this; +}; + + +/** + * If this document is a subdocument or populated document, returns the document's + * parent. Returns the original document if there is no parent. + * + * @return {Document} + * @api public + * @method parent + * @memberOf Document + * @instance + */ + +Document.prototype.parent = function() { + if (this.$isSubdocument || this.$__.wasPopulated) { + return this.$__.parent; + } + return this; +}; + +/** + * Alias for [`parent()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.parent()). If this document is a subdocument or populated + * document, returns the document's parent. Returns `undefined` otherwise. + * + * @return {Document} + * @api public + * @method $parent + * @memberOf Document + * @instance + */ + +Document.prototype.$parent = Document.prototype.parent; + +/** + * Set the parent of this document. + * + * @param {Document} parent + * @api private + * @method $__setParent + * @memberOf Document + * @instance + */ + +Document.prototype.$__setParent = function $__setParent(parent) { + this.$__.parent = parent; + this.$__parent = parent; +}; + +/** + * Helper for console.log + * + * @return {string} + * @api public + * @method inspect + * @memberOf Document + * @instance + */ + +Document.prototype.inspect = function(options) { + const isPOJO = utils.isPOJO(options); + let opts; + if (isPOJO) { + opts = options; + opts.minimize = false; + } + + const ret = arguments.length > 0 ? this.toObject(opts) : this.toObject(); + + if (ret == null) { + // If `toObject()` returns null, `this` is still an object, so if `inspect()` + // prints out null this can cause some serious confusion. See gh-7942. + return 'MongooseDocument { ' + ret + ' }'; + } + + return ret; +}; + +if (inspect.custom) { + // Avoid Node deprecation warning DEP0079 + Document.prototype[inspect.custom] = Document.prototype.inspect; +} + +/** + * Helper for console.log + * + * @return {string} + * @api public + * @method toString + * @memberOf Document + * @instance + */ + +Document.prototype.toString = function() { + const ret = this.inspect(); + if (typeof ret === 'string') { + return ret; + } + return inspect(ret); +}; + +/** + * Returns true if this document is equal to another document. + * + * Documents are considered equal when they have matching `_id`s, unless neither + * document has an `_id`, in which case this function falls back to using + * `deepEqual()`. + * + * @param {Document} [doc] a document to compare. If falsy, will always return "false". + * @return {boolean} + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.equals = function(doc) { + if (!doc) { + return false; + } + + const tid = this.$__getValue('_id'); + const docid = doc.$__ != null ? doc.$__getValue('_id') : doc; + if (!tid && !docid) { + return deepEqual(this, doc); + } + return tid && tid.equals + ? tid.equals(docid) + : tid === docid; +}; + +/** + * Populates paths on an existing document. + * + * #### Example: + * + * // Given a document, `populate()` lets you pull in referenced docs + * await doc.populate([ + * 'stories', + * { path: 'fans', sort: { name: -1 } } + * ]); + * doc.populated('stories'); // Array of ObjectIds + * doc.stories[0].title; // 'Casino Royale' + * doc.populated('fans'); // Array of ObjectIds + * + * // If the referenced doc has been deleted, `populate()` will + * // remove that entry from the array. + * await Story.delete({ title: 'Casino Royale' }); + * await doc.populate('stories'); // Empty array + * + * // You can also pass additional query options to `populate()`, + * // like projections: + * await doc.populate('fans', '-email'); + * doc.fans[0].email // undefined because of 2nd param `select` + * + * @param {string|object|Array} path either the path to populate or an object specifying all parameters, or either an array of those + * @param {object|string} [select] Field selection for the population query + * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field. + * @param {object} [match] Conditions for the population query + * @param {object} [options] Options for the population query (sort, etc) + * @param {string} [options.path=null] The path to populate. + * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](https://mongoosejs.com/docs/populate.html#deep-populate). + * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries. + * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](https://mongoosejs.com/docs/schematypes.html#schematype-options). + * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them. + * @param {object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object. + * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document. + * @param {object} [options.options=null] Additional options like `limit` and `lean`. + * @param {boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated + * @param {boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. We recommend setting this option to `true` if using transactions, especially if also populating multiple paths or paths with multiple models. MongoDB server does **not** support multiple operations in parallel on a single transaction. + * @param {Function} [callback] Callback + * @see population https://mongoosejs.com/docs/populate.html + * @see Query#select https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @see Model.populate https://mongoosejs.com/docs/api/model.html#Model.populate() + * @memberOf Document + * @instance + * @return {Promise|null} Returns a Promise if no `callback` is given. + * @api public + */ + +Document.prototype.populate = async function populate() { + const pop = {}; + const args = [...arguments]; + if (typeof args[args.length - 1] === 'function') { + throw new MongooseError('Document.prototype.populate() no longer accepts a callback'); + } + + if (args.length !== 0) { + // use hash to remove duplicate paths + const res = utils.populate.apply(null, args); + for (const populateOptions of res) { + pop[populateOptions.path] = populateOptions; + } + } + + const paths = utils.object.vals(pop); + + let topLevelModel = this.constructor; + if (this.$__isNested) { + topLevelModel = this.$__[scopeSymbol].constructor; + const nestedPath = this.$__.nestedPath; + paths.forEach(function(populateOptions) { + populateOptions.path = nestedPath + '.' + populateOptions.path; + }); + } + + // Use `$session()` by default if the document has an associated session + // See gh-6754 + if (this.$session() != null) { + const session = this.$session(); + paths.forEach(path => { + if (path.options == null) { + path.options = { session: session }; + return; + } + if (!('session' in path.options)) { + path.options.session = session; + } + }); + } + + paths.forEach(p => { + p._localModel = topLevelModel; + }); + + return topLevelModel.populate(this, paths); +}; + +/** + * Gets all populated documents associated with this document. + * + * @api public + * @return {Document[]} array of populated documents. Empty array if there are no populated documents associated with this document. + * @memberOf Document + * @method $getPopulatedDocs + * @instance + */ + +Document.prototype.$getPopulatedDocs = function $getPopulatedDocs() { + let keys = []; + if (this.$__.populated != null) { + keys = keys.concat(Object.keys(this.$__.populated)); + } + let result = []; + for (const key of keys) { + const value = this.$get(key); + if (Array.isArray(value)) { + result = result.concat(value); + } else if (value instanceof Document) { + result.push(value); + } + } + return result; +}; + +/** + * Gets _id(s) used during population of the given `path`. + * + * #### Example: + * + * const doc = await Model.findOne().populate('author'); + * + * console.log(doc.author.name); // Dr.Seuss + * console.log(doc.populated('author')); // '5144cf8050f071d979c118a7' + * + * If the path was not populated, returns `undefined`. + * + * @param {string} path + * @param {any} [val] + * @param {object} [options] + * @return {Array|ObjectId|number|Buffer|string|undefined} + * @memberOf Document + * @instance + * @api public + */ + +Document.prototype.populated = function(path, val, options) { + // val and options are internal + if (val == null || val === true) { + if (!this.$__.populated) { + return undefined; + } + if (typeof path !== 'string') { + return undefined; + } + + // Map paths can be populated with either `path.$*` or just `path` + const _path = path.endsWith('.$*') ? path.replace(/\.\$\*$/, '') : path; + + const v = this.$__.populated[_path]; + if (v) { + return val === true ? v : v.value; + } + return undefined; + } + + this.$__.populated || (this.$__.populated = {}); + this.$__.populated[path] = { value: val, options: options }; + + // If this was a nested populate, make sure each populated doc knows + // about its populated children (gh-7685) + const pieces = path.split('.'); + for (let i = 0; i < pieces.length - 1; ++i) { + const subpath = pieces.slice(0, i + 1).join('.'); + const subdoc = this.$get(subpath); + if (subdoc?.$__ != null && this.$populated(subpath)) { + const rest = pieces.slice(i + 1).join('.'); + subdoc.$populated(rest, val, options); + // No need to continue because the above recursion should take care of + // marking the rest of the docs as populated + break; + } + } + + return val; +}; + +/** + * Alias of [`.populated`](https://mongoosejs.com/docs/api/document.html#Document.prototype.populated()). + * + * @method $populated + * @memberOf Document + * @api public + */ + +Document.prototype.$populated = Document.prototype.populated; + +/** + * Throws an error if a given path is not populated + * + * #### Example: + * + * const doc = await Model.findOne().populate('author'); + * + * doc.$assertPopulated('author'); // does not throw + * doc.$assertPopulated('other path'); // throws an error + * + * // Manually populate and assert in one call. The following does + * // `doc.$set({ likes })` before asserting. + * doc.$assertPopulated('likes', { likes }); + * + * + * @param {string|string[]} path path or array of paths to check. `$assertPopulated` throws if any of the given paths is not populated. + * @param {object} [values] optional values to `$set()`. Convenient if you want to manually populate a path and assert that the path was populated in 1 call. + * @return {Document} this + * @memberOf Document + * @method $assertPopulated + * @instance + * @api public + */ + +Document.prototype.$assertPopulated = function $assertPopulated(path, values) { + if (Array.isArray(path)) { + path.forEach(p => this.$assertPopulated(p, values)); + return this; + } + + if (arguments.length > 1) { + this.$set(values); + } + + if (!this.$populated(path)) { + throw new MongooseError(`Expected path "${path}" to be populated`); + } + + return this; +}; + +/** + * Takes a populated field and returns it to its unpopulated state. + * + * #### Example: + * + * Model.findOne().populate('author').exec(function (err, doc) { + * console.log(doc.author.name); // Dr.Seuss + * console.log(doc.depopulate('author')); + * console.log(doc.author); // '5144cf8050f071d979c118a7' + * }) + * + * If the path was not provided, then all populated fields are returned to their unpopulated state. + * + * @param {string|string[]} [path] Specific Path to depopulate. If unset, will depopulate all paths on the Document. Or multiple space-delimited paths. + * @return {Document} this + * @see Document.populate https://mongoosejs.com/docs/api/document.html#Document.prototype.populate() + * @api public + * @memberOf Document + * @instance + */ + +Document.prototype.depopulate = function(path) { + if (typeof path === 'string') { + path = path.indexOf(' ') === -1 ? [path] : path.split(' '); + } + + let populatedIds; + const virtualKeys = this.$$populatedVirtuals ? Object.keys(this.$$populatedVirtuals) : []; + const populated = this.$__?.populated || {}; + + if (arguments.length === 0) { + // Depopulate all + for (const virtualKey of virtualKeys) { + delete this.$$populatedVirtuals[virtualKey]; + delete this._doc[virtualKey]; + delete populated[virtualKey]; + } + + const keys = Object.keys(populated); + + for (const key of keys) { + populatedIds = this.$populated(key); + if (!populatedIds) { + continue; + } + delete populated[key]; + if (Array.isArray(populatedIds)) { + const arr = utils.getValue(key, this._doc); + if (arr.isMongooseArray) { + const rawArray = arr.__array; + for (let i = 0; i < rawArray.length; ++i) { + const subdoc = rawArray[i]; + if (subdoc == null) { + continue; + } + rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; + } + } else { + utils.setValue(key, populatedIds, this._doc); + } + } else { + utils.setValue(key, populatedIds, this._doc); + } + } + return this; + } + + for (const singlePath of path) { + populatedIds = this.$populated(singlePath); + delete populated[singlePath]; + + if (virtualKeys.indexOf(singlePath) !== -1) { + delete this.$$populatedVirtuals[singlePath]; + delete this._doc[singlePath]; + } else if (populatedIds) { + if (Array.isArray(populatedIds)) { + const arr = utils.getValue(singlePath, this._doc); + if (arr.isMongooseArray) { + const rawArray = arr.__array; + for (let i = 0; i < rawArray.length; ++i) { + const subdoc = rawArray[i]; + if (subdoc == null) { + continue; + } + rawArray[i] = subdoc instanceof Document ? subdoc._doc._id : subdoc._id; + } + } else { + utils.setValue(singlePath, populatedIds, this._doc); + } + } else { + utils.setValue(singlePath, populatedIds, this._doc); + } + } + } + return this; +}; + + +/** + * Returns the full path to this document. + * + * @param {string} [path] + * @return {string} + * @api private + * @method $__fullPath + * @memberOf Document + * @instance + */ + +Document.prototype.$__fullPath = function(path) { + // overridden in SubDocuments + return path || ''; +}; + +/** + * Returns the changes that happened to the document + * in the format that will be sent to MongoDB. + * + * #### Example: + * + * const userSchema = new Schema({ + * name: String, + * age: Number, + * country: String + * }); + * const User = mongoose.model('User', userSchema); + * const user = await User.create({ + * name: 'Hafez', + * age: 25, + * country: 'Egypt' + * }); + * + * // returns an empty object, no changes happened yet + * user.getChanges(); // { } + * + * user.country = undefined; + * user.age = 26; + * + * user.getChanges(); // { $set: { age: 26 }, { $unset: { country: 1 } } } + * + * await user.save(); + * + * user.getChanges(); // { } + * + * Modifying the object that `getChanges()` returns does not affect the document's + * change tracking state. Even if you `delete user.getChanges().$set`, Mongoose + * will still send a `$set` to the server. + * + * @return {object} + * @api public + * @method getChanges + * @memberOf Document + * @instance + */ + +Document.prototype.getChanges = function() { + const delta = this.$__delta(); + const changes = delta ? delta[1] : {}; + return changes; +}; + +/** + * Produces a special query document of the modified properties used in updates. + * + * @param {string[]|null} [pathsToSave] paths to include in delta generation + * @param {Set|null} [pathsToSaveSet] pre-built Set of pathsToSave for O(1) exact lookup + * @api private + * @method $__delta + * @memberOf Document + * @instance + */ + +Document.prototype.$__delta = function $__delta(pathsToSave, pathsToSaveSet) { + const allDirty = this.$__dirty(); + let dirty = allDirty; + let unsavedDirty = null; + if (pathsToSave != null) { + dirty = []; + unsavedDirty = []; + for (const data of allDirty) { + if (isInPathsToSave(data.path, pathsToSaveSet, pathsToSave)) { + dirty.push(data); + } else { + unsavedDirty.push(data); + } + } + } + const optimisticConcurrency = this.$__schema.options.optimisticConcurrency; + if (optimisticConcurrency) { + if (Array.isArray(optimisticConcurrency)) { + const optCon = new Set(optimisticConcurrency); + const modPaths = this.modifiedPaths(); + const hasRelevantModPaths = pathsToSave == null ? + modPaths.find(path => optCon.has(path)) : + modPaths.find(path => optCon.has(path) && isInPathsToSave(path, pathsToSaveSet, pathsToSave)); + if (hasRelevantModPaths) { + this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; + } + } else if (Array.isArray(optimisticConcurrency?.exclude)) { + const excluded = new Set(optimisticConcurrency.exclude); + const modPaths = this.modifiedPaths(); + const hasRelevantModPaths = pathsToSave == null ? + modPaths.find(path => !excluded.has(path)) : + modPaths.find(path => !excluded.has(path) && isInPathsToSave(path, pathsToSaveSet, pathsToSave)); + if (hasRelevantModPaths) { + this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; + } + } else { + this.$__.version = dirty.length ? VERSION_ALL : VERSION_WHERE; + } + } + + if (!dirty.length && VERSION_ALL !== this.$__.version) { + return; + } + const where = {}; + const delta = {}; + const len = dirty.length; + const divergent = []; + let d = 0; + + where._id = this._doc._id; + // If `_id` is an object, need to depopulate, but also need to be careful + // because `_id` can technically be null (see gh-6406) + if (where?._id?.$__ != null) { + where._id = where._id.toObject({ transform: false, depopulate: true }); + } + for (; d < len; ++d) { + const data = dirty[d]; + let value = data.value; + const match = checkDivergentArray(this, data.path, value); + if (match) { + divergent.push(match); + continue; + } + + const pop = this.$populated(data.path, true); + if (!pop && this.$__.selected) { + // If any array was selected using an $elemMatch projection, we alter the path and where clause + // NOTE: MongoDB only supports projected $elemMatch on top level array. + const pathSplit = data.path.split('.'); + const top = pathSplit[0]; + if (this.$__.selected[top] && this.$__.selected[top].$elemMatch) { + // If the selected array entry was modified + if (pathSplit.length > 1 && pathSplit[1] == 0 && typeof where[top] === 'undefined') { + where[top] = this.$__.selected[top]; + pathSplit[1] = '$'; + data.path = pathSplit.join('.'); + } + // if the selected array was modified in any other way throw an error + else { + divergent.push(data.path); + continue; + } + } + } + + // If this path is set to default, and either this path or one of + // its parents is excluded, don't treat this path as dirty. + if (this.$isDefault(data.path) && this.$__.selected) { + if (data.path.indexOf('.') === -1 && isPathExcluded(this.$__.selected, data.path)) { + continue; + } + + const pathsToCheck = parentPaths(data.path); + if (pathsToCheck.find(path => isPathExcluded(this.$__.isSelected, path))) { + continue; + } + } + + if (divergent.length) continue; + if (value === undefined) { + operand(this, where, delta, data, 1, '$unset'); + } else if (value === null) { + operand(this, where, delta, data, null); + } else if (typeof value.getAtomics === 'function') { + // arrays and other custom container types + handleAtomics(this, where, delta, data, value); + } else if (value[MongooseBuffer.pathSymbol] && Buffer.isBuffer(value)) { + // MongooseBuffer + value = value.toObject(); + operand(this, where, delta, data, value); + } else { + if (this.$__.primitiveAtomics?.[data.path] != null) { + const val = this.$__.primitiveAtomics[data.path]; + const op = firstKey(val); + operand(this, where, delta, data, val[op], op); + } else { + value = clone(value, { + depopulate: true, + transform: false, + virtuals: false, + getters: false, + omitUndefined: true, + _isNested: true + }); + operand(this, where, delta, data, value); + } + } + } + + if (divergent.length) { + throw new DivergentArrayError(divergent); + } + + if (this.$__.version) { + this.$__version(where, delta); + } + + if (utils.hasOwnKeys(delta) === false) { + return [where, null, unsavedDirty]; + } + + return [where, delta, unsavedDirty]; +}; + +/** + * Determine if array was populated with some form of filter and is now + * being updated in a manner which could overwrite data unintentionally. + * + * @see https://github.com/Automattic/mongoose/issues/1334 + * @param {Document} doc + * @param {string} path + * @param {any} array + * @return {string|undefined} + * @api private + */ + +function checkDivergentArray(doc, path, array) { + // see if we populated this path + const pop = doc.$populated(path, true); + + if (!pop && doc.$__.selected) { + // If any array was selected using an $elemMatch projection, we deny the update. + // NOTE: MongoDB only supports projected $elemMatch on top level array. + const top = path.split('.')[0]; + if (doc.$__.selected[top + '.$']) { + return top; + } + } + + if (!(pop && utils.isMongooseArray(array))) return; + + // If the array was populated using options that prevented all + // documents from being returned (match, skip, limit) or they + // deselected the _id field, $pop and $set of the array are + // not safe operations. If _id was deselected, we do not know + // how to remove elements. $pop will pop off the _id from the end + // of the array in the db which is not guaranteed to be the + // same as the last element we have here. $set of the entire array + // would be similarly destructive as we never received all + // elements of the array and potentially would overwrite data. + const check = pop.options.match || + pop.options.options && Object.hasOwn(pop.options.options, 'limit') || // 0 is not permitted + pop.options.options?.skip || // 0 is permitted + pop.options.select && // deselected _id? + (pop.options.select._id === 0 || + /\s?-_id\s?/.test(pop.options.select)); + + if (check) { + const atomics = array[arrayAtomicsSymbol]; + if (utils.hasOwnKeys(atomics) === false || atomics.$set || atomics.$pop) { + return path; + } + } +} + +/** + * Apply the operation to the delta (update) clause as + * well as track versioning for our where clause. + * + * @param {Document} self + * @param {object} where Unused + * @param {object} delta + * @param {object} data + * @param {Mixed} val + * @param {string} [op] + * @api private + */ + +function operand(self, where, delta, data, val, op) { + // delta + op || (op = '$set'); + if (!delta[op]) delta[op] = {}; + delta[op][data.path] = val; + // disabled versioning? + if (self.$__schema.options.versionKey === false) return; + + // path excluded from versioning? + if (shouldSkipVersioning(self, data.path)) return; + + // already marked for versioning? + if (VERSION_ALL === (VERSION_ALL & self.$__.version)) return; + + if ( + self.$__schema.options.optimisticConcurrency === true || + Array.isArray(self.$__schema.options.optimisticConcurrency) || + Array.isArray(self.$__schema.options.optimisticConcurrency?.exclude) + ) { + return; + } + + switch (op) { + case '$set': + case '$unset': + case '$pop': + case '$pull': + case '$pullAll': + case '$push': + case '$addToSet': + case '$inc': + break; + default: + // nothing to do + return; + } + + // ensure updates sent with positional notation are + // editing the correct array element. + // only increment the version if an array position changes. + // modifying elements of an array is ok if position does not change. + if (op === '$push' || op === '$addToSet' || op === '$pullAll' || op === '$pull') { + if (/\.\d+\.|\.\d+$/.test(data.path)) { + self.$__.version = VERSION_ALL; + } else { + self.$__.version |= VERSION_INC; + } + } else if (/^\$p/.test(op)) { + // potentially changing array positions + self.$__.version = VERSION_ALL; + } else if (Array.isArray(val)) { + // $set an array + self.$__.version = VERSION_ALL; + } else if (/\.\d+\.|\.\d+$/.test(data.path)) { + // now handling $set, $unset + // subpath of array + self.$__.version |= VERSION_WHERE; + } +} + +/** + * Compiles an update and where clause for a `val` with _atomics. + * + * @param {Document} self + * @param {object} where + * @param {object} delta + * @param {object} data + * @param {Array} value + * @api private + */ + +function handleAtomics(self, where, delta, data, value) { + if (delta.$set?.[data.path]) { + // $set has precedence over other atomics + return; + } + + if (typeof value.getAtomics === 'function') { + value.getAtomics().forEach(function(atomic) { + const op = atomic[0]; + const val = atomic[1]; + operand(self, where, delta, data, val, op); + }); + return; + } + + if (typeof value.$__getAtomics === 'function') { + value.$__getAtomics().forEach(function(atomic) { + const op = atomic[0]; + const val = atomic[1]; + operand(self, where, delta, data, val, op); + }); + return; + } + + // legacy support for plugins + + const atomics = value[arrayAtomicsSymbol]; + const ops = Object.keys(atomics); + let i = ops.length; + let val; + let op; + + if (i === 0) { + // $set + + if (utils.isMongooseObject(value)) { + value = value.toObject({ depopulate: 1, _isNested: true }); + } else if (value.valueOf) { + value = value.valueOf(); + } + + return operand(self, where, delta, data, value); + } + + function iter(mem) { + return utils.isMongooseObject(mem) + ? mem.toObject({ depopulate: 1, _isNested: true }) + : mem; + } + + while (i--) { + op = ops[i]; + val = atomics[op]; + + if (utils.isMongooseObject(val)) { + val = val.toObject({ depopulate: true, transform: false, _isNested: true }); + } else if (Array.isArray(val)) { + val = val.map(iter); + } else if (val.valueOf) { + val = val.valueOf(); + } + + if (op === '$addToSet') { + val = { $each: val }; + } + + operand(self, where, delta, data, val, op); + } +} + +/** + * Determines whether versioning should be skipped for the given path + * + * @param {Document} self + * @param {string} path + * @return {boolean} true if versioning should be skipped for the given path + * @api private + */ +function shouldSkipVersioning(self, path) { + const skipVersioning = self.$__schema.options.skipVersioning; + if (!skipVersioning) return false; + + // Remove any array indexes from the path + path = path.replace(/\.\d+\./, '.'); + + return skipVersioning[path]; +} + +/** + * Returns a copy of this document with a deep clone of `_doc` and `$__`. + * + * @return {Document} a copy of this document + * @api public + * @method $clone + * @memberOf Document + * @instance + */ + +Document.prototype.$clone = function() { + const Model = this.constructor; + const clonedDoc = new Model(); + clonedDoc.$isNew = this.$isNew; + if (this._doc) { + clonedDoc._doc = clone(this._doc, { retainDocuments: true, parentDoc: clonedDoc }); + } + if (this.$__) { + const Cache = this.$__.constructor; + const clonedCache = new Cache(); + for (const key of Object.getOwnPropertyNames(this.$__)) { + if (key === 'activePaths') { + continue; + } + clonedCache[key] = clone(this.$__[key]); + } + Object.assign( + clonedCache.activePaths, + clone({ ...this.$__.activePaths }) + ); + clonedDoc.$__ = clonedCache; + } + return clonedDoc; +}; + +/** + * Creates a snapshot of this document's internal change tracking state. You can later + * reset this document's change tracking state using `$restoreModifiedPathsSnapshot()`. + * + * #### Example: + * + * const doc = await TestModel.findOne(); + * const snapshot = doc.$createModifiedPathsSnapshot(); + * + * @return {ModifiedPathsSnapshot} a copy of this document's internal change tracking state + * @api public + * @method $createModifiedPathsSnapshot + * @memberOf Document + * @instance + */ + +Document.prototype.$createModifiedPathsSnapshot = function $createModifiedPathsSnapshot() { + const subdocSnapshot = new WeakMap(); + if (!this.$isSubdocument) { + const subdocs = this.$getAllSubdocs(); + for (const child of subdocs) { + subdocSnapshot.set(child, child.$__.activePaths.clone()); + } + } + + return new ModifiedPathsSnapshot( + subdocSnapshot, + this.$__.activePaths.clone(), + this.$__.version + ); +}; + +/** + * Restore this document's change tracking state to the given snapshot. + * Note that `$restoreModifiedPathsSnapshot()` does **not** modify the document's + * properties, just resets the change tracking state. + * + * This method is especially useful when writing [custom transaction wrappers](https://github.com/Automattic/mongoose/issues/14268#issuecomment-2100505554) that need to restore change tracking when aborting a transaction. + * + * #### Example: + * + * const doc = await TestModel.findOne(); + * const snapshot = doc.$createModifiedPathsSnapshot(); + * + * doc.name = 'test'; + * doc.$restoreModifiedPathsSnapshot(snapshot); + * doc.$isModified('name'); // false because `name` was not modified when snapshot was taken + * doc.name; // 'test', `$restoreModifiedPathsSnapshot()` does **not** modify the document's data, only change tracking + * + * @param {ModifiedPathsSnapshot} snapshot of the document's internal change tracking state snapshot to restore + * @api public + * @method $restoreModifiedPathsSnapshot + * @return {Document} this + * @memberOf Document + * @instance + */ + +Document.prototype.$restoreModifiedPathsSnapshot = function $restoreModifiedPathsSnapshot(snapshot) { + this.$__.activePaths = snapshot.activePaths.clone(); + this.$__.version = snapshot.version; + if (!this.$isSubdocument) { + const subdocs = this.$getAllSubdocs(); + for (const child of subdocs) { + if (snapshot.subdocSnapshot.has(child)) { + child.$__.activePaths = snapshot.subdocSnapshot.get(child); + } + } + } + + return this; +}; + +/** + * Clear the document's modified paths. + * + * #### Example: + * + * const doc = await TestModel.findOne(); + * + * doc.name = 'test'; + * doc.$isModified('name'); // true + * + * doc.$clearModifiedPaths(); + * doc.name; // 'test', `$clearModifiedPaths()` does **not** modify the document's data, only change tracking + * + * @api public + * @return {Document} this + * @method $clearModifiedPaths + * @memberOf Document + * @instance + */ + +Document.prototype.$clearModifiedPaths = function $clearModifiedPaths() { + this.$__.activePaths.clear('modify'); + this.$__.activePaths.clear('init'); + this.$__.version = 0; + if (!this.$isSubdocument) { + const subdocs = this.$getAllSubdocs(); + for (const child of subdocs) { + child.$clearModifiedPaths(); + } + } + + return this; +}; + +/*! + * Check if the given document only has primitive values + */ + +Document.prototype.$__hasOnlyPrimitiveValues = function $__hasOnlyPrimitiveValues() { + return !this.$__.populated && !this.$__.wasPopulated && (this._doc == null || Object.values(this._doc).every(v => { + return v == null + || typeof v !== 'object' + || (utils.isNativeObject(v) && !Array.isArray(v)) + || isBsonType(v, 'ObjectId') + || isBsonType(v, 'Decimal128'); + })); +}; + +/*! + * Increment this document's version if necessary. + */ + +Document.prototype._applyVersionIncrement = function _applyVersionIncrement() { + if (!this.$__.version) return; + const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); + + this.$__.version = undefined; + if (doIncrement) { + const key = this.$__schema.options.versionKey; + const version = this.$__getValue(key) || 0; + this.$__setValue(key, version + 1); // increment version if was successful + } +}; + +/*! + * Increment this document's version if necessary. + */ + +Document.prototype._applyVersionIncrement = function _applyVersionIncrement() { + if (!this.$__.version) return; + const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); + + this.$__.version = undefined; + if (doIncrement) { + const key = this.$__schema.options.versionKey; + const version = this.$__getValue(key) || 0; + this.$__setValue(key, version + 1); // increment version if was successful + } +}; + +/*! + * Module exports. + */ + +Document.VERSION_WHERE = VERSION_WHERE; +Document.VERSION_INC = VERSION_INC; +Document.VERSION_ALL = VERSION_ALL; +Document.ValidationError = ValidationError; +module.exports = exports = Document; diff --git a/backend/node_modules/mongoose/lib/driver.js b/backend/node_modules/mongoose/lib/driver.js new file mode 100644 index 0000000..cf7ca3d --- /dev/null +++ b/backend/node_modules/mongoose/lib/driver.js @@ -0,0 +1,15 @@ +'use strict'; + +/*! + * ignore + */ + +let driver = null; + +module.exports.get = function() { + return driver; +}; + +module.exports.set = function(v) { + driver = v; +}; diff --git a/backend/node_modules/mongoose/lib/drivers/SPEC.md b/backend/node_modules/mongoose/lib/drivers/SPEC.md new file mode 100644 index 0000000..6464693 --- /dev/null +++ b/backend/node_modules/mongoose/lib/drivers/SPEC.md @@ -0,0 +1,4 @@ + +# Driver Spec + +TODO diff --git a/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/bulkWriteResult.js b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/bulkWriteResult.js new file mode 100644 index 0000000..e3c79b7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/bulkWriteResult.js @@ -0,0 +1,5 @@ +'use strict'; + +const BulkWriteResult = require('mongodb/lib/bulk/common').BulkWriteResult; + +module.exports = BulkWriteResult; diff --git a/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js new file mode 100644 index 0000000..03c95b5 --- /dev/null +++ b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/collection.js @@ -0,0 +1,393 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseCollection = require('../../collection'); +const MongooseError = require('../../error/mongooseError'); +const Collection = require('mongodb').Collection; +const ObjectId = require('../../types/objectid'); +const getConstructorName = require('../../helpers/getConstructorName'); +const internalToObjectOptions = require('../../options').internalToObjectOptions; +const stream = require('stream'); +const util = require('util'); + +const formatToObjectOptions = Object.freeze({ ...internalToObjectOptions, copyTrustedSymbol: false }); + +/** + * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) collection implementation. + * + * All methods methods from the [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) driver are copied and wrapped in queue management. + * + * @inherits Collection https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html + * @api private + */ + +function NativeCollection(name, conn, options) { + this.collection = null; + this.Promise = options.Promise || Promise; + this.modelName = options.modelName; + delete options.modelName; + this._closed = false; + MongooseCollection.apply(this, arguments); +} + +/*! + * Inherit from abstract Collection. + */ + +Object.setPrototypeOf(NativeCollection.prototype, MongooseCollection.prototype); + +/** + * Called when the connection opens. + * + * @api private + */ + +NativeCollection.prototype.onOpen = function() { + this.collection = this.conn.db.collection(this.name); + MongooseCollection.prototype.onOpen.call(this); + return this.collection; +}; + +/** + * Called when the connection closes + * + * @api private + */ + +NativeCollection.prototype.onClose = function(force) { + MongooseCollection.prototype.onClose.call(this, force); +}; + +/** + * Helper to get the collection, in case `this.collection` isn't set yet. + * May happen if `bufferCommands` is false and created the model when + * Mongoose was disconnected. + * + * @api private + */ + +NativeCollection.prototype._getCollection = function _getCollection() { + if (this.collection) { + return this.collection; + } + if (this.conn.db != null) { + this.collection = this.conn.db.collection(this.name); + return this.collection; + } + return null; +}; + +/** + * Copy the collection methods and make them subject to queues + * @param {number|string} I + * @api private + */ + +function iter(i) { + NativeCollection.prototype[i] = function() { + const collection = this._getCollection(); + const args = Array.from(arguments); + const _this = this; + const globalDebug = _this?.conn?.base?.options?.debug; + const connectionDebug = _this?.conn?.options?.debug; + const debug = connectionDebug == null ? globalDebug : connectionDebug; + const opId = new ObjectId(); + + // If user force closed, queueing will hang forever. See #5664 + if (this.conn.$wasForceClosed) { + const error = new MongooseError('Connection was force closed'); + if (args.length > 0 && + typeof args[args.length - 1] === 'function') { + args[args.length - 1](error); + return; + } else { + throw error; + } + } + + let timeout = null; + let waitForBufferPromise = null; + if (this._shouldBufferCommands() && this.buffer) { + this.conn.emit('buffer', { + _id: opId, + modelName: _this.modelName, + collectionName: _this.name, + method: i, + args: args + }); + + const bufferTimeoutMS = this._getBufferTimeoutMS(); + waitForBufferPromise = new Promise((resolve, reject) => { + this.addQueue(resolve); + + timeout = setTimeout(() => { + const removed = this.removeQueue(resolve); + if (removed) { + const message = 'Operation `' + this.name + '.' + i + '()` buffering timed out after ' + + bufferTimeoutMS + 'ms'; + const err = new MongooseError(message); + this.conn.emit('buffer-end', { _id: opId, modelName: _this.modelName, collectionName: _this.name, method: i, error: err }); + reject(err); + } + }, bufferTimeoutMS); + }); + + return waitForBufferPromise.then(() => { + if (timeout) { + clearTimeout(timeout); + } + return this[i].apply(this, args); + }); + } + + if (debug) { + if (typeof debug === 'function') { + let argsToAdd = null; + if (typeof args[args.length - 1] == 'function') { + argsToAdd = args.slice(0, args.length - 1); + } else { + argsToAdd = args; + } + debug.apply(_this, + [_this.name, i].concat(argsToAdd)); + } else if (debug instanceof stream.Writable) { + this.$printToStream(_this.name, i, args, debug); + } else { + const color = debug.color == null ? true : debug.color; + const shell = debug.shell == null ? false : debug.shell; + this.$print(_this.name, i, args, color, shell); + } + } + + this.conn.emit('operation-start', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, params: args }); + + try { + if (collection == null) { + const message = 'Cannot call `' + this.name + '.' + i + '()` before initial connection ' + + 'is complete if `bufferCommands = false`. Make sure you `await mongoose.connect()` if ' + + 'you have `bufferCommands = false`.'; + throw new MongooseError(message); + } + + const ret = collection[i].apply(collection, args); + if (typeof ret?.then === 'function') { + return ret.then( + result => { + if (timeout != null) { + clearTimeout(timeout); + } + this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result }); + return result; + }, + error => { + if (timeout != null) { + clearTimeout(timeout); + } + this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error }); + throw error; + } + ); + } + + this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, result: ret }); + if (timeout != null) { + clearTimeout(timeout); + } + return ret; + } catch (error) { + if (timeout != null) { + clearTimeout(timeout); + } + this.conn.emit('operation-end', { _id: opId, modelName: _this.modelName, collectionName: this.name, method: i, error: error }); + throw error; + } + }; +} + +for (const key of Object.getOwnPropertyNames(Collection.prototype)) { + // Janky hack to work around gh-3005 until we can get rid of the mongoose + // collection abstraction + const descriptor = Object.getOwnPropertyDescriptor(Collection.prototype, key); + // Skip properties with getters because they may throw errors (gh-8528) + if (descriptor.get !== undefined) { + continue; + } + if (typeof Collection.prototype[key] !== 'function') { + continue; + } + + iter(key); +} + +/** + * Debug print helper + * + * @api public + * @method $print + */ + +NativeCollection.prototype.$print = function(name, i, args, color, shell) { + const moduleName = color ? '\x1B[0;36mMongoose:\x1B[0m ' : 'Mongoose: '; + const functionCall = [name, i].join('.'); + const _args = []; + for (let j = args.length - 1; j >= 0; --j) { + if (this.$format(args[j]) || _args.length) { + _args.unshift(this.$format(args[j], color, shell)); + } + } + const params = '(' + _args.join(', ') + ')'; + + console.info(moduleName + functionCall + params); +}; + +/** + * Debug print helper + * + * @api public + * @method $print + */ + +NativeCollection.prototype.$printToStream = function(name, i, args, stream) { + const functionCall = [name, i].join('.'); + const _args = []; + for (let j = args.length - 1; j >= 0; --j) { + if (this.$format(args[j]) || _args.length) { + _args.unshift(this.$format(args[j])); + } + } + const params = '(' + _args.join(', ') + ')'; + + stream.write(functionCall + params, 'utf8'); +}; + +/** + * Formatter for debug print args + * + * @api public + * @method $format + */ + +NativeCollection.prototype.$format = function(arg, color, shell) { + const type = typeof arg; + if (type === 'function' || type === 'undefined') return ''; + return format(arg, false, color, shell); +}; + +/** + * Debug print helper + * @param {any} representation + * @api private + */ + +function inspectable(representation) { + const ret = { + inspect: function() { return representation; } + }; + if (util.inspect.custom) { + ret[util.inspect.custom] = ret.inspect; + } + return ret; +} +function map(o) { + return format(o, true); +} +function formatObjectId(x, key) { + x[key] = inspectable('ObjectId("' + x[key].toHexString() + '")'); +} +function formatDate(x, key, shell) { + if (shell) { + x[key] = inspectable('ISODate("' + x[key].toUTCString() + '")'); + } else { + x[key] = inspectable('new Date("' + x[key].toUTCString() + '")'); + } +} +function format(obj, sub, color, shell) { + if (typeof obj?.toBSON === 'function') { + obj = obj.toBSON(); + } + if (obj == null) { + return obj; + } + + const clone = require('../../helpers/clone'); + // `sub` indicates `format()` was called recursively, so skip cloning because we already + // did a deep clone on the top-level object. + let x = sub ? obj : clone(obj, formatToObjectOptions); + const constructorName = getConstructorName(x); + + if (constructorName === 'Binary') { + x = 'BinData(' + x.sub_type + ', "' + x.toString('base64') + '")'; + } else if (constructorName === 'ObjectId') { + x = inspectable('ObjectId("' + x.toHexString() + '")'); + } else if (constructorName === 'Date') { + x = inspectable('new Date("' + x.toUTCString() + '")'); + } else if (constructorName === 'Object') { + const keys = Object.keys(x); + const numKeys = keys.length; + let key; + for (let i = 0; i < numKeys; ++i) { + key = keys[i]; + if (x[key]) { + let error; + if (typeof x[key].toBSON === 'function') { + try { + // `session.toBSON()` throws an error. This means we throw errors + // in debug mode when using transactions, see gh-6712. As a + // workaround, catch `toBSON()` errors, try to serialize without + // `toBSON()`, and rethrow if serialization still fails. + x[key] = x[key].toBSON(); + } catch (_error) { + error = _error; + } + } + const _constructorName = getConstructorName(x[key]); + if (_constructorName === 'Binary') { + x[key] = 'BinData(' + x[key].sub_type + ', "' + + x[key].buffer.toString('base64') + '")'; + } else if (_constructorName === 'Object') { + x[key] = format(x[key], true); + } else if (_constructorName === 'ObjectId') { + formatObjectId(x, key); + } else if (_constructorName === 'Date') { + formatDate(x, key, shell); + } else if (_constructorName === 'ClientSession') { + x[key] = inspectable('ClientSession("' + + (x[key]?.id?.id?.buffer || '').toString('hex') + '")'); + } else if (Array.isArray(x[key])) { + x[key] = x[key].map(map); + } else if (error != null) { + // If there was an error with `toBSON()` and the object wasn't + // already converted to a string representation, rethrow it. + // Open to better ideas on how to handle this. + throw error; + } + } + } + } + if (sub) { + return x; + } + + return util. + inspect(x, false, 10, color). + replace(/\n/g, ''). + replace(/\s{2,}/g, ' '); +} + +/** + * Retrieves information about this collections indexes. + * + * @method getIndexes + * @api public + */ + +NativeCollection.prototype.getIndexes = NativeCollection.prototype.indexInformation; + +/*! + * Module exports. + */ + +module.exports = NativeCollection; diff --git a/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js new file mode 100644 index 0000000..714a70b --- /dev/null +++ b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js @@ -0,0 +1,506 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseConnection = require('../../connection'); +const MongooseError = require('../../error/index'); +const STATES = require('../../connectionState'); +const mongodb = require('mongodb'); +const pkg = require('../../../package.json'); +const processConnectionOptions = require('../../helpers/processConnectionOptions'); +const setTimeout = require('../../helpers/timers').setTimeout; +const utils = require('../../utils'); +const Schema = require('../../schema'); + +/** + * A [node-mongodb-native](https://github.com/mongodb/node-mongodb-native) connection implementation. + * + * @inherits Connection + * @api private + */ + +function NativeConnection() { + MongooseConnection.apply(this, arguments); + this._listening = false; + // Tracks the last time (as unix timestamp) the connection received a + // serverHeartbeatSucceeded or serverHeartbeatFailed event from the underlying MongoClient. + // If we haven't received one in a while (like due to a frozen AWS Lambda container) then + // `readyState` is likely stale. + this._lastHeartbeatAt = null; +} + +/** + * Expose the possible connection states. + * @api public + */ + +NativeConnection.STATES = STATES; + +/*! + * Inherits from Connection. + */ + +Object.setPrototypeOf(NativeConnection.prototype, MongooseConnection.prototype); + +/** + * Switches to a different database using the same connection pool. + * + * Returns a new connection object, with the new db. If you set the `useCache` + * option, `useDb()` will cache connections by `name`. + * + * **Note:** Calling `close()` on a `useDb()` connection will close the base connection as well. + * + * @param {string} name The database name + * @param {object} [options] + * @param {boolean} [options.useCache=false] If true, cache results so calling `useDb()` multiple times with the same name only creates 1 connection object. + * @return {Connection} New Connection Object + * @api public + */ + +NativeConnection.prototype.useDb = function(name, options) { + // Return immediately if cached + options = options || {}; + if (options.useCache && this.relatedDbs[name]) { + return this.relatedDbs[name]; + } + + // we have to manually copy all of the attributes... + const newConn = new this.constructor(); + newConn.name = name; + newConn.base = this.base; + newConn.collections = {}; + newConn.models = {}; + newConn.replica = this.replica; + newConn.config = Object.assign({}, this.config, newConn.config); + newConn.name = this.name; + newConn.options = this.options; + newConn._readyState = this._readyState; + newConn._closeCalled = this._closeCalled; + newConn._hasOpened = this._hasOpened; + newConn._listening = false; + newConn._parent = this; + + newConn.host = this.host; + newConn.port = this.port; + newConn.user = this.user; + newConn.pass = this.pass; + + // First, when we create another db object, we are not guaranteed to have a + // db object to work with. So, in the case where we have a db object and it + // is connected, we can just proceed with setting everything up. However, if + // we do not have a db or the state is not connected, then we need to wait on + // the 'open' event of the connection before doing the rest of the setup + // the 'connected' event is the first time we'll have access to the db object + + const _this = this; + + newConn.client = _this.client; + + if (this.db && this._readyState === STATES.connected) { + wireup(); + } else { + this._queue.push({ fn: wireup }); + } + + function wireup() { + newConn.client = _this.client; + newConn.db = _this.client.db(name); + newConn._lastHeartbeatAt = _this._lastHeartbeatAt; + newConn.onOpen(); + } + + newConn.name = name; + + // push onto the otherDbs stack, this is used when state changes + this.otherDbs.push(newConn); + newConn.otherDbs.push(this); + + // push onto the relatedDbs cache, this is used when state changes + if (options?.useCache) { + this.relatedDbs[newConn.name] = newConn; + newConn.relatedDbs = this.relatedDbs; + } + + return newConn; +}; + +/** + * Runs a [db-level aggregate()](https://www.mongodb.com/docs/manual/reference/method/db.aggregate/) on this connection's underlying `db` + * + * @param {Array} pipeline + * @param {object} [options] + */ + +NativeConnection.prototype.aggregate = function aggregate(pipeline, options) { + return new this.base.Aggregate(null, this).append(pipeline).option(options ?? {}); +}; + +/** + * Removes the database connection with the given name created with `useDb()`. + * + * Throws an error if the database connection was not found. + * + * #### Example: + * + * // Connect to `initialdb` first + * const conn = await mongoose.createConnection('mongodb://127.0.0.1:27017/initialdb').asPromise(); + * + * // Creates an un-cached connection to `mydb` + * const db = conn.useDb('mydb'); + * + * // Closes `db`, and removes `db` from `conn.relatedDbs` and `conn.otherDbs` + * await conn.removeDb('mydb'); + * + * @method removeDb + * @memberOf Connection + * @param {string} name The database name + * @return {Connection} this + */ + +NativeConnection.prototype.removeDb = function removeDb(name) { + const dbs = this.otherDbs.filter(db => db.name === name); + if (!dbs.length) { + throw new MongooseError(`No connections to database "${name}" found`); + } + + for (const db of dbs) { + db._closeCalled = true; + db._destroyCalled = true; + db._readyState = STATES.disconnected; + db.$wasForceClosed = true; + } + delete this.relatedDbs[name]; + this.otherDbs = this.otherDbs.filter(db => db.name !== name); +}; + +/** + * Closes the connection + * + * @param {boolean} [force] + * @return {Connection} this + * @api private + */ + +NativeConnection.prototype.doClose = async function doClose(force) { + if (this.client == null) { + return this; + } + + let skipCloseClient = false; + if (force != null && typeof force === 'object') { + skipCloseClient = force.skipCloseClient; + force = force.force; + } + + if (skipCloseClient) { + return this; + } + + await this.client.close(force); + // Defer because the driver will wait at least 1ms before finishing closing + // the pool, see https://github.com/mongodb-js/mongodb-core/blob/a8f8e4ce41936babc3b9112bf42d609779f03b39/lib/connection/pool.js#L1026-L1030. + // If there's queued operations, you may still get some background work + // after the callback is called. + await new Promise(resolve => setTimeout(resolve, 1)); + + return this; +}; + +/** + * Implementation of `listDatabases()` for MongoDB driver + * + * @return {Promise} + * @api public + */ + +NativeConnection.prototype.listDatabases = async function listDatabases() { + await this._waitForConnect(); + + return await this.db.admin().listDatabases(); +}; + +/*! + * ignore + */ + +NativeConnection.prototype.createClient = async function createClient(uri, options) { + if (typeof uri !== 'string') { + throw new MongooseError('The `uri` parameter to `openUri()` must be a ' + + `string, got "${typeof uri}". Make sure the first parameter to ` + + '`mongoose.connect()` or `mongoose.createConnection()` is a string.'); + } + + if (this._destroyCalled) { + throw new MongooseError( + 'Connection has been closed and destroyed, and cannot be used for re-opening the connection. ' + + 'Please create a new connection with `mongoose.createConnection()` or `mongoose.connect()`.' + ); + } + + if (this.readyState === STATES.connecting || this.readyState === STATES.connected) { + if (this._connectionString !== uri) { + throw new MongooseError('Can\'t call `openUri()` on an active connection with ' + + 'different connection strings. Make sure you aren\'t calling `mongoose.connect()` ' + + 'multiple times. See: https://mongoosejs.com/docs/connections.html#multiple_connections'); + } + } + + options = processConnectionOptions(uri, options); + + if (options) { + + const autoIndex = options.config?.autoIndex ?? options.autoIndex; + if (autoIndex != null) { + this.config.autoIndex = autoIndex !== false; + delete options.config; + delete options.autoIndex; + } + + if ('autoCreate' in options) { + this.config.autoCreate = !!options.autoCreate; + delete options.autoCreate; + } + + if ('sanitizeFilter' in options) { + this.config.sanitizeFilter = options.sanitizeFilter; + delete options.sanitizeFilter; + } + + if ('autoSearchIndex' in options) { + this.config.autoSearchIndex = options.autoSearchIndex; + delete options.autoSearchIndex; + } + + if ('bufferTimeoutMS' in options) { + this.config.bufferTimeoutMS = options.bufferTimeoutMS; + delete options.bufferTimeoutMS; + } + + // Backwards compat + if (options.user || options.pass) { + options.auth = options.auth || {}; + options.auth.username = options.user; + options.auth.password = options.pass; + + this.user = options.user; + this.pass = options.pass; + } + delete options.user; + delete options.pass; + + if (options.bufferCommands != null) { + this.config.bufferCommands = options.bufferCommands; + delete options.bufferCommands; + } + } else { + options = {}; + } + + this._connectionOptions = options; + const dbName = options.dbName; + if (dbName != null) { + this.$dbName = dbName; + } + delete options.dbName; + + if (!utils.hasUserDefinedProperty(options, 'driverInfo')) { + options.driverInfo = { + name: 'Mongoose', + version: pkg.version + }; + } + + const { schemaMap, encryptedFieldsMap } = this._buildEncryptionSchemas(); + + if ((utils.hasOwnKeys(schemaMap) || utils.hasOwnKeys(encryptedFieldsMap)) && !options.autoEncryption) { + throw new Error('Must provide `autoEncryption` when connecting with encrypted schemas.'); + } + + if (utils.hasOwnKeys(schemaMap)) { + options.autoEncryption.schemaMap = schemaMap; + } + + if (utils.hasOwnKeys(encryptedFieldsMap)) { + options.autoEncryption.encryptedFieldsMap = encryptedFieldsMap; + } + + this.readyState = STATES.connecting; + this._connectionString = uri; + + let client; + try { + client = new mongodb.MongoClient(uri, options); + } catch (error) { + this.readyState = STATES.disconnected; + throw error; + } + this.client = client; + + client.setMaxListeners(0); + await client.connect(); + + _setClient(this, client, options, dbName); + + for (const db of this.otherDbs) { + _setClient(db, client, {}, db.name); + } + return this; +}; + +/** + * Given a connection, which may or may not have encrypted models, build + * a schemaMap and/or an encryptedFieldsMap for the connection, combining all models + * into a single schemaMap and encryptedFields map. + * + * @returns {object} the generated schemaMap and encryptedFieldsMap + */ +NativeConnection.prototype._buildEncryptionSchemas = function() { + const qeMappings = {}; + const csfleMappings = {}; + + const encryptedModels = Object.values(this.models).filter(model => model.schema._hasEncryptedFields()); + + // If discriminators are configured for the collection, there might be multiple models + // pointing to the same namespace. For this scenario, we merge all the schemas for each namespace + // into a single schema and then generate a schemaMap/encryptedFieldsMap for the combined schema. + for (const model of encryptedModels) { + const { schema, collection: { collectionName } } = model; + const namespace = `${this.$dbName}.${collectionName}`; + const mappings = schema.encryptionType() === 'csfle' ? csfleMappings : qeMappings; + + mappings[namespace] ??= new Schema({}, { encryptionType: schema.encryptionType() }); + + const isNonRootDiscriminator = schema.discriminatorMapping && !schema.discriminatorMapping.isRoot; + if (isNonRootDiscriminator) { + const rootSchema = schema._baseSchema; + schema.eachPath((pathname) => { + if (rootSchema.path(pathname)) return; + if (!mappings[namespace]._hasEncryptedField(pathname)) return; + + throw new Error(`Cannot have duplicate keys in discriminators with encryption. key=${pathname}`); + }); + } + + mappings[namespace].add(schema); + } + + const schemaMap = Object.fromEntries(Object.entries(csfleMappings).map( + ([namespace, schema]) => ([namespace, schema._buildSchemaMap()]) + )); + + const encryptedFieldsMap = Object.fromEntries(Object.entries(qeMappings).map( + ([namespace, schema]) => ([namespace, schema._buildEncryptedFields()]) + )); + + return { + schemaMap, encryptedFieldsMap + }; +}; + +/*! + * ignore + */ + +NativeConnection.prototype.setClient = function setClient(client) { + if (!(client instanceof mongodb.MongoClient)) { + throw new MongooseError('Must call `setClient()` with an instance of MongoClient'); + } + if (this.readyState !== STATES.disconnected) { + throw new MongooseError('Cannot call `setClient()` on a connection that is already connected.'); + } + if (client.topology == null) { + throw new MongooseError('Cannot call `setClient()` with a MongoClient that you have not called `connect()` on yet.'); + } + + this._connectionString = client.s.url; + _setClient(this, client, {}, client.s.options.dbName); + + for (const model of Object.values(this.models)) { + // Errors handled internally, so safe to ignore error + model.init().catch(function $modelInitNoop() {}); + } + + return this; +}; + +/*! + * ignore + */ + +function _setClient(conn, client, options, dbName) { + const db = dbName != null ? client.db(dbName) : client.db(); + conn.db = db; + conn.client = client; + conn.host = client?.s?.options?.hosts?.[0]?.host; + conn.port = client?.s?.options?.hosts?.[0]?.port; + conn.name = dbName != null ? dbName : db.databaseName; + conn._closeCalled = client._closeCalled; + + const _handleReconnect = () => { + // If we aren't disconnected, we assume this reconnect is due to a + // socket timeout. If there's no activity on a socket for + // `socketTimeoutMS`, the driver will attempt to reconnect and emit + // this event. + if (conn.readyState !== STATES.connected) { + conn.readyState = STATES.connected; + conn.emit('reconnect'); + conn.emit('reconnected'); + conn.onOpen(); + } + }; + + const type = client?.topology?.description?.type || ''; + + if (type === 'Single') { + client.on('serverDescriptionChanged', ev => { + const newDescription = ev.newDescription; + if (newDescription.type === 'Unknown') { + conn.readyState = STATES.disconnected; + } else { + _handleReconnect(); + } + }); + } else if (type.startsWith('ReplicaSet')) { + client.on('topologyDescriptionChanged', ev => { + // Emit disconnected if we've lost connectivity to the primary + const description = ev.newDescription; + if (conn.readyState === STATES.connected && description.type !== 'ReplicaSetWithPrimary') { + // Implicitly emits 'disconnected' + conn.readyState = STATES.disconnected; + } else if (conn.readyState === STATES.disconnected && description.type === 'ReplicaSetWithPrimary') { + _handleReconnect(); + } + }); + } + + conn._lastHeartbeatAt = null; + + client.on('serverHeartbeatSucceeded', () => { + conn._lastHeartbeatAt = Date.now(); + for (const otherDb of conn.otherDbs) { + otherDb._lastHeartbeatAt = conn._lastHeartbeatAt; + } + }); + + if (options.monitorCommands) { + client.on('commandStarted', (data) => conn.emit('commandStarted', data)); + client.on('commandFailed', (data) => conn.emit('commandFailed', data)); + client.on('commandSucceeded', (data) => conn.emit('commandSucceeded', data)); + } + + conn.onOpen(); + + for (const i in conn.collections) { + if (Object.hasOwn(conn.collections, i)) { + conn.collections[i].onOpen(); + } + } +} + +/*! + * Module exports. + */ + +module.exports = NativeConnection; diff --git a/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js new file mode 100644 index 0000000..6274187 --- /dev/null +++ b/backend/node_modules/mongoose/lib/drivers/node-mongodb-native/index.js @@ -0,0 +1,10 @@ +/*! + * Module exports. + */ + +'use strict'; + +exports.BulkWriteResult = require('./bulkWriteResult'); +exports.Collection = require('./collection'); +exports.Connection = require('./connection'); +exports.ClientEncryption = require('mongodb').ClientEncryption; diff --git a/backend/node_modules/mongoose/lib/error/browserMissingSchema.js b/backend/node_modules/mongoose/lib/error/browserMissingSchema.js new file mode 100644 index 0000000..ffeffc7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/browserMissingSchema.js @@ -0,0 +1,29 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * MissingSchema Error constructor. + */ + +class MissingSchemaError extends MongooseError { + + constructor() { + super('Schema hasn\'t been registered for document.\n' + + 'Use mongoose.Document(name, schema)'); + } +} + +Object.defineProperty(MissingSchemaError.prototype, 'name', { + value: 'MongooseError' +}); + +/*! + * exports + */ + +module.exports = MissingSchemaError; diff --git a/backend/node_modules/mongoose/lib/error/bulkSaveIncompleteError.js b/backend/node_modules/mongoose/lib/error/bulkSaveIncompleteError.js new file mode 100644 index 0000000..c4b88e5 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/bulkSaveIncompleteError.js @@ -0,0 +1,44 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + + +/** + * If the underwriting `bulkWrite()` for `bulkSave()` succeeded, but wasn't able to update or + * insert all documents, we throw this error. + * + * @api private + */ + +class MongooseBulkSaveIncompleteError extends MongooseError { + constructor(modelName, documents, bulkWriteResult) { + const matchedCount = bulkWriteResult?.matchedCount ?? 0; + const insertedCount = bulkWriteResult?.insertedCount ?? 0; + let preview = documents.map(doc => doc._id).join(', '); + if (preview.length > 100) { + preview = preview.slice(0, 100) + '...'; + } + + const numDocumentsNotUpdated = documents.length - matchedCount - insertedCount; + super(`${modelName}.bulkSave() was not able to update ${numDocumentsNotUpdated} of the given documents due to incorrect version or optimistic concurrency, document ids: ${preview}`); + + this.modelName = modelName; + this.documents = documents; + this.bulkWriteResult = bulkWriteResult; + this.numDocumentsNotUpdated = numDocumentsNotUpdated; + } +} + +Object.defineProperty(MongooseBulkSaveIncompleteError.prototype, 'name', { + value: 'MongooseBulkSaveIncompleteError' +}); + +/*! + * exports + */ + +module.exports = MongooseBulkSaveIncompleteError; diff --git a/backend/node_modules/mongoose/lib/error/bulkWriteError.js b/backend/node_modules/mongoose/lib/error/bulkWriteError.js new file mode 100644 index 0000000..1711b03 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/bulkWriteError.js @@ -0,0 +1,41 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./'); + + +/** + * If `bulkWrite()` or `insertMany()` has validation errors, but + * all valid operations succeed, and 'throwOnValidationError' is true, + * Mongoose will throw this error. + * + * @api private + */ + +class MongooseBulkWriteError extends MongooseError { + constructor(validationErrors, results, rawResult, operation) { + let preview = validationErrors.map(e => e.message).join(', '); + if (preview.length > 200) { + preview = preview.slice(0, 200) + '...'; + } + super(`${operation} failed with ${validationErrors.length} Mongoose validation errors: ${preview}`); + + this.validationErrors = validationErrors; + this.results = results; + this.rawResult = rawResult; + this.operation = operation; + } +} + +Object.defineProperty(MongooseBulkWriteError.prototype, 'name', { + value: 'MongooseBulkWriteError' +}); + +/*! + * exports + */ + +module.exports = MongooseBulkWriteError; diff --git a/backend/node_modules/mongoose/lib/error/cast.js b/backend/node_modules/mongoose/lib/error/cast.js new file mode 100644 index 0000000..6c0df99 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/cast.js @@ -0,0 +1,158 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); +const util = require('util'); + +/** + * Casting Error constructor. + * + * @param {string} type + * @param {string} value + * @inherits MongooseError + * @api private + */ + +class CastError extends MongooseError { + constructor(type, value, path, reason, schemaType) { + // If no args, assume we'll `init()` later. + if (arguments.length > 0) { + const valueType = getValueType(value); + const messageFormat = getMessageFormat(schemaType); + const msg = formatMessage(null, type, value, path, messageFormat, valueType, reason); + super(msg); + this.init(type, value, path, reason, schemaType); + } else { + super(formatMessage()); + } + } + + toJSON() { + return { + stringValue: this.stringValue, + valueType: this.valueType, + kind: this.kind, + value: this.value, + path: this.path, + reason: this.reason, + name: this.name, + message: this.message + }; + } + /*! + * ignore + */ + init(type, value, path, reason, schemaType) { + this.stringValue = getStringValue(value); + this.messageFormat = getMessageFormat(schemaType); + this.kind = type; + this.value = value; + this.path = path; + this.reason = reason; + this.valueType = getValueType(value); + } + + /** + * ignore + * @param {Readonly} other + * @api private + */ + copy(other) { + this.messageFormat = other.messageFormat; + this.stringValue = other.stringValue; + this.kind = other.kind; + this.value = other.value; + this.path = other.path; + this.reason = other.reason; + this.message = other.message; + this.valueType = other.valueType; + } + + /*! + * ignore + */ + setModel(model) { + this.message = formatMessage(model, this.kind, this.value, this.path, + this.messageFormat, this.valueType); + } +} + +Object.defineProperty(CastError.prototype, 'name', { + value: 'CastError' +}); + +function getStringValue(value) { + let stringValue = util.inspect(value); + stringValue = stringValue.replace(/^'|'$/g, '"'); + if (!stringValue.startsWith('"')) { + stringValue = '"' + stringValue + '"'; + } + return stringValue; +} + +function getValueType(value) { + if (value == null) { + return '' + value; + } + + const t = typeof value; + if (t !== 'object') { + return t; + } + if (typeof value.constructor !== 'function') { + return t; + } + return value.constructor.name; +} + +function getMessageFormat(schemaType) { + const messageFormat = schemaType?._castErrorMessage || null; + if (typeof messageFormat === 'string' || typeof messageFormat === 'function') { + return messageFormat; + } +} + +/*! + * ignore + */ + +function formatMessage(model, kind, value, path, messageFormat, valueType, reason) { + if (typeof messageFormat === 'string') { + const stringValue = getStringValue(value); + let ret = messageFormat. + replace('{KIND}', kind). + replace('{VALUE}', stringValue). + replace('{PATH}', path); + if (model != null) { + ret = ret.replace('{MODEL}', model.modelName); + } + + return ret; + } else if (typeof messageFormat === 'function') { + return messageFormat(value, path, model, kind); + } else { + const stringValue = getStringValue(value); + const valueTypeMsg = valueType ? ' (type ' + valueType + ')' : ''; + let ret = 'Cast to ' + kind + ' failed for value ' + + stringValue + valueTypeMsg + ' at path "' + path + '"'; + if (model != null) { + ret += ' for model "' + model.modelName + '"'; + } + if (reason != null && + typeof reason.constructor === 'function' && + reason.constructor.name !== 'AssertionError' && + reason.constructor.name !== 'Error') { + ret += ' because of "' + reason.constructor.name + '"'; + } + return ret; + } +} + +/*! + * exports + */ + +module.exports = CastError; diff --git a/backend/node_modules/mongoose/lib/error/createCollectionsError.js b/backend/node_modules/mongoose/lib/error/createCollectionsError.js new file mode 100644 index 0000000..e82ab22 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/createCollectionsError.js @@ -0,0 +1,26 @@ +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * createCollections Error constructor + * + * @param {string} message + * @param {string} errorsMap + * @inherits MongooseError + * @api private + */ + +class CreateCollectionsError extends MongooseError { + constructor(message, errorsMap) { + super(message); + this.errors = errorsMap; + } +} + +Object.defineProperty(CreateCollectionsError.prototype, 'name', { + value: 'CreateCollectionsError' +}); + +module.exports = CreateCollectionsError; + diff --git a/backend/node_modules/mongoose/lib/error/divergentArray.js b/backend/node_modules/mongoose/lib/error/divergentArray.js new file mode 100644 index 0000000..7b6f910 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/divergentArray.js @@ -0,0 +1,40 @@ + +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * DivergentArrayError constructor. + * @param {string[]} paths + * @api private + */ + +class DivergentArrayError extends MongooseError { + + constructor(paths) { + const msg = 'For your own good, using `document.save()` to update an array ' + + 'which was selected using an $elemMatch projection OR ' + + 'populated using skip, limit, query conditions, or exclusion of ' + + 'the _id field when the operation results in a $pop or $set of ' + + 'the entire array is not supported. The following ' + + 'path(s) would have been modified unsafely:\n' + + ' ' + paths.join('\n ') + '\n' + + 'Use Model.updateOne() to update these arrays instead. ' + + 'See https://mongoosejs.com/docs/faq.html#divergent-array-error for more information.'; + super(msg); + } +} + +Object.defineProperty(DivergentArrayError.prototype, 'name', { + value: 'DivergentArrayError' +}); + +/*! + * exports + */ + +module.exports = DivergentArrayError; diff --git a/backend/node_modules/mongoose/lib/error/eachAsyncMultiError.js b/backend/node_modules/mongoose/lib/error/eachAsyncMultiError.js new file mode 100644 index 0000000..b732437 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/eachAsyncMultiError.js @@ -0,0 +1,41 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + + +/** + * If `eachAsync()` is called with `continueOnError: true`, there can be + * multiple errors. This error class contains an `errors` property, which + * contains an array of all errors that occurred in `eachAsync()`. + * + * @api private + */ + +class EachAsyncMultiError extends MongooseError { + /** + * @param {string} connectionString + */ + constructor(errors) { + let preview = errors.map(e => e.message).join(', '); + if (preview.length > 50) { + preview = preview.slice(0, 50) + '...'; + } + super(`eachAsync() finished with ${errors.length} errors: ${preview}`); + + this.errors = errors; + } +} + +Object.defineProperty(EachAsyncMultiError.prototype, 'name', { + value: 'EachAsyncMultiError' +}); + +/*! + * exports + */ + +module.exports = EachAsyncMultiError; diff --git a/backend/node_modules/mongoose/lib/error/index.js b/backend/node_modules/mongoose/lib/error/index.js new file mode 100644 index 0000000..f6266c6 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/index.js @@ -0,0 +1,237 @@ +'use strict'; + +/** + * MongooseError constructor. MongooseError is the base class for all + * Mongoose-specific errors. + * + * #### Example: + * + * const Model = mongoose.model('Test', new mongoose.Schema({ answer: Number })); + * const doc = new Model({ answer: 'not a number' }); + * const err = doc.validateSync(); + * + * err instanceof mongoose.Error.ValidationError; // true + * + * @constructor Error + * @param {string} msg Error message + * @inherits Error https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error + */ + +const MongooseError = require('./mongooseError'); + +/** + * The name of the error. The name uniquely identifies this Mongoose error. The + * possible values are: + * + * - `MongooseError`: general Mongoose error + * - `CastError`: Mongoose could not convert a value to the type defined in the schema path. May be in a `ValidationError` class' `errors` property. + * - `DivergentArrayError`: You attempted to `save()` an array that was modified after you loaded it with a `$elemMatch` or similar projection + * - `MissingSchemaError`: You tried to access a model with [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.model()) that was not defined + * - `DocumentNotFoundError`: The document you tried to [`save()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.save()) was not found + * - `ValidatorError`: error from an individual schema path's validator + * - `ValidationError`: error returned from [`validate()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) or [`validateSync()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.validateSync()). Contains zero or more `ValidatorError` instances in `.errors` property. + * - `MissingSchemaError`: You called `mongoose.Document()` without a schema + * - `ObjectExpectedError`: Thrown when you set a nested path to a non-object value with [strict mode set](https://mongoosejs.com/docs/guide.html#strict). + * - `ObjectParameterError`: Thrown when you pass a non-object value to a function which expects an object as a paramter + * - `OverwriteModelError`: Thrown when you call [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.model()) to re-define a model that was already defined. + * - `ParallelSaveError`: Thrown when you call [`save()`](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) on a document when the same document instance is already saving. + * - `StrictModeError`: Thrown when you set a path that isn't the schema and [strict mode](https://mongoosejs.com/docs/guide.html#strict) is set to `throw`. + * - `VersionError`: Thrown when the [document is out of sync](https://mongoosejs.com/docs/guide.html#versionKey) + * + * @api public + * @property {string} name + * @memberOf Error + * @instance + */ + +/*! + * Module exports. + */ + +module.exports = exports = MongooseError; + +/** + * The default built-in validator error messages. + * + * @see Error.messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + * @memberOf Error + * @static + */ + +MongooseError.messages = require('./messages'); + +// backward compat +MongooseError.Messages = MongooseError.messages; + +/** + * An instance of this error class will be thrown when mongoose failed to + * cast a value. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.CastError = require('./cast'); + +/** + * An instance of this error class will be thrown when `save()` fails + * because the underlying + * document was not found. The constructor takes one parameter, the + * conditions that mongoose passed to `updateOne()` when trying to update + * the document. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.DocumentNotFoundError = require('./notFound'); + +/** + * An instance of this error class will be thrown when [validation](https://mongoosejs.com/docs/validation.html) failed. + * The `errors` property contains an object whose keys are the paths that failed and whose values are + * instances of CastError or ValidationError. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.ValidationError = require('./validation'); + +/** + * A `ValidationError` has a hash of `errors` that contain individual + * `ValidatorError` instances. + * + * #### Example: + * + * const schema = Schema({ name: { type: String, required: true } }); + * const Model = mongoose.model('Test', schema); + * const doc = new Model({}); + * + * // Top-level error is a ValidationError, **not** a ValidatorError + * const err = doc.validateSync(); + * err instanceof mongoose.Error.ValidationError; // true + * + * // A ValidationError `err` has 0 or more ValidatorErrors keyed by the + * // path in the `err.errors` property. + * err.errors['name'] instanceof mongoose.Error.ValidatorError; + * + * err.errors['name'].kind; // 'required' + * err.errors['name'].path; // 'name' + * err.errors['name'].value; // undefined + * + * Instances of `ValidatorError` have the following properties: + * + * - `kind`: The validator's `type`, like `'required'` or `'regexp'` + * - `path`: The path that failed validation + * - `value`: The value that failed validation + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.ValidatorError = require('./validator'); + +/** + * An instance of this error class will be thrown when you call `save()` after + * the document in the database was changed in a potentially unsafe way. See + * the [`versionKey` option](https://mongoosejs.com/docs/guide.html#versionKey) for more information. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.VersionError = require('./version'); + +/** + * An instance of this error class will be thrown when you call `save()` multiple + * times on the same document in parallel. See the [FAQ](https://mongoosejs.com/docs/faq.html) for more + * information. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.ParallelSaveError = require('./parallelSave'); + +/** + * Thrown when a model with the given name was already registered on the connection. + * See [the FAQ about `OverwriteModelError`](https://mongoosejs.com/docs/faq.html#overwrite-model-error). + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.OverwriteModelError = require('./overwriteModel'); + +/** + * Thrown when you try to access a model that has not been registered yet + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.MissingSchemaError = require('./missingSchema'); + +/** + * Thrown when some documents failed to save when calling `bulkSave()` + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.MongooseBulkSaveIncompleteError = require('./bulkSaveIncompleteError'); + +/** + * Thrown when the MongoDB Node driver can't connect to a valid server + * to send an operation to. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.MongooseServerSelectionError = require('./serverSelection'); + +/** + * An instance of this error will be thrown if you used an array projection + * and then modified the array in an unsafe way. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.DivergentArrayError = require('./divergentArray'); + +/** + * Thrown when your try to pass values to model constructor that + * were not specified in schema or change immutable properties when + * `strict` mode is `"throw"` + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.StrictModeError = require('./strict'); + +/** + * An instance of this error class will be returned when mongoose failed to + * populate with a path that is not existing. + * + * @api public + * @memberOf Error + * @static + */ + +MongooseError.StrictPopulateError = require('./strictPopulate'); diff --git a/backend/node_modules/mongoose/lib/error/invalidSchemaOption.js b/backend/node_modules/mongoose/lib/error/invalidSchemaOption.js new file mode 100644 index 0000000..5d09419 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/invalidSchemaOption.js @@ -0,0 +1,32 @@ + +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * InvalidSchemaOption Error constructor. + * @param {string} name + * @api private + */ + +class InvalidSchemaOptionError extends MongooseError { + + constructor(name, option) { + const msg = `Cannot create use schema for property "${name}" because the schema has the ${option} option enabled.`; + super(msg); + } +} + +Object.defineProperty(InvalidSchemaOptionError.prototype, 'name', { + value: 'InvalidSchemaOptionError' +}); + +/*! + * exports + */ + +module.exports = InvalidSchemaOptionError; diff --git a/backend/node_modules/mongoose/lib/error/messages.js b/backend/node_modules/mongoose/lib/error/messages.js new file mode 100644 index 0000000..94a239d --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/messages.js @@ -0,0 +1,47 @@ + +/** + * The default built-in validator error messages. These may be customized. + * + * // customize within each schema or globally like so + * const mongoose = require('mongoose'); + * mongoose.Error.messages.String.enum = "Your custom message for {PATH}."; + * + * Error messages support basic templating. Mongoose will replace the following strings with the corresponding value. + * + * - `{PATH}` is replaced with the invalid document path + * - `{VALUE}` is replaced with the invalid value + * - `{TYPE}` is replaced with the validator type such as "regexp", "min", or "user defined" + * - `{MIN}` is replaced with the declared min value for the Number.min validator + * - `{MAX}` is replaced with the declared max value for the Number.max validator + * + * Click the "show code" link below to see all defaults. + * + * @static + * @memberOf MongooseError + * @api public + */ + +'use strict'; + +const msg = module.exports = exports = {}; + +msg.DocumentNotFoundError = null; + +msg.general = {}; +msg.general.default = 'Validator failed for path `{PATH}` with value `{VALUE}`'; +msg.general.required = 'Path `{PATH}` is required.'; + +msg.Number = {}; +msg.Number.min = 'Path `{PATH}` ({VALUE}) is less than minimum allowed value ({MIN}).'; +msg.Number.max = 'Path `{PATH}` ({VALUE}) is more than maximum allowed value ({MAX}).'; +msg.Number.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.'; + +msg.Date = {}; +msg.Date.min = 'Path `{PATH}` ({VALUE}) is before minimum allowed value ({MIN}).'; +msg.Date.max = 'Path `{PATH}` ({VALUE}) is after maximum allowed value ({MAX}).'; + +msg.String = {}; +msg.String.enum = '`{VALUE}` is not a valid enum value for path `{PATH}`.'; +msg.String.match = 'Path `{PATH}` is invalid ({VALUE}).'; +msg.String.minlength = 'Path `{PATH}` (`{VALUE}`, length {LENGTH}) is shorter than the minimum allowed length ({MINLENGTH}).'; +msg.String.maxlength = 'Path `{PATH}` (`{VALUE}`, length {LENGTH}) is longer than the maximum allowed length ({MAXLENGTH}).'; diff --git a/backend/node_modules/mongoose/lib/error/missingSchema.js b/backend/node_modules/mongoose/lib/error/missingSchema.js new file mode 100644 index 0000000..de21f25 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/missingSchema.js @@ -0,0 +1,33 @@ + +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * MissingSchema Error constructor. + * @param {string} name + * @api private + */ + +class MissingSchemaError extends MongooseError { + + constructor(name) { + const msg = 'Schema hasn\'t been registered for model "' + name + '".\n' + + 'Use mongoose.model(name, schema)'; + super(msg); + } +} + +Object.defineProperty(MissingSchemaError.prototype, 'name', { + value: 'MissingSchemaError' +}); + +/*! + * exports + */ + +module.exports = MissingSchemaError; diff --git a/backend/node_modules/mongoose/lib/error/mongooseError.js b/backend/node_modules/mongoose/lib/error/mongooseError.js new file mode 100644 index 0000000..5505105 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/mongooseError.js @@ -0,0 +1,13 @@ +'use strict'; + +/*! + * ignore + */ + +class MongooseError extends Error { } + +Object.defineProperty(MongooseError.prototype, 'name', { + value: 'MongooseError' +}); + +module.exports = MongooseError; diff --git a/backend/node_modules/mongoose/lib/error/notFound.js b/backend/node_modules/mongoose/lib/error/notFound.js new file mode 100644 index 0000000..87fdd8b --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/notFound.js @@ -0,0 +1,47 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); +const util = require('util'); + +/** + * OverwriteModel Error constructor. + * @api private + */ + +class DocumentNotFoundError extends MongooseError { + + constructor(filter, model, numAffected, result) { + let msg; + const messages = MongooseError.messages; + if (messages.DocumentNotFoundError != null) { + msg = typeof messages.DocumentNotFoundError === 'function' ? + messages.DocumentNotFoundError(filter, model) : + messages.DocumentNotFoundError; + } else { + msg = 'No document found for query "' + util.inspect(filter) + + '" on model "' + model + '"'; + } + + super(msg); + + this.result = result; + this.numAffected = numAffected; + this.filter = filter; + // Backwards compat + this.query = filter; + } +} + +Object.defineProperty(DocumentNotFoundError.prototype, 'name', { + value: 'DocumentNotFoundError' +}); + +/*! + * exports + */ + +module.exports = DocumentNotFoundError; diff --git a/backend/node_modules/mongoose/lib/error/objectExpected.js b/backend/node_modules/mongoose/lib/error/objectExpected.js new file mode 100644 index 0000000..bd89ffc --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/objectExpected.js @@ -0,0 +1,31 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * Strict mode error constructor + * + * @param {string} type + * @param {string} value + * @api private + */ + +class ObjectExpectedError extends MongooseError { + + constructor(path, val) { + const typeDescription = Array.isArray(val) ? 'array' : 'primitive value'; + super('Tried to set nested object field `' + path + + `\` to ${typeDescription} \`` + val + '`'); + this.path = path; + } +} + +Object.defineProperty(ObjectExpectedError.prototype, 'name', { + value: 'ObjectExpectedError' +}); + +module.exports = ObjectExpectedError; diff --git a/backend/node_modules/mongoose/lib/error/objectParameter.js b/backend/node_modules/mongoose/lib/error/objectParameter.js new file mode 100644 index 0000000..9070acf --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/objectParameter.js @@ -0,0 +1,31 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * Constructor for errors that happen when a parameter that's expected to be + * an object isn't an object + * + * @param {any} value + * @param {string} paramName + * @param {string} fnName + * @api private + */ + +class ObjectParameterError extends MongooseError { + constructor(value, paramName, fnName) { + super('Parameter "' + paramName + '" to ' + fnName + + '() must be an object, got "' + (value == null ? value : value.toString()) + '" (type ' + typeof value + ')'); + } +} + + +Object.defineProperty(ObjectParameterError.prototype, 'name', { + value: 'ObjectParameterError' +}); + +module.exports = ObjectParameterError; diff --git a/backend/node_modules/mongoose/lib/error/overwriteModel.js b/backend/node_modules/mongoose/lib/error/overwriteModel.js new file mode 100644 index 0000000..b013d28 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/overwriteModel.js @@ -0,0 +1,31 @@ + +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * OverwriteModel Error constructor. + * @param {string} name + * @api private + */ + +class OverwriteModelError extends MongooseError { + + constructor(name) { + super('Cannot overwrite `' + name + '` model once compiled.'); + } +} + +Object.defineProperty(OverwriteModelError.prototype, 'name', { + value: 'OverwriteModelError' +}); + +/*! + * exports + */ + +module.exports = OverwriteModelError; diff --git a/backend/node_modules/mongoose/lib/error/parallelSave.js b/backend/node_modules/mongoose/lib/error/parallelSave.js new file mode 100644 index 0000000..fd554fa --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/parallelSave.js @@ -0,0 +1,33 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); + + +/** + * ParallelSave Error constructor. + * + * @param {Document} doc + * @api private + */ + +class ParallelSaveError extends MongooseError { + + constructor(doc) { + const msg = 'Can\'t save() the same doc multiple times in parallel. Document: '; + super(msg + doc._doc._id); + } +} + +Object.defineProperty(ParallelSaveError.prototype, 'name', { + value: 'ParallelSaveError' +}); + +/*! + * exports + */ + +module.exports = ParallelSaveError; diff --git a/backend/node_modules/mongoose/lib/error/parallelValidate.js b/backend/node_modules/mongoose/lib/error/parallelValidate.js new file mode 100644 index 0000000..d70e296 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/parallelValidate.js @@ -0,0 +1,33 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); + + +/** + * ParallelValidate Error constructor. + * + * @param {Document} doc + * @api private + */ + +class ParallelValidateError extends MongooseError { + + constructor(doc) { + const msg = 'Can\'t validate() the same doc multiple times in parallel. Document: '; + super(msg + doc._doc._id); + } +} + +Object.defineProperty(ParallelValidateError.prototype, 'name', { + value: 'ParallelValidateError' +}); + +/*! + * exports + */ + +module.exports = ParallelValidateError; diff --git a/backend/node_modules/mongoose/lib/error/serverSelection.js b/backend/node_modules/mongoose/lib/error/serverSelection.js new file mode 100644 index 0000000..493ac46 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/serverSelection.js @@ -0,0 +1,62 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); +const allServersUnknown = require('../helpers/topology/allServersUnknown'); +const isAtlas = require('../helpers/topology/isAtlas'); +const isSSLError = require('../helpers/topology/isSSLError'); + +/*! + * ignore + */ + +const atlasMessage = 'Could not connect to any servers in your MongoDB Atlas cluster. ' + + 'One common reason is that you\'re trying to access the database from ' + + 'an IP that isn\'t whitelisted. Make sure your current IP address is on your Atlas ' + + 'cluster\'s IP whitelist: https://www.mongodb.com/docs/atlas/security-whitelist/'; + +const sslMessage = 'Mongoose is connecting with SSL enabled, but the server is ' + + 'not accepting SSL connections. Please ensure that the MongoDB server you are ' + + 'connecting to is configured to accept SSL connections. Learn more: ' + + 'https://mongoosejs.com/docs/tutorials/ssl.html'; + +class MongooseServerSelectionError extends MongooseError { + /** + * MongooseServerSelectionError constructor + * + * @api private + */ + assimilateError(err) { + const reason = err.reason; + // Special message for a case that is likely due to IP whitelisting issues. + const isAtlasWhitelistError = isAtlas(reason) && + allServersUnknown(reason) && + err.message.indexOf('bad auth') === -1 && + err.message.indexOf('Authentication failed') === -1; + + if (isAtlasWhitelistError) { + this.message = atlasMessage; + } else if (isSSLError(reason)) { + this.message = sslMessage; + } else { + this.message = err.message; + } + for (const key in err) { + if (key !== 'name') { + this[key] = err[key]; + } + } + this.cause = reason; + + return this; + } +} + +Object.defineProperty(MongooseServerSelectionError.prototype, 'name', { + value: 'MongooseServerSelectionError' +}); + +module.exports = MongooseServerSelectionError; diff --git a/backend/node_modules/mongoose/lib/error/setOptionError.js b/backend/node_modules/mongoose/lib/error/setOptionError.js new file mode 100644 index 0000000..6644fa7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/setOptionError.js @@ -0,0 +1,103 @@ +/*! + * Module requirements + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); +const util = require('util'); +const combinePathErrors = require('../helpers/error/combinePathErrors'); + +/** + * Mongoose.set Error + * + * @api private + * @inherits MongooseError + */ + +class SetOptionError extends MongooseError { + + constructor() { + super(''); + + this.errors = {}; + } + + /** + * Console.log helper + */ + toString() { + return combinePathErrors(this); + } + + /** + * inspect helper + * @api private + */ + inspect() { + return Object.assign(new Error(this.message), this); + } + + /** + * add message + * @param {string} key + * @param {string|Error} error + * @api private + */ + addError(key, error) { + if (error instanceof SetOptionError) { + const { errors } = error; + for (const optionKey of Object.keys(errors)) { + this.addError(optionKey, errors[optionKey]); + } + + return; + } + + this.errors[key] = error; + this.message = combinePathErrors(this); + } +} + + +if (util.inspect.custom) { + // Avoid Node deprecation warning DEP0079 + SetOptionError.prototype[util.inspect.custom] = SetOptionError.prototype.inspect; +} + +/** + * Helper for JSON.stringify + * Ensure `name` and `message` show up in toJSON output re: gh-9847 + * @api private + */ +Object.defineProperty(SetOptionError.prototype, 'toJSON', { + enumerable: false, + writable: false, + configurable: true, + value: function() { + return Object.assign({}, this, { name: this.name, message: this.message }); + } +}); + + +Object.defineProperty(SetOptionError.prototype, 'name', { + value: 'SetOptionError' +}); + +class SetOptionInnerError extends MongooseError { + /** + * Error for the "errors" array in "SetOptionError" with consistent message + * @param {string} key + */ + constructor(key) { + super(`"${key}" is not a valid option to set`); + } +} + +SetOptionError.SetOptionInnerError = SetOptionInnerError; + +/*! + * Module exports + */ + +module.exports = SetOptionError; diff --git a/backend/node_modules/mongoose/lib/error/strict.js b/backend/node_modules/mongoose/lib/error/strict.js new file mode 100644 index 0000000..039a0bb --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/strict.js @@ -0,0 +1,35 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * Strict mode error constructor + * + * @param {string} path + * @param {string} [msg] + * @param {boolean} [immutable] + * @inherits MongooseError + * @api private + */ + + +class StrictModeError extends MongooseError { + + constructor(path, msg, immutable) { + msg = msg || 'Field `' + path + '` is not in schema and strict ' + + 'mode is set to throw.'; + super(msg); + this.isImmutableError = !!immutable; + this.path = path; + } +} + +Object.defineProperty(StrictModeError.prototype, 'name', { + value: 'StrictModeError' +}); + +module.exports = StrictModeError; diff --git a/backend/node_modules/mongoose/lib/error/strictPopulate.js b/backend/node_modules/mongoose/lib/error/strictPopulate.js new file mode 100644 index 0000000..a711385 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/strictPopulate.js @@ -0,0 +1,31 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * Strict mode error constructor + * + * @param {string} path + * @param {string} [msg] + * @inherits MongooseError + * @api private + */ + +class StrictPopulateError extends MongooseError { + + constructor(path, msg) { + msg = msg || 'Cannot populate path `' + path + '` because it is not in your schema. ' + 'Set the `strictPopulate` option to false to override.'; + super(msg); + this.path = path; + } +} + +Object.defineProperty(StrictPopulateError.prototype, 'name', { + value: 'StrictPopulateError' +}); + +module.exports = StrictPopulateError; diff --git a/backend/node_modules/mongoose/lib/error/syncIndexes.js b/backend/node_modules/mongoose/lib/error/syncIndexes.js new file mode 100644 index 0000000..fc1ca2d --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/syncIndexes.js @@ -0,0 +1,30 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); + +/** + * SyncIndexes Error constructor. + * + * @param {string} message + * @param {string} errorsMap + * @inherits MongooseError + * @api private + */ + +class SyncIndexesError extends MongooseError { + constructor(message, errorsMap) { + super(message); + this.errors = errorsMap; + } +} + +Object.defineProperty(SyncIndexesError.prototype, 'name', { + value: 'SyncIndexesError' +}); + + +module.exports = SyncIndexesError; diff --git a/backend/node_modules/mongoose/lib/error/validation.js b/backend/node_modules/mongoose/lib/error/validation.js new file mode 100644 index 0000000..5da1cbe --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/validation.js @@ -0,0 +1,97 @@ +/*! + * Module requirements + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); +const getConstructorName = require('../helpers/getConstructorName'); +const util = require('util'); +const combinePathErrors = require('../helpers/error/combinePathErrors'); + +/** + * Document Validation Error + * + * @api private + * @param {Document} [instance] + * @inherits MongooseError + */ + +class ValidationError extends MongooseError { + + constructor(instance) { + let _message; + if (getConstructorName(instance) === 'model') { + _message = instance.constructor.modelName + ' validation failed'; + } else { + _message = 'Validation failed'; + } + + super(_message); + + this.errors = {}; + this._message = _message; + + if (instance) { + instance.$errors = this.errors; + } + } + + /** + * Console.log helper + */ + toString() { + return this.name + ': ' + combinePathErrors(this); + } + + /** + * add message + * @param {string} path + * @param {string|Error} error + * @api private + */ + addError(path, error) { + if (error instanceof ValidationError) { + const { errors } = error; + for (const errorPath of Object.keys(errors)) { + this.addError(`${path}.${errorPath}`, errors[errorPath]); + } + + return; + } + + this.errors[path] = error; + this.message = this._message + ': ' + combinePathErrors(this); + } +} + + +if (util.inspect.custom) { + // Avoid Node deprecation warning DEP0079 + ValidationError.prototype[util.inspect.custom] = ValidationError.prototype.inspect; +} + +/** + * Helper for JSON.stringify + * Ensure `name` and `message` show up in toJSON output re: gh-9847 + * @api private + */ +Object.defineProperty(ValidationError.prototype, 'toJSON', { + enumerable: false, + writable: false, + configurable: true, + value: function() { + return Object.assign({}, this, { name: this.name, message: this.message }); + } +}); + + +Object.defineProperty(ValidationError.prototype, 'name', { + value: 'ValidationError' +}); + +/*! + * Module exports + */ + +module.exports = ValidationError; diff --git a/backend/node_modules/mongoose/lib/error/validator.js b/backend/node_modules/mongoose/lib/error/validator.js new file mode 100644 index 0000000..4ada301 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/validator.js @@ -0,0 +1,100 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('./mongooseError'); + +/** + * Schema validator error + * + * @param {object} properties + * @param {Document} doc + * @api private + */ + +class ValidatorError extends MongooseError { + + constructor(properties, doc) { + let msg = properties.message; + if (!msg) { + msg = MongooseError.messages.general.default; + } + + const message = formatMessage(msg, properties, doc); + super(message); + + properties = Object.assign({}, properties, { message: message }); + this.properties = properties; + this.kind = properties.type; + this.path = properties.path; + this.value = properties.value; + this.reason = properties.reason; + } + + /** + * toString helper + * TODO remove? This defaults to `${this.name}: ${this.message}` + * @api private + */ + toString() { + return this.message; + } + + /** + * Ensure `name` and `message` show up in toJSON output re: gh-9296 + * @api private + */ + + toJSON() { + return Object.assign({ name: this.name, message: this.message }, this); + } +} + + +Object.defineProperty(ValidatorError.prototype, 'name', { + value: 'ValidatorError' +}); + +/** + * The object used to define this validator. Not enumerable to hide + * it from `require('util').inspect()` output re: gh-3925 + * @api private + */ + +Object.defineProperty(ValidatorError.prototype, 'properties', { + enumerable: false, + writable: true, + value: null +}); + +// Exposed for testing +ValidatorError.prototype.formatMessage = formatMessage; + +/** + * Formats error messages + * @api private + */ + +function formatMessage(msg, properties, doc) { + if (typeof msg === 'function') { + return msg(properties, doc); + } + + const propertyNames = Object.keys(properties); + for (const propertyName of propertyNames) { + if (propertyName === 'message') { + continue; + } + msg = msg.replace('{' + propertyName.toUpperCase() + '}', properties[propertyName]); + } + + return msg; +} + +/*! + * exports + */ + +module.exports = ValidatorError; diff --git a/backend/node_modules/mongoose/lib/error/version.js b/backend/node_modules/mongoose/lib/error/version.js new file mode 100644 index 0000000..67ad2c3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/error/version.js @@ -0,0 +1,38 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./mongooseError'); + +/** + * Version Error constructor. + * + * @param {Document} doc + * @param {number} currentVersion + * @param {string[]} modifiedPaths + * @api private + */ + +class VersionError extends MongooseError { + + constructor(doc, currentVersion, modifiedPaths) { + const modifiedPathsStr = modifiedPaths.join(', '); + super('No matching document found for id "' + doc._doc._id + + '" version ' + currentVersion + ' modifiedPaths "' + modifiedPathsStr + '"'); + this.version = currentVersion; + this.modifiedPaths = modifiedPaths; + } +} + + +Object.defineProperty(VersionError.prototype, 'name', { + value: 'VersionError' +}); + +/*! + * exports + */ + +module.exports = VersionError; diff --git a/backend/node_modules/mongoose/lib/helpers/aggregate/prepareDiscriminatorPipeline.js b/backend/node_modules/mongoose/lib/helpers/aggregate/prepareDiscriminatorPipeline.js new file mode 100644 index 0000000..e8e8640 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/aggregate/prepareDiscriminatorPipeline.js @@ -0,0 +1,39 @@ +'use strict'; + +module.exports = function prepareDiscriminatorPipeline(pipeline, schema, prefix) { + const discriminatorMapping = schema?.discriminatorMapping; + prefix = prefix || ''; + + if (discriminatorMapping && !discriminatorMapping.isRoot) { + const originalPipeline = pipeline; + const filterKey = (prefix.length > 0 ? prefix + '.' : prefix) + discriminatorMapping.key; + const discriminatorValue = discriminatorMapping.value; + + // If the first pipeline stage is a match and it doesn't specify a `__t` + // key, add the discriminator key to it. This allows for potential + // aggregation query optimizations not to be disturbed by this feature. + if (originalPipeline[0] != null && + originalPipeline[0].$match && + (originalPipeline[0].$match[filterKey] === undefined || originalPipeline[0].$match[filterKey] === discriminatorValue)) { + originalPipeline[0].$match[filterKey] = discriminatorValue; + // `originalPipeline` is a ref, so there's no need for + // aggregate._pipeline = originalPipeline + } else if (originalPipeline[0]?.$geoNear) { + originalPipeline[0].$geoNear.query = + originalPipeline[0].$geoNear.query || {}; + originalPipeline[0].$geoNear.query[filterKey] = discriminatorValue; + } else if (originalPipeline[0]?.$search) { + if (originalPipeline[1]?.$match != null) { + originalPipeline[1].$match[filterKey] = originalPipeline[1].$match[filterKey] || discriminatorValue; + } else { + const match = {}; + match[filterKey] = discriminatorValue; + originalPipeline.splice(1, 0, { $match: match }); + } + } else { + const match = {}; + match[filterKey] = discriminatorValue; + originalPipeline.unshift({ $match: match }); + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js b/backend/node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js new file mode 100644 index 0000000..1f5d1e3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/aggregate/stringifyFunctionOperators.js @@ -0,0 +1,50 @@ +'use strict'; + +module.exports = function stringifyFunctionOperators(pipeline) { + if (!Array.isArray(pipeline)) { + return; + } + + for (const stage of pipeline) { + if (stage == null) { + continue; + } + + const canHaveAccumulator = stage.$group || stage.$bucket || stage.$bucketAuto; + if (canHaveAccumulator != null) { + for (const key of Object.keys(canHaveAccumulator)) { + handleAccumulator(canHaveAccumulator[key]); + } + } + + const stageType = Object.keys(stage)[0]; + if (stageType && typeof stage[stageType] === 'object') { + const stageOptions = stage[stageType]; + for (const key of Object.keys(stageOptions)) { + if (stageOptions[key] != null && + stageOptions[key].$function != null && + typeof stageOptions[key].$function.body === 'function') { + stageOptions[key].$function.body = stageOptions[key].$function.body.toString(); + } + } + } + + if (stage.$facet != null) { + for (const key of Object.keys(stage.$facet)) { + stringifyFunctionOperators(stage.$facet[key]); + } + } + } +}; + +function handleAccumulator(operator) { + if (operator == null || operator.$accumulator == null) { + return; + } + + for (const key of ['init', 'accumulate', 'merge', 'finalize']) { + if (typeof operator.$accumulator[key] === 'function') { + operator.$accumulator[key] = String(operator.$accumulator[key]); + } + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/arrayDepth.js b/backend/node_modules/mongoose/lib/helpers/arrayDepth.js new file mode 100644 index 0000000..16b92c1 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/arrayDepth.js @@ -0,0 +1,33 @@ +'use strict'; + +module.exports = arrayDepth; + +function arrayDepth(arr) { + if (!Array.isArray(arr)) { + return { min: 0, max: 0, containsNonArrayItem: true }; + } + if (arr.length === 0) { + return { min: 1, max: 1, containsNonArrayItem: false }; + } + if (arr.length === 1 && !Array.isArray(arr[0])) { + return { min: 1, max: 1, containsNonArrayItem: false }; + } + + const res = arrayDepth(arr[0]); + + for (let i = 1; i < arr.length; ++i) { + const _res = arrayDepth(arr[i]); + if (_res.min < res.min) { + res.min = _res.min; + } + if (_res.max > res.max) { + res.max = _res.max; + } + res.containsNonArrayItem = res.containsNonArrayItem || _res.containsNonArrayItem; + } + + res.min = res.min + 1; + res.max = res.max + 1; + + return res; +} diff --git a/backend/node_modules/mongoose/lib/helpers/buildMiddlewareFilter.js b/backend/node_modules/mongoose/lib/helpers/buildMiddlewareFilter.js new file mode 100644 index 0000000..b94a9b0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/buildMiddlewareFilter.js @@ -0,0 +1,24 @@ +'use strict'; + +const symbols = require('../schema/symbols'); + +/** + * Filter predicate that returns true for built-in middleware (marked with `builtInMiddleware` symbol). + */ +const isBuiltInMiddleware = hook => hook.fn[symbols.builtInMiddleware]; + +/** + * Builds a filter for kareem's execPre/execPost based on middleware options. + * + * @param {object} options - Options object that may contain `middleware` setting + * @param {string} phase - Either 'pre' or 'post' + * @returns {Function|null} - null runs all middleware, isBuiltInMiddleware skips user middleware + */ +function buildMiddlewareFilter(options, phase) { + const shouldRun = options?.middleware?.[phase] ?? options?.middleware ?? true; + return shouldRun ? null : isBuiltInMiddleware; +} + +module.exports = { + buildMiddlewareFilter +}; diff --git a/backend/node_modules/mongoose/lib/helpers/clone.js b/backend/node_modules/mongoose/lib/helpers/clone.js new file mode 100644 index 0000000..11d4618 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/clone.js @@ -0,0 +1,236 @@ +'use strict'; + +const Decimal = require('../types/decimal128'); +const ObjectId = require('../types/objectid'); +const specialProperties = require('./specialProperties'); +const isMongooseObject = require('./isMongooseObject'); +const getFunctionName = require('./getFunctionName'); +const isBsonType = require('./isBsonType'); +const isMongooseArray = require('../types/array/isMongooseArray').isMongooseArray; +const isObject = require('./isObject'); +const isPOJO = require('./isPOJO'); +const symbols = require('./symbols'); +const trustedSymbol = require('./query/trusted').trustedSymbol; +const BSON = require('mongodb/lib/bson'); +const UUID = BSON.UUID; + +const Binary = BSON.Binary; + +/** + * Object clone with Mongoose natives support. + * + * If options.minimize is true, creates a minimal data object. Empty objects and undefined values will not be cloned. This makes the data payload sent to MongoDB as small as possible. + * + * Functions and primitives are never cloned. + * + * @param {object} obj the object to clone + * @param {object} options + * @param {boolean} isArrayChild true if cloning immediately underneath an array. Special case for minimize. + * @return {object} the cloned object + * @api private + */ + +function clone(obj, options, isArrayChild) { + if (obj == null) { + return obj; + } + + if (isBsonType(obj, 'Double')) { + return new BSON.Double(obj.value); + } + if (typeof obj === 'number' || typeof obj === 'string' || typeof obj === 'boolean' || typeof obj === 'bigint') { + return obj; + } + + if (Array.isArray(obj)) { + return cloneArray(obj, options); + } + + if (isMongooseObject(obj)) { + if (options) { + if (options.flattenUUIDs) { + if (obj instanceof Binary && obj._subtype === Binary.SUBTYPE_UUID) { + return obj.toString(); + } + if (obj?.isMongooseBuffer && obj._subtype === Binary.SUBTYPE_UUID) { + return obj.toUUID().toString(); + } + } + if (options.retainDocuments && obj.$__ != null) { + const clonedDoc = obj.$clone(); + if (obj.__index != null) { + clonedDoc.__index = obj.__index; + } + if (obj.__parentArray != null) { + clonedDoc.__parentArray = options.parentArray ?? obj.__parentArray; + } + clonedDoc.$__setParent(options.parentDoc ?? obj.$__parent); + return clonedDoc; + } + if (options.retainDocuments && obj.$isMongooseMap) { + const clonedParent = options.parentDoc ?? obj.$__parent; + const MongooseMap = obj.constructor; + const ret = new MongooseMap({}, obj.$__path, clonedParent, obj.$__schemaType); + for (const [key, value] of obj) { + ret.$__set(key, clone(value, { ...options, parentDoc: clonedParent })); + } + return ret; + } + } + + if (isPOJO(obj) && obj.$__ != null && obj._doc != null) { + return obj._doc; + } + + let ret; + if (options?.json && typeof obj.toJSON === 'function') { + ret = obj.toJSON(options); + } else { + ret = obj.toObject(options); + } + + return ret; + } + + const objConstructor = obj.constructor; + + if (objConstructor) { + switch (getFunctionName(objConstructor)) { + case 'Object': + return cloneObject(obj, options, isArrayChild); + case 'Date': + return new objConstructor(+obj); + case 'RegExp': + return cloneRegExp(obj); + default: + // ignore + break; + } + } + + if (isBsonType(obj, 'ObjectId')) { + if (options?.flattenObjectIds) { + return obj.toJSON(); + } + return new ObjectId(obj.id); + } + + if (isBsonType(obj, 'Decimal128')) { + if (options?.flattenDecimals) { + return obj.toJSON(); + } + return Decimal.fromString(obj.toString()); + } + + if (obj instanceof UUID) { + if (options?.flattenUUIDs) { + return obj.toJSON(); + } + return new UUID(obj.buffer); + } + + // object created with Object.create(null) + if (!objConstructor && isObject(obj)) { + return cloneObject(obj, options, isArrayChild); + } + + if (typeof obj === 'object' && obj[symbols.schemaTypeSymbol]) { + return obj.clone(); + } + + // If we're cloning this object to go into a MongoDB command, + // and there's a `toBSON()` function, assume this object will be + // stored as a primitive in MongoDB and doesn't need to be cloned. + if (options?.bson && typeof obj.toBSON === 'function') { + return obj; + } + + if (typeof obj.valueOf === 'function') { + return obj.valueOf(); + } + + return cloneObject(obj, options, isArrayChild); +} +module.exports = clone; + +/*! + * ignore + */ + +function cloneObject(obj, options, isArrayChild) { + const minimize = options?.minimize; + const omitUndefined = options?.omitUndefined; + const seen = options?._seen; + const ret = {}; + let hasKeys; + + if (seen && seen.has(obj)) { + return seen.get(obj); + } else if (seen) { + seen.set(obj, ret); + } + if (trustedSymbol in obj && options?.copyTrustedSymbol !== false) { + ret[trustedSymbol] = obj[trustedSymbol]; + } + + const keys = Object.keys(obj); + const len = keys.length; + + for (let i = 0; i < len; ++i) { + const key = keys[i]; + if (specialProperties.has(key)) { + continue; + } + + // Don't pass `isArrayChild` down + const val = clone(obj[key], options, false); + + if ((minimize === false || omitUndefined) && typeof val === 'undefined') { + delete ret[key]; + } else if (minimize !== true || (typeof val !== 'undefined')) { + hasKeys || (hasKeys = true); + ret[key] = val; + } + } + + return minimize && !isArrayChild ? hasKeys && ret : ret; +} + +function cloneArray(arr, options) { + let i = 0; + const len = arr.length; + + let ret = null; + if (options?.retainDocuments) { + if (arr.isMongooseDocumentArray) { + ret = new (arr.$schemaType().schema.base.Types.DocumentArray)([], arr.$path(), arr.$parent(), arr.$schemaType()); + } else if (arr.isMongooseArray) { + ret = new (arr.$parent().schema.base.Types.Array)([], arr.$path(), arr.$parent(), arr.$schemaType()); + } else { + ret = new Array(len); + } + } else { + ret = new Array(len); + } + + arr = isMongooseArray(arr) ? arr.__array : arr; + if (ret.isMongooseDocumentArray) { + // Create new options object to avoid mutating the shared options. + // Subdocs need parentArray to point to their own cloned array. + options = { ...options, parentArray: ret }; + } + for (i = 0; i < len; ++i) { + ret[i] = clone(arr[i], options, true); + } + + return ret; +} + +function cloneRegExp(regexp) { + const ret = new RegExp(regexp.source, regexp.flags); + + if (ret.lastIndex !== regexp.lastIndex) { + ret.lastIndex = regexp.lastIndex; + } + return ret; +} diff --git a/backend/node_modules/mongoose/lib/helpers/common.js b/backend/node_modules/mongoose/lib/helpers/common.js new file mode 100644 index 0000000..b067b9a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/common.js @@ -0,0 +1,127 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const Binary = require('mongodb/lib/bson').Binary; +const isBsonType = require('./isBsonType'); +const isMongooseObject = require('./isMongooseObject'); +const MongooseError = require('../error'); +const util = require('util'); + +exports.flatten = flatten; +exports.modifiedPaths = modifiedPaths; + +/*! + * ignore + */ + +function flatten(update, path, options, schema) { + let keys; + if (update && isMongooseObject(update) && !Buffer.isBuffer(update)) { + keys = Object.keys(update.toObject({ transform: false, virtuals: false }) || {}); + } else { + keys = Object.keys(update || {}); + } + + const numKeys = keys.length; + const result = {}; + path = path ? path + '.' : ''; + + for (let i = 0; i < numKeys; ++i) { + const key = keys[i]; + const val = update[key]; + result[path + key] = val; + + // Avoid going into mixed paths if schema is specified + const keySchema = schema?.path?.(path + key); + const isNested = schema?.nested?.[path + key]; + if (keySchema?.instance === 'Mixed') continue; + + if (shouldFlatten(val)) { + if (options?.skipArrays && Array.isArray(val)) { + continue; + } + const flat = flatten(val, path + key, options, schema); + for (const k in flat) { + result[k] = flat[k]; + } + if (Array.isArray(val)) { + result[path + key] = val; + } + } + + if (isNested) { + const paths = Object.keys(schema.paths); + for (const p of paths) { + if (p.startsWith(path + key + '.') && !Object.hasOwn(result, p)) { + result[p] = void 0; + } + } + } + } + + return result; +} + +/*! + * ignore + */ + +function modifiedPaths(update, path, result, recursion = null) { + if (update == null || typeof update !== 'object') { + return; + } + + if (recursion == null) { + recursion = { + raw: { update, path }, + trace: new WeakSet() + }; + } + + if (recursion.trace.has(update)) { + throw new MongooseError(`a circular reference in the update value, updateValue: +${util.inspect(recursion.raw.update, { showHidden: false, depth: 1 })} +updatePath: '${recursion.raw.path}'`); + } + recursion.trace.add(update); + + const keys = Object.keys(update || {}); + const numKeys = keys.length; + result = result || {}; + path = path ? path + '.' : ''; + + for (let i = 0; i < numKeys; ++i) { + const key = keys[i]; + let val = update[key]; + + const _path = path + key; + result[_path] = true; + if (!Buffer.isBuffer(val) && isMongooseObject(val)) { + val = val.toObject({ transform: false, virtuals: false }); + } + if (shouldFlatten(val)) { + modifiedPaths(val, path + key, result, recursion); + } + } + recursion.trace.delete(update); + + return result; +} + +/*! + * ignore + */ + +function shouldFlatten(val) { + return val && + typeof val === 'object' && + !(val instanceof Date) && + !isBsonType(val, 'ObjectId') && + (!Array.isArray(val) || val.length !== 0) && + !(val instanceof Buffer) && + !isBsonType(val, 'Decimal128') && + !(val instanceof Binary); +} diff --git a/backend/node_modules/mongoose/lib/helpers/createJSONSchemaTypeDefinition.js b/backend/node_modules/mongoose/lib/helpers/createJSONSchemaTypeDefinition.js new file mode 100644 index 0000000..993ada2 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/createJSONSchemaTypeDefinition.js @@ -0,0 +1,24 @@ +'use strict'; + +/** + * Handles creating `{ type: 'object' }` vs `{ bsonType: 'object' }` vs `{ bsonType: ['object', 'null'] }` + * + * @param {string} type + * @param {string} bsonType + * @param {boolean} useBsonType + * @param {boolean} isRequired + */ + +module.exports = function createJSONSchemaTypeArray(type, bsonType, useBsonType, isRequired) { + if (useBsonType) { + if (isRequired) { + return { bsonType }; + } + return { bsonType: [bsonType, 'null'] }; + } else { + if (isRequired) { + return { type }; + } + return { type: [type, 'null'] }; + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/cursor/eachAsync.js b/backend/node_modules/mongoose/lib/helpers/cursor/eachAsync.js new file mode 100644 index 0000000..90a7632 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/cursor/eachAsync.js @@ -0,0 +1,225 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const EachAsyncMultiError = require('../../error/eachAsyncMultiError'); +const immediate = require('../immediate'); + +/** + * Execute `fn` for every document in the cursor. If `fn` returns a promise, + * will wait for the promise to resolve before iterating on to the next one. + * Returns a promise that resolves when done. + * + * @param {Function} next the thunk to call to get the next document + * @param {Function} fn + * @param {object} options + * @param {number} [options.batchSize=null] if set, Mongoose will call `fn` with an array of at most `batchSize` documents, instead of a single document + * @param {number} [options.parallel=1] maximum number of `fn` calls that Mongoose will run in parallel + * @param {AbortSignal} [options.signal] allow cancelling this eachAsync(). Once the abort signal is fired, `eachAsync()` will immediately fulfill the returned promise (or call the callback) and not fetch any more documents. + * @return {Promise} + * @api public + * @method eachAsync + */ + +module.exports = async function eachAsync(next, fn, options) { + const parallel = options.parallel || 1; + const batchSize = options.batchSize; + const signal = options.signal; + const continueOnError = options.continueOnError; + const aggregatedErrors = []; + const enqueue = asyncQueue(); + + let aborted = false; + + return new Promise((resolve, reject) => { + if (signal != null) { + if (signal.aborted) { + return resolve(null); + } + + signal.addEventListener('abort', () => { + aborted = true; + return resolve(null); + }, { once: true }); + } + + if (batchSize != null) { + if (typeof batchSize !== 'number') { + throw new TypeError('batchSize must be a number'); + } else if (!Number.isInteger(batchSize)) { + throw new TypeError('batchSize must be an integer'); + } else if (batchSize < 1) { + throw new TypeError('batchSize must be at least 1'); + } + } + + iterate((err, res) => { + if (err != null) { + return reject(err); + } + resolve(res); + }); + }); + + function iterate(finalCallback) { + let handleResultsInProgress = 0; + let currentDocumentIndex = 0; + + let error = null; + for (let i = 0; i < parallel; ++i) { + enqueue(createFetch()); + } + + function createFetch() { + let documentsBatch = []; + let drained = false; + + return fetch; + + function fetch(done) { + if (drained || aborted) { + return done(); + } else if (error) { + return done(); + } + + next(function(err, doc) { + if (error != null) { + return done(); + } + if (err != null) { + if (err.name === 'MongoCursorExhaustedError') { + // We may end up calling `next()` multiple times on an exhausted + // cursor, which leads to an error. In case cursor is exhausted, + // just treat it as if the cursor returned no document, which is + // how a cursor indicates it is exhausted. + doc = null; + } else if (continueOnError) { + aggregatedErrors.push(err); + } else { + error = err; + finalCallback(err); + return done(); + } + } + if (doc == null) { + drained = true; + if (handleResultsInProgress <= 0) { + const finalErr = continueOnError ? + createEachAsyncMultiError(aggregatedErrors) : + error; + + finalCallback(finalErr); + } else if (batchSize && documentsBatch.length) { + handleNextResult(documentsBatch, currentDocumentIndex++, handleNextResultCallBack); + } + return done(); + } + + ++handleResultsInProgress; + + // Kick off the subsequent `next()` before handling the result, but + // make sure we know that we still have a result to handle re: #8422 + immediate(() => done()); + + if (batchSize) { + documentsBatch.push(doc); + } + + // If the current documents size is less than the provided batch size don't process the documents yet + if (batchSize && documentsBatch.length !== batchSize) { + immediate(() => enqueue(fetch)); + return; + } + + const docsToProcess = batchSize ? documentsBatch : doc; + + function handleNextResultCallBack(err) { + if (batchSize) { + handleResultsInProgress -= documentsBatch.length; + documentsBatch = []; + } else { + --handleResultsInProgress; + } + if (err != null) { + if (continueOnError) { + aggregatedErrors.push(err); + } else { + error = err; + return finalCallback(err); + } + } + if ((drained || aborted) && handleResultsInProgress <= 0) { + const finalErr = continueOnError ? + createEachAsyncMultiError(aggregatedErrors) : + error; + return finalCallback(finalErr); + } + + immediate(() => enqueue(fetch)); + } + + handleNextResult(docsToProcess, currentDocumentIndex++, handleNextResultCallBack); + }); + } + } + } + + function handleNextResult(doc, i, callback) { + let maybePromise; + try { + maybePromise = fn(doc, i); + } catch (err) { + return callback(err); + } + if (typeof maybePromise?.then === 'function') { + maybePromise.then( + function() { callback(null); }, + function(error) { + callback(error || new Error('`eachAsync()` promise rejected without error')); + }); + } else { + callback(null); + } + } +}; + +// `next()` can only execute one at a time, so make sure we always execute +// `next()` in series, while still allowing multiple `fn()` instances to run +// in parallel. +function asyncQueue() { + const _queue = []; + let inProgress = null; + let id = 0; + + return function enqueue(fn) { + if ( + inProgress === null && + _queue.length === 0 + ) { + inProgress = id++; + return fn(_step); + } + _queue.push(fn); + }; + + function _step() { + if (_queue.length !== 0) { + inProgress = id++; + const fn = _queue.shift(); + fn(_step); + } else { + inProgress = null; + } + } +} + +function createEachAsyncMultiError(aggregatedErrors) { + if (aggregatedErrors.length === 0) { + return null; + } + + return new EachAsyncMultiError(aggregatedErrors); +} diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/applyEmbeddedDiscriminators.js b/backend/node_modules/mongoose/lib/helpers/discriminator/applyEmbeddedDiscriminators.js new file mode 100644 index 0000000..01593a9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/applyEmbeddedDiscriminators.js @@ -0,0 +1,36 @@ +'use strict'; + +module.exports = applyEmbeddedDiscriminators; + +function applyEmbeddedDiscriminators(schema, seen = new WeakSet(), overwriteExisting = false) { + if (seen.has(schema)) { + return; + } + seen.add(schema); + for (const path of Object.keys(schema.paths)) { + const schemaType = schema.paths[path]; + if (!schemaType.schema) { + continue; + } + applyEmbeddedDiscriminators(schemaType.schema, seen); + if (!schemaType.schema._applyDiscriminators) { + continue; + } + if (schemaType._appliedDiscriminators && !overwriteExisting) { + continue; + } + for (const discriminatorKey of schemaType.schema._applyDiscriminators.keys()) { + const { + schema: discriminatorSchema, + options + } = schemaType.schema._applyDiscriminators.get(discriminatorKey); + applyEmbeddedDiscriminators(discriminatorSchema, seen); + schemaType.discriminator( + discriminatorKey, + discriminatorSchema, + overwriteExisting ? { ...options, overwriteExisting: true } : options + ); + } + schemaType._appliedDiscriminators = true; + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js b/backend/node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js new file mode 100644 index 0000000..1b81a48 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/areDiscriminatorValuesEqual.js @@ -0,0 +1,16 @@ +'use strict'; + +const isBsonType = require('../isBsonType'); + +module.exports = function areDiscriminatorValuesEqual(a, b) { + if (typeof a === 'string' && typeof b === 'string') { + return a === b; + } + if (typeof a === 'number' && typeof b === 'number') { + return a === b; + } + if (isBsonType(a, 'ObjectId') && isBsonType(b, 'ObjectId')) { + return a.toString() === b.toString(); + } + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js b/backend/node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js new file mode 100644 index 0000000..4eb56de --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function checkEmbeddedDiscriminatorKeyProjection(userProjection, path, schema, selected, addedPaths) { + const userProjectedInPath = Object.keys(userProjection). + reduce((cur, key) => cur || key.startsWith(path + '.'), false); + const _discriminatorKey = path + '.' + schema.options.discriminatorKey; + if (!userProjectedInPath && + addedPaths.length === 1 && + addedPaths[0] === _discriminatorKey) { + selected.splice(selected.indexOf(_discriminatorKey), 1); + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/getConstructor.js b/backend/node_modules/mongoose/lib/helpers/discriminator/getConstructor.js new file mode 100644 index 0000000..a649a96 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/getConstructor.js @@ -0,0 +1,29 @@ +'use strict'; + +const getDiscriminatorByValue = require('./getDiscriminatorByValue'); + +/** + * Find the correct constructor, taking into account discriminators + * @api private + */ + +module.exports = function getConstructor(Constructor, value, defaultDiscriminatorValue) { + const discriminatorKey = Constructor.schema.options.discriminatorKey; + let discriminatorValue = value?.[discriminatorKey]; + if (discriminatorValue == null) { + discriminatorValue = defaultDiscriminatorValue; + } + if (Constructor.discriminators && + discriminatorValue != null) { + if (Constructor.discriminators[discriminatorValue]) { + Constructor = Constructor.discriminators[discriminatorValue]; + } else { + const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, discriminatorValue); + if (constructorByValue) { + Constructor = constructorByValue; + } + } + } + + return Constructor; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js b/backend/node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js new file mode 100644 index 0000000..f66038d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/getDiscriminatorByValue.js @@ -0,0 +1,28 @@ +'use strict'; + +const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual'); + +/** + * returns discriminator by discriminatorMapping.value + * + * @param {object} discriminators + * @param {string} value + * @api private + */ + +module.exports = function getDiscriminatorByValue(discriminators, value) { + if (discriminators == null) { + return null; + } + for (const name of Object.keys(discriminators)) { + const it = discriminators[name]; + if ( + it.schema && + it.schema.discriminatorMapping && + areDiscriminatorValuesEqual(it.schema.discriminatorMapping.value, value) + ) { + return it; + } + } + return null; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js b/backend/node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js new file mode 100644 index 0000000..3f9a678 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/getSchemaDiscriminatorByValue.js @@ -0,0 +1,27 @@ +'use strict'; + +const areDiscriminatorValuesEqual = require('./areDiscriminatorValuesEqual'); + +/** + * returns discriminator by discriminatorMapping.value + * + * @param {Schema} schema + * @param {string} value + * @api private + */ + +module.exports = function getSchemaDiscriminatorByValue(schema, value) { + if (schema?.discriminators == null) { + return null; + } + for (const key of Object.keys(schema.discriminators)) { + const discriminatorSchema = schema.discriminators[key]; + if (discriminatorSchema.discriminatorMapping == null) { + continue; + } + if (areDiscriminatorValuesEqual(discriminatorSchema.discriminatorMapping.value, value)) { + return discriminatorSchema; + } + } + return null; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/discriminator/mergeDiscriminatorSchema.js b/backend/node_modules/mongoose/lib/helpers/discriminator/mergeDiscriminatorSchema.js new file mode 100644 index 0000000..7f21a7e --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/discriminator/mergeDiscriminatorSchema.js @@ -0,0 +1,96 @@ +'use strict'; +const schemaMerge = require('../schema/merge'); +const specialProperties = require('../../helpers/specialProperties'); +const isBsonType = require('../../helpers/isBsonType'); +const ObjectId = require('../../types/objectid'); +const SchemaType = require('../../schemaType'); +const isObject = require('../../helpers/isObject'); +/** + * Merges `from` into `to` without overwriting existing properties. + * + * @param {object} to + * @param {object} from + * @param {string} [path] + * @api private + */ + +module.exports = function mergeDiscriminatorSchema(to, from, path, seen) { + const keys = Object.keys(from); + let i = 0; + const len = keys.length; + let key; + + path = path || ''; + seen = seen || new WeakSet(); + + if (seen.has(from)) { + return; + } + seen.add(from); + + while (i < len) { + key = keys[i++]; + if (!path) { + if (key === 'discriminators' || + key === 'base' || + key === '_applyDiscriminators' || + key === '_userProvidedOptions' || + key === 'options' || + key === 'tree') { + continue; + } + } + if (path === 'tree' && from?.instanceOfSchema) { + continue; + } + if (specialProperties.has(key)) { + continue; + } + if (key === 'parentSchema') { + // Skip parentSchema to avoid merging parent schema multiple times + // through child paths' back-references (gh-15966) + continue; + } + if (to[key] == null) { + to[key] = from[key]; + } else if (isObject(from[key])) { + if (!isObject(to[key])) { + to[key] = {}; + } + if (from[key] != null) { + // Skip merging schemas if we're creating a discriminator schema and + // base schema has a given path as a single nested but discriminator schema + // has the path as a document array, or vice versa (gh-9534) + if ((from[key].$isSingleNested && to[key].$isMongooseDocumentArray) || + (from[key].$isMongooseDocumentArray && to[key].$isSingleNested) || + (from[key].$isMongooseDocumentArrayElement && to[key].$isMongooseDocumentArrayElement)) { + continue; + } else if (from[key].instanceOfSchema) { + if (to[key].instanceOfSchema) { + schemaMerge(to[key], from[key].clone(), true); + } else { + to[key] = from[key].clone(); + } + continue; + } else if (isBsonType(from[key], 'ObjectId')) { + to[key] = new ObjectId(from[key]); + continue; + } else if (from[key] instanceof SchemaType) { + if (to[key] == null) { + to[key] = from[key].clone(); + } + // For container types with nested schemas, we need to continue to the + // recursive merge below to properly merge the nested schemas + if (!from[key].$isMongooseDocumentArray && !from[key].$isSingleNested) { + continue; + } + } + } + mergeDiscriminatorSchema(to[key], from[key], path ? path + '.' + key : key, seen); + } + } + + if (from?.instanceOfSchema) { + to.tree = Object.assign({}, from.tree, to.tree); + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/document/applyDefaults.js b/backend/node_modules/mongoose/lib/helpers/document/applyDefaults.js new file mode 100644 index 0000000..b4714c3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/applyDefaults.js @@ -0,0 +1,132 @@ +'use strict'; + +const isNestedProjection = require('../projection/isNestedProjection'); + +module.exports = function applyDefaults(doc, fields, exclude, hasIncludedChildren, isBeforeSetters, pathsToSkip, options) { + const paths = Object.keys(doc.$__schema.paths); + const plen = paths.length; + const skipParentChangeTracking = options?.skipParentChangeTracking; + + for (let i = 0; i < plen; ++i) { + let def; + let curPath = ''; + const p = paths[i]; + + if (p === '_id' && doc.$__.skipId) { + continue; + } + + const type = doc.$__schema.paths[p]; + const path = type.splitPath(); + const len = path.length; + if (path[len - 1] === '$*') { + continue; + } + let included = false; + let doc_ = doc._doc; + for (let j = 0; j < len; ++j) { + if (doc_ == null) { + break; + } + + const piece = path[j]; + curPath += (!curPath.length ? '' : '.') + piece; + + if (exclude === true) { + if (curPath in fields) { + break; + } + } else if (exclude === false && fields && !included) { + const hasSubpaths = type.$isSingleNested || type.$isMongooseDocumentArray; + if ((curPath in fields && !isNestedProjection(fields[curPath])) || (j === len - 1 && hasSubpaths && hasIncludedChildren != null && hasIncludedChildren[curPath])) { + included = true; + } else if (hasIncludedChildren != null && !hasIncludedChildren[curPath]) { + break; + } + } + + if (j === len - 1) { + if (doc_[piece] !== void 0) { + break; + } + + if (isBeforeSetters != null) { + if (typeof type.defaultValue === 'function') { + if (!type.defaultValue.$runBeforeSetters && isBeforeSetters) { + break; + } + if (type.defaultValue.$runBeforeSetters && !isBeforeSetters) { + break; + } + } else if (!isBeforeSetters) { + // Non-function defaults should always run **before** setters + continue; + } + } + + if (pathsToSkip && pathsToSkip[curPath]) { + break; + } + + if (fields && exclude !== null) { + if (exclude === true) { + // apply defaults to all non-excluded fields + if (p in fields) { + continue; + } + + try { + def = type.getDefault(doc, false); + } catch (err) { + doc.invalidate(p, err); + break; + } + + if (typeof def !== 'undefined') { + doc_[piece] = def; + applyChangeTracking(doc, p, skipParentChangeTracking); + } + } else if (included) { + // selected field + try { + def = type.getDefault(doc, false); + } catch (err) { + doc.invalidate(p, err); + break; + } + + if (typeof def !== 'undefined') { + doc_[piece] = def; + applyChangeTracking(doc, p, skipParentChangeTracking); + } + } + } else { + try { + def = type.getDefault(doc, false); + } catch (err) { + doc.invalidate(p, err); + break; + } + + if (typeof def !== 'undefined') { + doc_[piece] = def; + applyChangeTracking(doc, p, skipParentChangeTracking); + } + } + } else { + doc_ = doc_[piece]; + } + } + } +}; + +/*! + * ignore + */ + +function applyChangeTracking(doc, fullPath, skipParentChangeTracking) { + doc.$__.activePaths.default(fullPath); + if (!skipParentChangeTracking && doc.$isSubdocument && doc.$isSingleNested && doc.$parent() != null) { + doc.$parent().$__.activePaths.default(doc.$__pathRelativeToParent(fullPath)); + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/document/applyTimestamps.js b/backend/node_modules/mongoose/lib/helpers/document/applyTimestamps.js new file mode 100644 index 0000000..83e221d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/applyTimestamps.js @@ -0,0 +1,106 @@ +'use strict'; + +const handleTimestampOption = require('../schema/handleTimestampOption'); +const mpath = require('mpath'); +const utils = require('../../utils'); + +module.exports = applyTimestamps; + +/** + * Apply a given schema's timestamps to the given POJO + * + * @param {Schema} schema + * @param {object} obj + * @param {object} [options] + * @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt + * @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time. + */ + +function applyTimestamps(schema, obj, options) { + if (obj == null) { + return obj; + } + + applyTimestampsToChildren(schema, obj, options); + return applyTimestampsToDoc(schema, obj, options); +} + +/** + * Apply timestamps to any subdocuments + * + * @param {Schema} schema subdocument schema + * @param {object} res subdocument + * @param {object} [options] + * @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt + * @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time. + */ + +function applyTimestampsToChildren(schema, res, options) { + for (const childSchema of schema.childSchemas) { + const _path = childSchema.model.path; + const _schema = childSchema.schema; + if (!_path) { + continue; + } + const _obj = mpath.get(_path, res); + if (_obj == null || (Array.isArray(_obj) && _obj.flat(Infinity).length === 0)) { + continue; + } + + applyTimestamps(_schema, _obj, options); + } +} + +/** + * Apply timestamps to a given document. Does not apply timestamps to subdocuments: use `applyTimestampsToChildren` instead + * + * @param {Schema} schema + * @param {object} obj + * @param {object} [options] + * @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt + * @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time. + */ + +function applyTimestampsToDoc(schema, obj, options) { + if (obj == null || typeof obj !== 'object') { + return; + } + if (Array.isArray(obj)) { + for (const el of obj) { + applyTimestampsToDoc(schema, el, options); + } + return; + } + + if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) { + for (const discriminatorKey of Object.keys(schema.discriminators)) { + const discriminator = schema.discriminators[discriminatorKey]; + const key = discriminator.discriminatorMapping.key; + const value = discriminator.discriminatorMapping.value; + if (obj[key] == value) { + schema = discriminator; + break; + } + } + } + + const createdAt = handleTimestampOption(schema.options.timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(schema.options.timestamps, 'updatedAt'); + const currentTime = options?.currentTime; + + let ts = null; + if (currentTime != null) { + ts = currentTime(); + } else if (schema.base?.now) { + ts = schema.base.now(); + } else { + ts = new Date(); + } + + if (createdAt && obj[createdAt] == null && !options?.isUpdate) { + obj[createdAt] = ts; + } + if (updatedAt) { + obj[updatedAt] = ts; + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/document/applyVirtuals.js b/backend/node_modules/mongoose/lib/helpers/document/applyVirtuals.js new file mode 100644 index 0000000..7b7c4e7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/applyVirtuals.js @@ -0,0 +1,147 @@ +'use strict'; + +const mpath = require('mpath'); +const utils = require('../../utils'); + +module.exports = applyVirtuals; + +/** + * Apply a given schema's virtuals to a given POJO + * + * @param {Schema} schema + * @param {object} obj + * @param {string[]} [virtuals] optional whitelist of virtuals to apply + * @returns + */ + +function applyVirtuals(schema, obj, virtuals) { + if (obj == null) { + return obj; + } + + let virtualsForChildren = virtuals; + let toApply = null; + + if (Array.isArray(virtuals)) { + virtualsForChildren = []; + toApply = []; + for (const virtual of virtuals) { + if (virtual.length === 1) { + toApply.push(virtual[0]); + } else { + virtualsForChildren.push(virtual); + } + } + } + + applyVirtualsToChildren(schema, obj, virtualsForChildren); + return applyVirtualsToDoc(schema, obj, toApply); +} + +/** + * Apply virtuals to any subdocuments + * + * @param {Schema} schema subdocument schema + * @param {object} res subdocument + * @param {string[]} [virtuals] optional whitelist of virtuals to apply + */ + +function applyVirtualsToChildren(schema, res, virtuals) { + let attachedVirtuals = false; + for (const childSchema of schema.childSchemas) { + const _path = childSchema.model.path; + const _schema = childSchema.schema; + if (!_path) { + continue; + } + const _obj = mpath.get(_path, res); + if (_obj == null || (Array.isArray(_obj) && _obj.flat(Infinity).length === 0)) { + continue; + } + + let virtualsForChild = null; + if (Array.isArray(virtuals)) { + virtualsForChild = []; + for (const virtual of virtuals) { + if (virtual[0] == _path) { + virtualsForChild.push(virtual.slice(1)); + } + } + + if (virtualsForChild.length === 0) { + continue; + } + } + + applyVirtuals(_schema, _obj, virtualsForChild); + attachedVirtuals = true; + } + + if (virtuals?.length && !attachedVirtuals) { + applyVirtualsToDoc(schema, res, virtuals); + } +} + +/** + * Apply virtuals to a given document. Does not apply virtuals to subdocuments: use `applyVirtualsToChildren` instead + * + * @param {Schema} schema + * @param {object} doc + * @param {string[]} [virtuals] optional whitelist of virtuals to apply + * @returns + */ + +function applyVirtualsToDoc(schema, obj, virtuals) { + if (obj == null || typeof obj !== 'object') { + return; + } + if (Array.isArray(obj)) { + for (const el of obj) { + applyVirtualsToDoc(schema, el, virtuals); + } + return; + } + + if (schema.discriminators && utils.hasOwnKeys(schema.discriminators)) { + for (const discriminatorKey of Object.keys(schema.discriminators)) { + const discriminator = schema.discriminators[discriminatorKey]; + const key = discriminator.discriminatorMapping.key; + const value = discriminator.discriminatorMapping.value; + if (obj[key] == value) { + schema = discriminator; + break; + } + } + } + + if (virtuals == null) { + virtuals = Object.keys(schema.virtuals); + } + for (const virtual of virtuals) { + if (schema.virtuals[virtual] == null) { + continue; + } + const virtualType = schema.virtuals[virtual]; + const sp = Array.isArray(virtual) + ? virtual + : virtual.indexOf('.') === -1 + ? [virtual] + : virtual.split('.'); + let cur = obj; + for (let i = 0; i < sp.length - 1; ++i) { + cur[sp[i]] = sp[i] in cur ? cur[sp[i]] : {}; + cur = cur[sp[i]]; + } + let val = virtualType.applyGetters(cur[sp[sp.length - 1]], obj); + const isPopulateVirtual = + virtualType.options?.ref || virtualType.options?.refPath; + if (isPopulateVirtual && val === undefined) { + if (virtualType.options.justOne) { + val = null; + } else { + val = []; + } + } + cur[sp[sp.length - 1]] = val; + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js b/backend/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js new file mode 100644 index 0000000..bbf0c45 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/cleanModifiedSubpaths.js @@ -0,0 +1,45 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function cleanModifiedSubpaths(doc, path, options) { + options = options || {}; + const skipDocArrays = options.skipDocArrays; + + let deleted = 0; + if (!doc) { + return deleted; + } + + for (const modifiedPath of Object.keys(doc.$__.activePaths.getStatePaths('modify'))) { + if (skipDocArrays) { + const schemaType = doc.$__schema.path(modifiedPath); + if (schemaType?.$isMongooseDocumentArray) { + continue; + } + } + if (modifiedPath.startsWith(path + '.')) { + doc.$__.activePaths.clearPath(modifiedPath); + ++deleted; + + if (doc.$isSubdocument) { + cleanParent(doc, modifiedPath); + } + } + } + return deleted; +}; + +function cleanParent(doc, path, seen = new Set()) { + if (seen.has(doc)) { + throw new Error('Infinite subdocument loop: subdoc with _id ' + doc._id + ' is a parent of itself'); + } + const parent = doc.$parent(); + const newPath = doc.$__pathRelativeToParent(void 0, false) + '.' + path; + parent.$__.activePaths.clearPath(newPath); + if (parent.$isSubdocument) { + cleanParent(parent, newPath, seen); + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/document/compile.js b/backend/node_modules/mongoose/lib/helpers/document/compile.js new file mode 100644 index 0000000..c13a15c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/compile.js @@ -0,0 +1,241 @@ +'use strict'; + +const clone = require('../../helpers/clone'); +const documentSchemaSymbol = require('../../helpers/symbols').documentSchemaSymbol; +const internalToObjectOptions = require('../../options').internalToObjectOptions; +const utils = require('../../utils'); + +let Document; +const getSymbol = require('../../helpers/symbols').getSymbol; +const scopeSymbol = require('../../helpers/symbols').scopeSymbol; + +const isPOJO = utils.isPOJO; + +/*! + * exports + */ + +exports.compile = compile; +exports.defineKey = defineKey; + +const _isEmptyOptions = Object.freeze({ + minimize: true, + virtuals: false, + getters: false, + transform: false +}); + +const noDottedPathGetOptions = Object.freeze({ + noDottedPath: true +}); + +/** + * Compiles schemas. + * @param {object} tree + * @param {any} proto + * @param {string} prefix + * @param {object} options + * @api private + */ + +function compile(tree, proto, prefix, options) { + Document = Document || require('../../document'); + const typeKey = options.typeKey; + + for (const key of Object.keys(tree)) { + const limb = tree[key]; + + const hasSubprops = isPOJO(limb) && + utils.hasOwnKeys(limb) && + (!limb[typeKey] || (typeKey === 'type' && isPOJO(limb.type) && limb.type.type)); + const subprops = hasSubprops ? limb : null; + + defineKey({ prop: key, subprops: subprops, prototype: proto, prefix: prefix, options: options }); + } +} + +/** + * Defines the accessor named prop on the incoming prototype. + * @param {object} options + * @param {string} options.prop + * @param {boolean} options.subprops + * @param {any} options.prototype + * @param {string} [options.prefix] + * @param {object} options.options + * @api private + */ + +function defineKey({ prop, subprops, prototype, prefix, options }) { + Document = Document || require('../../document'); + const path = (prefix ? prefix + '.' : '') + prop; + prefix = prefix || ''; + const useGetOptions = prefix ? Object.freeze({}) : noDottedPathGetOptions; + + if (subprops) { + Object.defineProperty(prototype, prop, { + enumerable: true, + configurable: true, + get: function() { + if (!this.$__) { + return undefined; + } + const _this = this; + if (!this.$__.getters) { + this.$__.getters = {}; + } + + if (!this.$__.getters[path]) { + const nested = Object.create(Document.prototype, getOwnPropertyDescriptors(this)); + + // save scope for nested getters/setters + if (!prefix) { + nested.$__[scopeSymbol] = this; + } + nested.$__.nestedPath = path; + + Object.defineProperty(nested, 'schema', { + enumerable: false, + configurable: true, + writable: false, + value: prototype.schema + }); + + Object.defineProperty(nested, '$__schema', { + enumerable: false, + configurable: true, + writable: false, + value: prototype.schema + }); + + Object.defineProperty(nested, documentSchemaSymbol, { + enumerable: false, + configurable: true, + writable: false, + value: prototype.schema + }); + + Object.defineProperty(nested, 'toObject', { + enumerable: false, + configurable: true, + writable: false, + value: function() { + return clone(_this.get(path, null, { + virtuals: this && + this.schema && + this.schema.options && + this.schema.options.toObject && + this.schema.options.toObject.virtuals || null + })); + } + }); + + Object.defineProperty(nested, '$__get', { + enumerable: false, + configurable: true, + writable: false, + value: function() { + return _this.get(path, null, { + virtuals: this?.schema?.options?.toObject?.virtuals || null + }); + } + }); + + Object.defineProperty(nested, 'toJSON', { + enumerable: false, + configurable: true, + writable: false, + value: function() { + return _this.get(path, null, { + virtuals: this?.schema?.options?.toJSON?.virtuals || null + }); + } + }); + + Object.defineProperty(nested, '$__isNested', { + enumerable: false, + configurable: true, + writable: false, + value: true + }); + + Object.defineProperty(nested, '$isEmpty', { + enumerable: false, + configurable: true, + writable: false, + value: function() { + return Object.keys(this.get(path, null, _isEmptyOptions) || {}).length === 0; + } + }); + + Object.defineProperty(nested, '$__parent', { + enumerable: false, + configurable: true, + writable: false, + value: this + }); + + compile(subprops, nested, path, options); + this.$__.getters[path] = nested; + } + + return this.$__.getters[path]; + }, + set: function(v) { + if (v?.$__isNested) { + // Convert top-level to POJO, but leave subdocs hydrated so `$set` + // can handle them. See gh-9293. + v = v.$__get(); + } else if (v instanceof Document && !v.$__isNested) { + v = v.$toObject(internalToObjectOptions); + } + const doc = this.$__[scopeSymbol] || this; + doc.$set(path, v); + } + }); + } else { + Object.defineProperty(prototype, prop, { + enumerable: true, + configurable: true, + get: function() { + return this[getSymbol].call( + this.$__[scopeSymbol] || this, + path, + null, + useGetOptions + ); + }, + set: function(v) { + this.$set.call(this.$__[scopeSymbol] || this, path, v); + } + }); + } +} + +// gets descriptors for all properties of `object` +// makes all properties non-enumerable to match previous behavior to #2211 +function getOwnPropertyDescriptors(object) { + const result = {}; + + Object.getOwnPropertyNames(object).forEach(function(key) { + const skip = [ + 'isNew', + '$__', + '$errors', + 'errors', + '_doc', + '$locals', + '$op', + '__parentArray', + '__index', + '$isDocumentArrayElement' + ].indexOf(key) === -1; + if (skip) { + return; + } + + result[key] = Object.getOwnPropertyDescriptor(object, key); + result[key].enumerable = false; + }); + + return result; +} diff --git a/backend/node_modules/mongoose/lib/helpers/document/getDeepestSubdocumentForPath.js b/backend/node_modules/mongoose/lib/helpers/document/getDeepestSubdocumentForPath.js new file mode 100644 index 0000000..346a51b --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/getDeepestSubdocumentForPath.js @@ -0,0 +1,38 @@ +'use strict'; + +/** + * Find the deepest subdocument along a given path to ensure setter functions run + * with the correct subdocument as `this`. If no subdocuments, returns the top-level + * document. + * + * @param {Document} doc + * @param {string[]} parts + * @param {Schema} schema + * @returns {Document} + */ + +module.exports = function getDeepestSubdocumentForPath(doc, parts, schema) { + let curPath = parts[0]; + let curSchema = schema; + let subdoc = doc; + for (let i = 0; i < parts.length - 1; ++i) { + const curSchemaType = curSchema.path(curPath); + if (curSchemaType?.schema) { + let newSubdoc = subdoc.get(curPath); + curSchema = curSchemaType.schema; + curPath = parts[i + 1]; + if (Array.isArray(newSubdoc) && !isNaN(curPath)) { + newSubdoc = newSubdoc[curPath]; + curPath = ''; + } + if (newSubdoc == null) { + break; + } + subdoc = newSubdoc; + } else { + curPath += curPath.length ? '.' + parts[i + 1] : parts[i + 1]; + } + } + + return subdoc; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js b/backend/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js new file mode 100644 index 0000000..a398cf9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/getEmbeddedDiscriminatorPath.js @@ -0,0 +1,53 @@ +'use strict'; + +const get = require('../get'); +const getSchemaDiscriminatorByValue = require('../discriminator/getSchemaDiscriminatorByValue'); + +/** + * Like `schema.path()`, except with a document, because impossible to + * determine path type without knowing the embedded discriminator key. + * + * @param {Document} doc + * @param {string|string[]} path + * @param {object} [options] + * @api private + */ + +module.exports = function getEmbeddedDiscriminatorPath(doc, path, options) { + options = options || {}; + const typeOnly = options.typeOnly; + const parts = Array.isArray(path) ? + path : + (path.indexOf('.') === -1 ? [path] : path.split('.')); + let schemaType = null; + let type = 'adhocOrUndefined'; + + const schema = getSchemaDiscriminatorByValue(doc.schema, doc.get(doc.schema.options.discriminatorKey)) || doc.schema; + + for (let i = 0; i < parts.length; ++i) { + const subpath = parts.slice(0, i + 1).join('.'); + schemaType = schema.path(subpath); + if (schemaType == null) { + type = 'adhocOrUndefined'; + continue; + } + if (schemaType.instance === 'Mixed') { + return typeOnly ? 'real' : schemaType; + } + type = schema.pathType(subpath); + if ((schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) && + schemaType.schema.discriminators != null) { + const discriminators = schemaType.schema.discriminators; + const discriminatorKey = doc.get(subpath + '.' + + get(schemaType, 'schema.options.discriminatorKey')); + if (discriminatorKey == null || discriminators[discriminatorKey] == null) { + continue; + } + const rest = parts.slice(i + 1).join('.'); + return getEmbeddedDiscriminatorPath(doc.get(subpath), rest, options); + } + } + + // Are we getting the whole schema or just the type, 'real', 'nested', etc. + return typeOnly ? type : schemaType; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js b/backend/node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js new file mode 100644 index 0000000..5f71517 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/handleSpreadDoc.js @@ -0,0 +1,35 @@ +'use strict'; + +const utils = require('../../utils'); + +const keysToSkip = new Set(['__index', '__parentArray', '_doc']); + +/** + * Using spread operator on a Mongoose document gives you a + * POJO that has a tendency to cause infinite recursion. So + * we use this function on `set()` to prevent that. + */ + +module.exports = function handleSpreadDoc(v, includeExtraKeys) { + if (utils.isPOJO(v) && v.$__ != null && v._doc != null) { + if (includeExtraKeys) { + const extraKeys = {}; + for (const key of Object.keys(v)) { + if (typeof key === 'symbol') { + continue; + } + if (key[0] === '$') { + continue; + } + if (keysToSkip.has(key)) { + continue; + } + extraKeys[key] = v[key]; + } + return { ...v._doc, ...extraKeys }; + } + return v._doc; + } + + return v; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/document/isInPathsToSave.js b/backend/node_modules/mongoose/lib/helpers/document/isInPathsToSave.js new file mode 100644 index 0000000..3d8e0d6 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/document/isInPathsToSave.js @@ -0,0 +1,24 @@ +'use strict'; + +/** + * Returns true if `path` is included by the `pathsToSave` filter. + * Matches exact paths and child paths (e.g. 'metadata.views' is included by 'metadata'). + * + * @param {string} path + * @param {Set} pathsToSaveSet - pre-built Set of pathsToSave for O(1) exact lookup + * @param {string[]} pathsToSave - original array, used for prefix matching + * @returns {boolean} + */ +module.exports = function isInPathsToSave(path, pathsToSaveSet, pathsToSave) { + if (pathsToSaveSet.has(path)) { + return true; + } + + for (const pathToSave of pathsToSave) { + if (path.slice(0, pathToSave.length) === pathToSave && path.charAt(pathToSave.length) === '.') { + return true; + } + } + + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/each.js b/backend/node_modules/mongoose/lib/helpers/each.js new file mode 100644 index 0000000..9ce5cc6 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/each.js @@ -0,0 +1,25 @@ +'use strict'; + +module.exports = function each(arr, cb, done) { + if (arr.length === 0) { + return done(); + } + + let remaining = arr.length; + let err = null; + for (const v of arr) { + cb(v, function(_err) { + if (err != null) { + return; + } + if (_err != null) { + err = _err; + return done(err); + } + + if (--remaining <= 0) { + return done(); + } + }); + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/error/combinePathErrors.js b/backend/node_modules/mongoose/lib/helpers/error/combinePathErrors.js new file mode 100644 index 0000000..841dbc0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/error/combinePathErrors.js @@ -0,0 +1,22 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function combinePathErrors(err) { + const keys = Object.keys(err.errors || {}); + const len = keys.length; + const msgs = []; + let key; + + for (let i = 0; i < len; ++i) { + key = keys[i]; + if (err === err.errors[key]) { + continue; + } + msgs.push(key + ': ' + err.errors[key].message); + } + + return msgs.join(', '); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/firstKey.js b/backend/node_modules/mongoose/lib/helpers/firstKey.js new file mode 100644 index 0000000..4495d2a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/firstKey.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function firstKey(obj) { + if (obj == null) { + return null; + } + return Object.keys(obj)[0]; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/get.js b/backend/node_modules/mongoose/lib/helpers/get.js new file mode 100644 index 0000000..2a91441 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/get.js @@ -0,0 +1,65 @@ +'use strict'; + +/** + * Simplified lodash.get to work around the annoying null quirk. See: + * https://github.com/lodash/lodash/issues/3659 + * @api private + */ + +module.exports = function get(obj, path, def) { + let parts; + let isPathArray = false; + if (typeof path === 'string') { + if (path.indexOf('.') === -1) { + const _v = getProperty(obj, path); + if (_v == null) { + return def; + } + return _v; + } + + parts = path.split('.'); + } else { + isPathArray = true; + parts = path; + + if (parts.length === 1) { + const _v = getProperty(obj, parts[0]); + if (_v == null) { + return def; + } + return _v; + } + } + let rest = path; + let cur = obj; + for (const part of parts) { + if (cur == null) { + return def; + } + + // `lib/cast.js` depends on being able to get dotted paths in updates, + // like `{ $set: { 'a.b': 42 } }` + if (!isPathArray && cur[rest] != null) { + return cur[rest]; + } + + cur = getProperty(cur, part); + + if (!isPathArray) { + rest = rest.substring(part.length + 1); + } + } + + return cur == null ? def : cur; +}; + +function getProperty(obj, prop) { + if (obj == null) { + return obj; + } + if (obj instanceof Map) { + return obj.get(prop); + } + return obj[prop]; +} diff --git a/backend/node_modules/mongoose/lib/helpers/getConstructorName.js b/backend/node_modules/mongoose/lib/helpers/getConstructorName.js new file mode 100644 index 0000000..5ebe7bb --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/getConstructorName.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * If `val` is an object, returns constructor name, if possible. Otherwise returns undefined. + * @api private + */ + +module.exports = function getConstructorName(val) { + if (val == null) { + return void 0; + } + if (typeof val.constructor !== 'function') { + return void 0; + } + return val.constructor.name; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js b/backend/node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js new file mode 100644 index 0000000..52ed5bf --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/getDefaultBulkwriteResult.js @@ -0,0 +1,18 @@ +'use strict'; + +function getDefaultBulkwriteResult() { + return { + ok: 1, + nInserted: 0, + nUpserted: 0, + nMatched: 0, + nModified: 0, + nRemoved: 0, + upserted: [], + writeErrors: [], + insertedIds: [], + writeConcernErrors: [] + }; +} + +module.exports = getDefaultBulkwriteResult; diff --git a/backend/node_modules/mongoose/lib/helpers/getFunctionName.js b/backend/node_modules/mongoose/lib/helpers/getFunctionName.js new file mode 100644 index 0000000..d1f3a5a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/getFunctionName.js @@ -0,0 +1,10 @@ +'use strict'; + +const functionNameRE = /^function\s*([^\s(]+)/; + +module.exports = function(fn) { + return ( + fn.name || + (fn.toString().trim().match(functionNameRE) || [])[1] + ); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/immediate.js b/backend/node_modules/mongoose/lib/helpers/immediate.js new file mode 100644 index 0000000..73085f7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/immediate.js @@ -0,0 +1,16 @@ +/*! + * Centralize this so we can more easily work around issues with people + * stubbing out `process.nextTick()` in tests using sinon: + * https://github.com/sinonjs/lolex#automatically-incrementing-mocked-time + * See gh-6074 + */ + +'use strict'; + +const nextTick = typeof process !== 'undefined' && typeof process.nextTick === 'function' ? + process.nextTick.bind(process) : + cb => setTimeout(cb, 0); // Fallback for browser build + +module.exports = function immediate(cb) { + return nextTick(cb); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/applySchemaCollation.js b/backend/node_modules/mongoose/lib/helpers/indexes/applySchemaCollation.js new file mode 100644 index 0000000..464210e --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/applySchemaCollation.js @@ -0,0 +1,13 @@ +'use strict'; + +const isTextIndex = require('./isTextIndex'); + +module.exports = function applySchemaCollation(indexKeys, indexOptions, schemaOptions) { + if (isTextIndex(indexKeys)) { + return; + } + + if (Object.hasOwn(schemaOptions, 'collation') && !Object.hasOwn(indexOptions, 'collation')) { + indexOptions.collation = schemaOptions.collation; + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js b/backend/node_modules/mongoose/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js new file mode 100644 index 0000000..b18cf56 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/decorateDiscriminatorIndexOptions.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function decorateDiscriminatorIndexOptions(schema, indexOptions) { + // If the model is a discriminator and has an index, add a + // partialFilterExpression by default so the index will only apply + // to that discriminator. + const discriminatorName = schema.discriminatorMapping?.value; + if (discriminatorName && !('sparse' in indexOptions)) { + const discriminatorKey = schema.options.discriminatorKey; + indexOptions.partialFilterExpression = indexOptions.partialFilterExpression || {}; + indexOptions.partialFilterExpression[discriminatorKey] = discriminatorName; + } + return indexOptions; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/getRelatedIndexes.js b/backend/node_modules/mongoose/lib/helpers/indexes/getRelatedIndexes.js new file mode 100644 index 0000000..e68cb8d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/getRelatedIndexes.js @@ -0,0 +1,63 @@ +'use strict'; + +const hasDollarKeys = require('../query/hasDollarKeys'); + +function getRelatedSchemaIndexes(model, schemaIndexes) { + return getRelatedIndexes({ + baseModelName: model.baseModelName, + discriminatorMapping: model.schema.discriminatorMapping, + indexes: schemaIndexes, + indexesType: 'schema' + }); +} + +function getRelatedDBIndexes(model, dbIndexes) { + return getRelatedIndexes({ + baseModelName: model.baseModelName, + discriminatorMapping: model.schema.discriminatorMapping, + indexes: dbIndexes, + indexesType: 'db' + }); +} + +module.exports = { + getRelatedSchemaIndexes, + getRelatedDBIndexes +}; + +function getRelatedIndexes({ + baseModelName, + discriminatorMapping, + indexes, + indexesType +}) { + const discriminatorKey = discriminatorMapping?.key; + const discriminatorValue = discriminatorMapping?.value; + + if (!discriminatorKey) { + return indexes; + } + + const isChildDiscriminatorModel = Boolean(baseModelName); + if (isChildDiscriminatorModel) { + return indexes.filter(index => { + const partialFilterExpression = getPartialFilterExpression(index, indexesType); + return partialFilterExpression && partialFilterExpression[discriminatorKey] === discriminatorValue; + }); + } + + return indexes.filter(index => { + const partialFilterExpression = getPartialFilterExpression(index, indexesType); + return !partialFilterExpression + || !partialFilterExpression[discriminatorKey] + || (hasDollarKeys(partialFilterExpression[discriminatorKey]) && !('$eq' in partialFilterExpression[discriminatorKey])); + }); +} + +function getPartialFilterExpression(index, indexesType) { + if (indexesType === 'schema') { + const options = index[1]; + return options?.partialFilterExpression; + } + return index.partialFilterExpression; +} diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js b/backend/node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js new file mode 100644 index 0000000..8123dfc --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/isDefaultIdIndex.js @@ -0,0 +1,18 @@ +'use strict'; + +const get = require('../get'); + +module.exports = function isDefaultIdIndex(index) { + if (Array.isArray(index)) { + // Mongoose syntax + const keys = Object.keys(index[0]); + return keys.length === 1 && keys[0] === '_id' && index[0]._id !== 'hashed'; + } + + if (typeof index !== 'object') { + return false; + } + + const key = get(index, 'key', {}); + return Object.keys(key).length === 1 && Object.hasOwn(key, '_id'); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js b/backend/node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js new file mode 100644 index 0000000..22d4212 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/isIndexEqual.js @@ -0,0 +1,95 @@ +'use strict'; + +const get = require('../get'); +const utils = require('../../utils'); +/** + * Given a Mongoose index definition (key + options objects) and a MongoDB server + * index definition, determine if the two indexes are equal. + * + * @param {object} schemaIndexKeysObject the Mongoose index spec + * @param {object} options the Mongoose index definition's options + * @param {object} dbIndex the index in MongoDB as returned by `listIndexes()` + * @api private + */ + +module.exports = function isIndexEqual(schemaIndexKeysObject, options, dbIndex) { + // Special case: text indexes have a special format in the db. For example, + // `{ name: 'text' }` becomes: + // { + // v: 2, + // key: { _fts: 'text', _ftsx: 1 }, + // name: 'name_text', + // ns: 'test.tests', + // weights: { name: 1 }, + // default_language: 'english', + // language_override: 'language', + // textIndexVersion: 3 + // } + if (dbIndex.textIndexVersion != null) { + delete dbIndex.key._fts; + delete dbIndex.key._ftsx; + const weights = { ...dbIndex.weights, ...dbIndex.key }; + if (Object.keys(weights).length !== Object.keys(schemaIndexKeysObject).length) { + return false; + } + for (const prop of Object.keys(weights)) { + if (!(prop in schemaIndexKeysObject)) { + return false; + } + const weight = weights[prop]; + if (weight !== get(options, 'weights.' + prop) && !(weight === 1 && get(options, 'weights.' + prop) == null)) { + return false; + } + } + + if (options['default_language'] !== dbIndex['default_language']) { + return dbIndex['default_language'] === 'english' && options['default_language'] == null; + } + + return true; + } + + const optionKeys = [ + 'unique', + 'partialFilterExpression', + 'sparse', + 'expireAfterSeconds', + 'collation' + ]; + for (const key of optionKeys) { + if (!(key in options) && !(key in dbIndex)) { + continue; + } + if (key === 'collation') { + if (options[key] == null || dbIndex[key] == null) { + return options[key] == null && dbIndex[key] == null; + } + const definedKeys = Object.keys(options.collation); + const schemaCollation = options.collation; + const dbCollation = dbIndex.collation; + for (const opt of definedKeys) { + if (get(schemaCollation, opt) !== get(dbCollation, opt)) { + return false; + } + } + } else if (!utils.deepEqual(options[key], dbIndex[key])) { + return false; + } + } + + const schemaIndexKeys = Object.keys(schemaIndexKeysObject); + const dbIndexKeys = Object.keys(dbIndex.key); + if (schemaIndexKeys.length !== dbIndexKeys.length) { + return false; + } + for (let i = 0; i < schemaIndexKeys.length; ++i) { + if (schemaIndexKeys[i] !== dbIndexKeys[i]) { + return false; + } + if (!utils.deepEqual(schemaIndexKeysObject[schemaIndexKeys[i]], dbIndex.key[dbIndexKeys[i]])) { + return false; + } + } + + return true; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/isIndexSpecEqual.js b/backend/node_modules/mongoose/lib/helpers/indexes/isIndexSpecEqual.js new file mode 100644 index 0000000..32b2a19 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/isIndexSpecEqual.js @@ -0,0 +1,32 @@ +'use strict'; + +/** + * Compares two index specifications to determine if they are equal. + * + * #### Example: + * isIndexSpecEqual({ a: 1, b: 1 }, { a: 1, b: 1 }); // true + * isIndexSpecEqual({ a: 1, b: 1 }, { b: 1, a: 1 }); // false + * isIndexSpecEqual({ a: 1, b: -1 }, { a: 1, b: 1 }); // false + * + * @param {object} spec1 The first index specification to compare. + * @param {object} spec2 The second index specification to compare. + * @returns {boolean} Returns true if the index specifications are equal, otherwise returns false. + */ + +module.exports = function isIndexSpecEqual(spec1, spec2) { + const spec1Keys = Object.keys(spec1); + const spec2Keys = Object.keys(spec2); + + if (spec1Keys.length !== spec2Keys.length) { + return false; + } + + for (let i = 0; i < spec1Keys.length; i++) { + const key = spec1Keys[i]; + if (key !== spec2Keys[i] || spec1[key] !== spec2[key]) { + return false; + } + } + + return true; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/isTextIndex.js b/backend/node_modules/mongoose/lib/helpers/indexes/isTextIndex.js new file mode 100644 index 0000000..fdd98d4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/isTextIndex.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Returns `true` if the given index options have a `text` option. + */ + +module.exports = function isTextIndex(indexKeys) { + let isTextIndex = false; + for (const key of Object.keys(indexKeys)) { + if (indexKeys[key] === 'text') { + isTextIndex = true; + } + } + + return isTextIndex; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/indexes/isTimeseriesIndex.js b/backend/node_modules/mongoose/lib/helpers/indexes/isTimeseriesIndex.js new file mode 100644 index 0000000..0a4512b --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/indexes/isTimeseriesIndex.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Returns `true` if the given index matches the schema's `timestamps` options + */ + +module.exports = function isTimeseriesIndex(dbIndex, schemaOptions) { + if (schemaOptions.timeseries == null) { + return false; + } + const { timeField, metaField } = schemaOptions.timeseries; + if (typeof timeField !== 'string' || typeof metaField !== 'string') { + return false; + } + return Object.keys(dbIndex.key).length === 2 && dbIndex.key[timeField] === 1 && dbIndex.key[metaField] === 1; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/isAsyncFunction.js b/backend/node_modules/mongoose/lib/helpers/isAsyncFunction.js new file mode 100644 index 0000000..679590e --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isAsyncFunction.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = function isAsyncFunction(v) { + return ( + typeof v === 'function' && + v.constructor && + v.constructor.name === 'AsyncFunction' + ); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/isBsonType.js b/backend/node_modules/mongoose/lib/helpers/isBsonType.js new file mode 100644 index 0000000..d3a71f0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isBsonType.js @@ -0,0 +1,15 @@ +'use strict'; + +/** + * Determines if `obj` has the given BSON type. + * @api private + */ + +function isBsonType(obj, typename) { + return ( + obj != null && + obj._bsontype === typename + ); +} + +module.exports = isBsonType; diff --git a/backend/node_modules/mongoose/lib/helpers/isMongooseObject.js b/backend/node_modules/mongoose/lib/helpers/isMongooseObject.js new file mode 100644 index 0000000..85466a1 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isMongooseObject.js @@ -0,0 +1,22 @@ +'use strict'; + +const isMongooseArray = require('../types/array/isMongooseArray').isMongooseArray; +/** + * Returns if `v` is a mongoose object that has a `toObject()` method we can use. + * + * This is for compatibility with libs like Date.js which do foolish things to Natives. + * + * @param {any} v + * @api private + */ + +module.exports = function(v) { + return ( + v != null && ( + isMongooseArray(v) || // Array or Document Array + v.$__ != null || // Document + v.isMongooseBuffer || // Buffer + v.$isMongooseMap // Map + ) + ); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/isObject.js b/backend/node_modules/mongoose/lib/helpers/isObject.js new file mode 100644 index 0000000..9d88788 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isObject.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Determines if `arg` is an object. + * + * @param {object|Array|string|Function|RegExp|any} arg + * @api private + * @return {boolean} + */ + +module.exports = function(arg) { + return ( + Buffer.isBuffer(arg) || + Object.prototype.toString.call(arg) === '[object Object]' + ); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/isPOJO.js b/backend/node_modules/mongoose/lib/helpers/isPOJO.js new file mode 100644 index 0000000..2f1a00f --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isPOJO.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function isPOJO(arg) { + if (arg == null || typeof arg !== 'object') { + return false; + } + const proto = Object.getPrototypeOf(arg); + // Prototype may be null if you used `Object.create(null)` + // Checking `proto`'s constructor is safe because `getPrototypeOf()` + // explicitly crosses the boundary from object data to object metadata + return !proto || proto.constructor.name === 'Object'; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/isPromise.js b/backend/node_modules/mongoose/lib/helpers/isPromise.js new file mode 100644 index 0000000..0da827e --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isPromise.js @@ -0,0 +1,6 @@ +'use strict'; +function isPromise(val) { + return !!val && (typeof val === 'object' || typeof val === 'function') && typeof val.then === 'function'; +} + +module.exports = isPromise; diff --git a/backend/node_modules/mongoose/lib/helpers/isSimpleValidator.js b/backend/node_modules/mongoose/lib/helpers/isSimpleValidator.js new file mode 100644 index 0000000..07aadcd --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/isSimpleValidator.js @@ -0,0 +1,22 @@ +'use strict'; + +/** + * Determines if `arg` is a flat object. + * + * @param {object|Array|string|Function|RegExp|any} arg + * @api private + * @return {boolean} + */ + +module.exports = function isSimpleValidator(obj) { + const keys = Object.keys(obj); + let result = true; + for (let i = 0, len = keys.length; i < len; ++i) { + if (typeof obj[keys[i]] === 'object' && obj[keys[i]] !== null) { + result = false; + break; + } + } + + return result; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/minimize.js b/backend/node_modules/mongoose/lib/helpers/minimize.js new file mode 100644 index 0000000..6bee18d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/minimize.js @@ -0,0 +1,41 @@ +'use strict'; + +const { isPOJO } = require('../utils'); + +module.exports = minimize; + +/** + * Minimizes an object, removing undefined values and empty objects + * + * @param {object} object to minimize + * @return {object|undefined} + * @api private + */ + +function minimize(obj) { + const keys = Object.keys(obj); + let i = keys.length; + let hasKeys; + let key; + let val; + + while (i--) { + key = keys[i]; + val = obj[key]; + + if (isPOJO(val)) { + obj[key] = minimize(val); + } + + if (undefined === obj[key]) { + delete obj[key]; + continue; + } + + hasKeys = true; + } + + return hasKeys + ? obj + : undefined; +} diff --git a/backend/node_modules/mongoose/lib/helpers/model/applyDefaultsToPOJO.js b/backend/node_modules/mongoose/lib/helpers/model/applyDefaultsToPOJO.js new file mode 100644 index 0000000..0570c69 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/applyDefaultsToPOJO.js @@ -0,0 +1,52 @@ +'use strict'; + +module.exports = function applyDefaultsToPOJO(doc, schema) { + const paths = Object.keys(schema.paths); + const plen = paths.length; + + for (let i = 0; i < plen; ++i) { + let curPath = ''; + const p = paths[i]; + + const type = schema.paths[p]; + const path = type.splitPath(); + const len = path.length; + let doc_ = doc; + for (let j = 0; j < len; ++j) { + if (doc_ == null) { + break; + } + + const piece = path[j]; + curPath += (!curPath.length ? '' : '.') + piece; + + if (j === len - 1) { + if (typeof doc_[piece] !== 'undefined') { + if (type.$isSingleNested) { + applyDefaultsToPOJO(doc_[piece], type.schema); + } else if (type.$isMongooseDocumentArray && Array.isArray(doc_[piece])) { + doc_[piece].forEach(el => applyDefaultsToPOJO(el, type.schema)); + } + + break; + } + + const def = type.getDefault(doc, false, { skipCast: true }); + if (typeof def !== 'undefined') { + doc_[piece] = def; + + if (type.$isSingleNested) { + applyDefaultsToPOJO(def, type.schema); + } else if (type.$isMongooseDocumentArray && Array.isArray(def)) { + def.forEach(el => applyDefaultsToPOJO(el, type.schema)); + } + } + } else { + if (doc_[piece] == null) { + doc_[piece] = {}; + } + doc_ = doc_[piece]; + } + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/applyHooks.js b/backend/node_modules/mongoose/lib/helpers/model/applyHooks.js new file mode 100644 index 0000000..cb68a7f --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/applyHooks.js @@ -0,0 +1,151 @@ +'use strict'; + +const { buildMiddlewareFilter } = require('../buildMiddlewareFilter'); + +/*! + * ignore + */ + +module.exports = applyHooks; + +/*! + * ignore + */ + +applyHooks.middlewareFunctions = [ + 'deleteOne', + 'remove', + 'save', + 'updateOne', + 'validate', + 'init' +]; + +/*! + * ignore + */ + +const alreadyHookedFunctions = new Set(applyHooks.middlewareFunctions.flatMap(fn => ([fn, `$__${fn}`]))); + +/** + * Register hooks for this model + * + * @param {Model} model + * @param {Schema} schema + * @param {object} options + * @api private + */ + +function applyHooks(model, schema, options) { + options = options || {}; + + const kareemOptions = { + useErrorHandlers: true, + numCallbackParams: 1, + nullResultByDefault: true, + contextParameter: true + }; + const objToDecorate = options.decorateDoc ? model : model.prototype; + model.$appliedHooks = true; + for (const key of Object.keys(schema.paths)) { + let type = schema.paths[key]; + let childModel = null; + + const result = findChildModel(type); + if (result) { + childModel = result.childModel; + type = result.type; + } else { + continue; + } + + if (childModel.$appliedHooks) { + continue; + } + + applyHooks(childModel, type.schema, { + ...options, + decorateDoc: false, + isChildSchema: true + }); + if (childModel.discriminators != null) { + const keys = Object.keys(childModel.discriminators); + for (const key of keys) { + applyHooks(childModel.discriminators[key], + childModel.discriminators[key].schema, options); + } + } + } + + // Built-in hooks rely on hooking internal functions in order to support + // promises and make it so that `doc.save.toString()` provides meaningful + // information. + + const middleware = schema._getDocumentMiddleware(); + + model._middleware = middleware; + + objToDecorate.$__init = middleware. + createWrapperSync('init', objToDecorate.$__init, null, { + ...kareemOptions, + getOptions: (args) => { + const opts = args[1]; + return { + pre: { filter: buildMiddlewareFilter(opts, 'pre') }, + post: { filter: buildMiddlewareFilter(opts, 'post') } + }; + } + }); + + // Support hooks for custom methods + const customMethods = Object.keys(schema.methods); + const customMethodOptions = Object.assign({}, kareemOptions, { + // Only use `checkForPromise` for custom methods, because mongoose + // query thunks are not as consistent as I would like about returning + // a nullish value rather than the query. If a query thunk returns + // a query, `checkForPromise` causes infinite recursion + checkForPromise: true + }); + for (const method of customMethods) { + if (alreadyHookedFunctions.has(method)) { + continue; + } + if (!middleware.hasHooks(method)) { + // Don't wrap if there are no hooks for the custom method to avoid + // surprises. Also, `createWrapper()` enforces consistent async, + // so wrapping a sync method would break it. + continue; + } + const originalMethod = objToDecorate[method]; + objToDecorate[`$__${method}`] = objToDecorate[method]; + objToDecorate[method] = middleware. + createWrapper(method, originalMethod, null, customMethodOptions); + } +} + +/** + * Check if there is an embedded schematype in the given schematype. Handles drilling down into primitive + * arrays and maps in case of array of array of subdocs or map of subdocs. + * + * @param {SchemaType} curType + * @returns {{ childModel: Model | typeof Subdocument, curType: SchemaType } | null} + */ + +function findChildModel(curType) { + if (curType.$isSingleNested || curType.$isMongooseDocumentArray) { + return { childModel: curType.Constructor, type: curType }; + } + if (curType.instance === 'Array') { + const embedded = curType.getEmbeddedSchemaType(); + if (embedded) { + return findChildModel(embedded); + } + } + if (curType.instance === 'Map') { + const mapType = curType.getEmbeddedSchemaType(); + if (mapType) { + return findChildModel(mapType); + } + } + return null; +} diff --git a/backend/node_modules/mongoose/lib/helpers/model/applyMethods.js b/backend/node_modules/mongoose/lib/helpers/model/applyMethods.js new file mode 100644 index 0000000..748315a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/applyMethods.js @@ -0,0 +1,70 @@ +'use strict'; + +const get = require('../get'); +const utils = require('../../utils'); + +/** + * Register methods for this model + * + * @param {Model} model + * @param {Schema} schema + * @api private + */ + +module.exports = function applyMethods(model, schema) { + const Model = require('../../model'); + + function apply(method, schema) { + Object.defineProperty(model.prototype, method, { + get: function() { + const h = {}; + for (const k in schema.methods[method]) { + h[k] = schema.methods[method][k].bind(this); + } + return h; + }, + configurable: true + }); + } + for (const method of Object.keys(schema.methods)) { + const fn = schema.methods[method]; + if (Object.hasOwn(schema.tree, method)) { + throw new Error('You have a method and a property in your schema both ' + + 'named "' + method + '"'); + } + + // Avoid making custom methods if user sets a method to itself, e.g. + // `schema.method(save, Document.prototype.save)`. Can happen when + // calling `loadClass()` with a class that `extends Document`. See gh-12254 + if (typeof fn === 'function' && + Model.prototype[method] === fn) { + delete schema.methods[method]; + continue; + } + + if (schema.reserved[method] && + !get(schema, `methodOptions.${method}.suppressWarning`, false)) { + utils.warn(`mongoose: the method name "${method}" is used by mongoose ` + + 'internally, overwriting it may cause bugs. If you\'re sure you know ' + + 'what you\'re doing, you can suppress this error by using ' + + `\`schema.method('${method}', fn, { suppressWarning: true })\`.`); + } + if (typeof fn === 'function') { + model.prototype[method] = fn; + } else { + apply(method, schema); + } + } + + // Recursively call `applyMethods()` on child schemas + model.$appliedMethods = true; + for (const key of Object.keys(schema.paths)) { + const type = schema.paths[key]; + if (type.$isSingleNested && !type.Constructor.$appliedMethods) { + applyMethods(type.Constructor, type.schema); + } + if (type.$isMongooseDocumentArray && !type.Constructor.$appliedMethods) { + applyMethods(type.Constructor, type.schema); + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/applyStaticHooks.js b/backend/node_modules/mongoose/lib/helpers/model/applyStaticHooks.js new file mode 100644 index 0000000..eb0caaf --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/applyStaticHooks.js @@ -0,0 +1,33 @@ +'use strict'; + +const { queryMiddlewareFunctions, aggregateMiddlewareFunctions, modelMiddlewareFunctions, documentMiddlewareFunctions } = require('../../constants'); + +const middlewareFunctions = Array.from( + new Set([ + ...queryMiddlewareFunctions, + ...aggregateMiddlewareFunctions, + ...modelMiddlewareFunctions, + ...documentMiddlewareFunctions + ]) +); + +module.exports = function applyStaticHooks(model, hooks, statics) { + hooks = hooks.filter(hook => { + // If the custom static overwrites an existing middleware, don't apply + // middleware to it by default. This avoids a potential backwards breaking + // change with plugins like `mongoose-delete` that use statics to overwrite + // built-in Mongoose functions. + if (middlewareFunctions.indexOf(hook.name) !== -1) { + return !!hook.model; + } + return hook.model !== false; + }); + + for (const key of Object.keys(statics)) { + if (hooks.hasHooks(key)) { + const original = model[key]; + + model[key] = hooks.createWrapper(key, original); + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/applyStatics.js b/backend/node_modules/mongoose/lib/helpers/model/applyStatics.js new file mode 100644 index 0000000..d94d91c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/applyStatics.js @@ -0,0 +1,13 @@ +'use strict'; + +/** + * Register statics for this model + * @param {Model} model + * @param {Schema} schema + * @api private + */ +module.exports = function applyStatics(model, schema) { + for (const i in schema.statics) { + model[i] = schema.statics[i]; + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/castBulkWrite.js b/backend/node_modules/mongoose/lib/helpers/model/castBulkWrite.js new file mode 100644 index 0000000..67b18fc --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/castBulkWrite.js @@ -0,0 +1,322 @@ +'use strict'; + +const MongooseError = require('../../error/mongooseError'); +const getDiscriminatorByValue = require('../../helpers/discriminator/getDiscriminatorByValue'); +const applyTimestampsToChildren = require('../update/applyTimestampsToChildren'); +const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate'); +const cast = require('../../cast'); +const castUpdate = require('../query/castUpdate'); +const clone = require('../clone'); +const decorateUpdateWithVersionKey = require('../update/decorateUpdateWithVersionKey'); +const { inspect } = require('util'); +const setDefaultsOnInsert = require('../setDefaultsOnInsert'); + +/** + * Given a model and a bulkWrite op, return a thunk that handles casting and + * validating the individual op. + * @param {Model} originalModel + * @param {object} op + * @param {object} [options] + * @api private + */ + +module.exports = function castBulkWrite(originalModel, op, options) { + let now; + const timestampOpts = originalModel.schema.get('timestamps'); + if (typeof timestampOpts?.currentTime === 'function') { + now = timestampOpts.currentTime(); + } else { + now = originalModel.base.now(); + } + + if (op['insertOne']) { + return callback => module.exports.castInsertOne(originalModel, op['insertOne'], options).then(() => callback(null), err => callback(err)); + } else if (op['updateOne']) { + return (callback) => { + try { + module.exports.castUpdateOne(originalModel, op['updateOne'], options, now); + callback(null); + } catch (err) { + callback(err); + } + }; + } else if (op['updateMany']) { + return (callback) => { + try { + module.exports.castUpdateMany(originalModel, op['updateMany'], options, now); + callback(null); + } catch (err) { + callback(err); + } + }; + } else if (op['replaceOne']) { + return (callback) => { + module.exports.castReplaceOne(originalModel, op['replaceOne'], options).then(() => callback(null), err => callback(err)); + }; + } else if (op['deleteOne']) { + return (callback) => { + try { + module.exports.castDeleteOne(originalModel, op['deleteOne']); + callback(null); + } catch (err) { + callback(err); + } + }; + } else if (op['deleteMany']) { + return (callback) => { + try { + module.exports.castDeleteMany(originalModel, op['deleteMany']); + callback(null); + } catch (err) { + callback(err); + } + }; + } else { + return (callback) => { + const error = new MongooseError(`Invalid op passed to \`bulkWrite()\`: ${inspect(op)}`); + callback(error, null); + }; + } +}; + +module.exports.castInsertOne = async function castInsertOne(originalModel, insertOne, options) { + const model = decideModelByObject(originalModel, insertOne['document']); + + const doc = new model(insertOne['document']); + if (model.schema.options.timestamps && getTimestampsOpt(insertOne, options)) { + doc.initializeTimestamps(); + } + if (options.session != null) { + doc.$session(options.session); + } + const versionKey = model?.schema?.options?.versionKey; + if (versionKey && doc[versionKey] == null) { + doc[versionKey] = 0; + } + insertOne['document'] = doc; + + if (options.skipValidation || insertOne.skipValidation) { + return insertOne; + } + + await insertOne['document'].$validate(); + return insertOne; +}; + +module.exports.castUpdateOne = function castUpdateOne(originalModel, updateOne, options, now) { + if (!updateOne['filter']) { + throw new Error('Must provide a filter object.'); + } + if (!updateOne['update']) { + throw new Error('Must provide an update object.'); + } + + const model = decideModelByObject(originalModel, updateOne['filter']); + const schema = model.schema; + const strict = options.strict ?? model.schema.options.strict; + + const update = clone(updateOne['update']); + + _addDiscriminatorToObject(schema, updateOne['filter']); + + const doInitTimestamps = getTimestampsOpt(updateOne, options); + + if (model.schema.$timestamps != null && doInitTimestamps) { + const createdAt = model.schema.$timestamps.createdAt; + const updatedAt = model.schema.$timestamps.updatedAt; + applyTimestampsToUpdate(now, createdAt, updatedAt, update, { + timestamps: updateOne.timestamps, + overwriteImmutable: updateOne.overwriteImmutable + }); + } + + if (doInitTimestamps) { + applyTimestampsToChildren(now, update, model.schema); + } + + const globalSetDefaultsOnInsert = originalModel.base.options.setDefaultsOnInsert; + const shouldSetDefaultsOnInsert = updateOne.setDefaultsOnInsert ?? globalSetDefaultsOnInsert; + if (shouldSetDefaultsOnInsert !== false) { + setDefaultsOnInsert(updateOne['filter'], model.schema, update, { + setDefaultsOnInsert: true, + upsert: updateOne.upsert + }); + } + + decorateUpdateWithVersionKey( + update, + updateOne, + model.schema.options.versionKey + ); + + updateOne['filter'] = cast(model.schema, updateOne['filter'], { + strict: strict, + upsert: updateOne.upsert + }); + updateOne['update'] = castUpdate(model.schema, update, { + strict: strict, + upsert: updateOne.upsert, + arrayFilters: updateOne.arrayFilters, + overwriteDiscriminatorKey: updateOne.overwriteDiscriminatorKey, + overwriteImmutable: updateOne.overwriteImmutable + }, model, updateOne['filter']); + + return updateOne; +}; + +module.exports.castUpdateMany = function castUpdateMany(originalModel, updateMany, options, now) { + if (!updateMany['filter']) { + throw new Error('Must provide a filter object.'); + } + if (!updateMany['update']) { + throw new Error('Must provide an update object.'); + } + + const model = decideModelByObject(originalModel, updateMany['filter']); + const schema = model.schema; + const strict = options.strict ?? model.schema.options.strict; + + const globalSetDefaultsOnInsert = originalModel.base.options.setDefaultsOnInsert; + const shouldSetDefaultsOnInsert = updateMany.setDefaultsOnInsert ?? globalSetDefaultsOnInsert; + + if (shouldSetDefaultsOnInsert !== false) { + setDefaultsOnInsert(updateMany['filter'], model.schema, updateMany['update'], { + setDefaultsOnInsert: true, + upsert: updateMany.upsert + }); + } + + const doInitTimestamps = getTimestampsOpt(updateMany, options); + + if (model.schema.$timestamps != null && doInitTimestamps) { + const createdAt = model.schema.$timestamps.createdAt; + const updatedAt = model.schema.$timestamps.updatedAt; + applyTimestampsToUpdate(now, createdAt, updatedAt, updateMany['update'], { + timestamps: updateMany.timestamps, + overwriteImmutable: updateMany.overwriteImmutable + }); + } + if (doInitTimestamps) { + applyTimestampsToChildren(now, updateMany['update'], model.schema); + } + + _addDiscriminatorToObject(schema, updateMany['filter']); + + decorateUpdateWithVersionKey( + updateMany['update'], + updateMany, + model.schema.options.versionKey + ); + + updateMany['filter'] = cast(model.schema, updateMany['filter'], { + strict: strict, + upsert: updateMany.upsert + }); + + updateMany['update'] = castUpdate(model.schema, updateMany['update'], { + strict: strict, + upsert: updateMany.upsert, + arrayFilters: updateMany.arrayFilters, + overwriteDiscriminatorKey: updateMany.overwriteDiscriminatorKey, + overwriteImmutable: updateMany.overwriteImmutable + }, model, updateMany['filter']); +}; + +module.exports.castReplaceOne = async function castReplaceOne(originalModel, replaceOne, options) { + const model = decideModelByObject(originalModel, replaceOne['filter']); + const schema = model.schema; + const strict = options.strict ?? model.schema.options.strict; + + _addDiscriminatorToObject(schema, replaceOne['filter']); + replaceOne['filter'] = cast(model.schema, replaceOne['filter'], { + strict: strict, + upsert: replaceOne.upsert + }); + + // set `skipId`, otherwise we get "_id field cannot be changed" + const doc = new model(replaceOne['replacement'], strict, { skipId: true }); + if (model.schema.options.timestamps && getTimestampsOpt(replaceOne, options)) { + doc.initializeTimestamps(); + } + if (options.session != null) { + doc.$session(options.session); + } + const versionKey = model?.schema?.options?.versionKey; + if (versionKey && doc[versionKey] == null) { + doc[versionKey] = 0; + } + replaceOne['replacement'] = doc; + + if (options.skipValidation || replaceOne.skipValidation) { + replaceOne['replacement'] = replaceOne['replacement'].toBSON(); + return; + } + + await replaceOne['replacement'].$validate(); + replaceOne['replacement'] = replaceOne['replacement'].toBSON(); +}; + +module.exports.castDeleteOne = function castDeleteOne(originalModel, deleteOne) { + const model = decideModelByObject(originalModel, deleteOne['filter']); + const schema = model.schema; + + _addDiscriminatorToObject(schema, deleteOne['filter']); + + deleteOne['filter'] = cast(model.schema, deleteOne['filter']); +}; + +module.exports.castDeleteMany = function castDeleteMany(originalModel, deleteMany) { + const model = decideModelByObject(originalModel, deleteMany['filter']); + const schema = model.schema; + + _addDiscriminatorToObject(schema, deleteMany['filter']); + + deleteMany['filter'] = cast(model.schema, deleteMany['filter']); +}; + +module.exports.cast = { + insertOne: module.exports.castInsertOne, + updateOne: module.exports.castUpdateOne, + updateMany: module.exports.castUpdateMany, + replaceOne: module.exports.castReplaceOne, + deleteOne: module.exports.castDeleteOne, + deleteMany: module.exports.castDeleteMany +}; + +function _addDiscriminatorToObject(schema, obj) { + if (schema == null) { + return; + } + if (schema.discriminatorMapping && !schema.discriminatorMapping.isRoot) { + obj[schema.discriminatorMapping.key] = schema.discriminatorMapping.value; + } +} + +/** + * gets discriminator model if discriminator key is present in object + * @api private + */ + +function decideModelByObject(model, object) { + const discriminatorKey = model.schema.options.discriminatorKey; + if (object != null && Object.hasOwn(object, discriminatorKey)) { + model = getDiscriminatorByValue(model.discriminators, object[discriminatorKey]) || model; + } + return model; +} + +/** + * gets timestamps option for a given operation. If the option is set within an individual operation, use it. Otherwise, use the global timestamps option configured in the `bulkWrite` options. Overall default is `true`. + * @api private + */ + +function getTimestampsOpt(opCommand, options) { + const opLevelOpt = opCommand.timestamps; + const bulkLevelOpt = options.timestamps; + if (opLevelOpt != null) { + return opLevelOpt; + } else if (bulkLevelOpt != null) { + return bulkLevelOpt; + } + return true; +} diff --git a/backend/node_modules/mongoose/lib/helpers/model/decorateBulkWriteResult.js b/backend/node_modules/mongoose/lib/helpers/model/decorateBulkWriteResult.js new file mode 100644 index 0000000..6cc9263 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/decorateBulkWriteResult.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function decorateBulkWriteResult(resultOrError, validationErrors, results) { + resultOrError.mongoose = resultOrError.mongoose || {}; + resultOrError.mongoose.validationErrors = validationErrors; + resultOrError.mongoose.results = results; + return resultOrError; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/discriminator.js b/backend/node_modules/mongoose/lib/helpers/model/discriminator.js new file mode 100644 index 0000000..7faea75 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/discriminator.js @@ -0,0 +1,265 @@ +'use strict'; + +const Mixed = require('../../schema/mixed'); +const applyBuiltinPlugins = require('../schema/applyBuiltinPlugins'); +const clone = require('../clone'); +const defineKey = require('../document/compile').defineKey; +const get = require('../get'); +const utils = require('../../utils'); +const mergeDiscriminatorSchema = require('../../helpers/discriminator/mergeDiscriminatorSchema'); + +const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = { + toJSON: true, + toObject: true, + _id: true, + id: true, + virtuals: true, + methods: true, + statics: true +}; + +/** + * Validate fields declared on the child schema when either schema is configured for encryption. Specifically, this function ensures that: + * + * - any encrypted fields are declared on exactly one of the schemas (not both) + * - encrypted fields cannot be declared on either the parent or child schema, where the other schema declares the same field without encryption. + * + * @param {Schema} parentSchema + * @param {Schema} childSchema + */ +function validateDiscriminatorSchemasForEncryption(parentSchema, childSchema) { + if (parentSchema.encryptionType() == null && childSchema.encryptionType() == null) return; + + const allSharedNestedPaths = setIntersection( + allNestedPaths(parentSchema), + allNestedPaths(childSchema) + ); + + for (const path of allSharedNestedPaths) { + if (parentSchema._hasEncryptedField(path) && childSchema._hasEncryptedField(path)) { + throw new Error(`encrypted fields cannot be declared on both the base schema and the child schema in a discriminator. path=${path}`); + } + + if (parentSchema._hasEncryptedField(path) || childSchema._hasEncryptedField(path)) { + throw new Error(`encrypted fields cannot have the same path as a non-encrypted field for discriminators. path=${path}`); + } + } + + function allNestedPaths(schema) { + return [...Object.keys(schema.paths), ...Object.keys(schema.singleNestedPaths)]; + } + + /** + * @param {Iterable} i1 + * @param {Iterable} i2 + */ + function* setIntersection(i1, i2) { + const s1 = new Set(i1); + for (const item of i2) { + if (s1.has(item)) { + yield item; + } + } + } +} + +/*! + * ignore + */ + +module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks, overwriteExisting) { + if (!schema?.instanceOfSchema) { + throw new Error('You must pass a valid discriminator Schema'); + } + + mergeHooks = mergeHooks == null ? true : mergeHooks; + + if (model.schema.discriminatorMapping && + !model.schema.discriminatorMapping.isRoot) { + throw new Error('Discriminator "' + name + + '" can only be a discriminator of the root model'); + } + + if (applyPlugins) { + const applyPluginsToDiscriminators = get(model.base, + 'options.applyPluginsToDiscriminators', false) || !mergeHooks; + // Even if `applyPluginsToDiscriminators` isn't set, we should still apply + // global plugins to schemas embedded in the discriminator schema (gh-7370) + model.base._applyPlugins(schema, { + skipTopLevel: !applyPluginsToDiscriminators + }); + } else if (!mergeHooks) { + applyBuiltinPlugins(schema); + } + + const key = model.schema.options.discriminatorKey; + + const existingPath = model.schema.path(key); + if (existingPath != null) { + if (!utils.hasUserDefinedProperty(existingPath.options, 'select')) { + existingPath.options.select = true; + } + existingPath.options.$skipDiscriminatorCheck = true; + } else { + const baseSchemaAddition = {}; + baseSchemaAddition[key] = { + default: void 0, + select: true, + $skipDiscriminatorCheck: true + }; + baseSchemaAddition[key][model.schema.options.typeKey] = String; + model.schema.add(baseSchemaAddition); + defineKey({ + prop: key, + prototype: model.prototype, + options: model.schema.options + }); + } + + if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) { + throw new Error('Discriminator "' + name + + '" cannot have field with name "' + key + '"'); + } + + let value = name; + if ((typeof tiedValue === 'string' && tiedValue.length) || tiedValue != null) { + value = tiedValue; + } + + validateDiscriminatorSchemasForEncryption(model.schema, schema); + + function merge(schema, baseSchema) { + // Retain original schema before merging base schema + schema._baseSchema = baseSchema; + if (baseSchema.paths._id && + baseSchema.paths._id.options && + !baseSchema.paths._id.options.auto) { + schema.remove('_id'); + } + + // Find conflicting paths: if something is a path in the base schema + // and a nested path in the child schema, overwrite the base schema path. + // See gh-6076 + const baseSchemaPaths = Object.keys(baseSchema.paths); + const conflictingPaths = []; + + for (const path of baseSchemaPaths) { + if (schema.nested[path]) { + conflictingPaths.push(path); + continue; + } + + if (path.indexOf('.') === -1) { + continue; + } + const sp = path.split('.').slice(0, -1); + let cur = ''; + for (const piece of sp) { + cur += (cur.length ? '.' : '') + piece; + if (schema.paths[cur] instanceof Mixed || + schema.singleNestedPaths[cur] instanceof Mixed) { + conflictingPaths.push(path); + } + } + } + + // Shallow clone `obj` so we can add additional properties without modifying original + // schema. `Schema.prototype.clone()` copies `obj` by reference, no cloning. + schema.obj = { ...schema.obj }; + mergeDiscriminatorSchema(schema, baseSchema); + schema._gatherChildSchemas(); + + // Clean up conflicting paths _after_ merging re: gh-6076 + for (const conflictingPath of conflictingPaths) { + delete schema.paths[conflictingPath]; + } + + // Rebuild schema models because schemas may have been merged re: #7884 + schema.childSchemas.forEach(obj => { + obj.model.prototype.$__setSchema(obj.schema); + }); + + const obj = {}; + obj[key] = { + default: value, + select: true, + set: function(newName) { + if (newName === value || (Array.isArray(value) && utils.deepEqual(newName, value))) { + return value; + } + throw new Error('Can\'t set discriminator key "' + key + '"'); + }, + $skipDiscriminatorCheck: true + }; + obj[key][schema.options.typeKey] = existingPath ? existingPath.options[schema.options.typeKey] : String; + schema.add(obj); + + schema.discriminatorMapping = { key: key, value: value, isRoot: false }; + + if (baseSchema.options.collection) { + schema.options.collection = baseSchema.options.collection; + } + const toJSON = schema.options.toJSON; + const toObject = schema.options.toObject; + const _id = schema.options._id; + const id = schema.options.id; + + const keys = Object.keys(schema.options); + schema.options.discriminatorKey = baseSchema.options.discriminatorKey; + const userProvidedOptions = schema._userProvidedOptions; + for (const _key of keys) { + if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) { + // Use `schema.options` in `deepEqual()` because of `discriminatorKey` + // set above. We don't allow customizing discriminator key, always + // overwrite. See gh-9238 + if (_key in userProvidedOptions && !utils.deepEqual(schema.options[_key], baseSchema.options[_key])) { + throw new Error('Can\'t customize discriminator option ' + _key + + ' (can only modify ' + + Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') + + ')'); + } + } + } + schema.options = clone(baseSchema.options); + + for (const _key of Object.keys(userProvidedOptions)) { + schema.options[_key] = userProvidedOptions[_key]; + } + if (toJSON) schema.options.toJSON = toJSON; + if (toObject) schema.options.toObject = toObject; + if (typeof _id !== 'undefined') { + schema.options._id = _id; + } + schema.options.id = id; + if (mergeHooks) { + schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks); + } + if (applyPlugins) { + schema.plugins = Array.prototype.slice.call(baseSchema.plugins); + } + schema.callQueue = baseSchema.callQueue.concat(schema.callQueue); + delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema + } + + // merges base schema into new discriminator schema and sets new type field. + merge(schema, model.schema); + + if (!model.discriminators) { + model.discriminators = {}; + } + + if (!model.schema.discriminatorMapping) { + model.schema.discriminatorMapping = { key: key, value: null, isRoot: true }; + } + if (!model.schema.discriminators) { + model.schema.discriminators = {}; + } + + model.schema.discriminators[name] = schema; + + if (model.discriminators[name] && !schema.options.overwriteModels && !overwriteExisting) { + throw new Error('Discriminator with name "' + name + '" already exists'); + } + + return schema; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/model/pushNestedArrayPaths.js b/backend/node_modules/mongoose/lib/helpers/model/pushNestedArrayPaths.js new file mode 100644 index 0000000..7f234fa --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/model/pushNestedArrayPaths.js @@ -0,0 +1,15 @@ +'use strict'; + +module.exports = function pushNestedArrayPaths(paths, nestedArray, path) { + if (nestedArray == null) { + return; + } + + for (let i = 0; i < nestedArray.length; ++i) { + if (Array.isArray(nestedArray[i])) { + pushNestedArrayPaths(paths, nestedArray[i], path + '.' + i); + } else { + paths.push(path + '.' + i); + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/omitUndefined.js b/backend/node_modules/mongoose/lib/helpers/omitUndefined.js new file mode 100644 index 0000000..5c9eb88 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/omitUndefined.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = function omitUndefined(val) { + if (val == null || typeof val !== 'object') { + return val; + } + if (Array.isArray(val)) { + for (let i = val.length - 1; i >= 0; --i) { + if (val[i] === undefined) { + val.splice(i, 1); + } + } + } + for (const key of Object.keys(val)) { + if (val[key] === void 0) { + delete val[key]; + } + } + return val; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/once.js b/backend/node_modules/mongoose/lib/helpers/once.js new file mode 100644 index 0000000..dfa5ee7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/once.js @@ -0,0 +1,12 @@ +'use strict'; + +module.exports = function once(fn) { + let called = false; + return function() { + if (called) { + return; + } + called = true; + return fn.apply(null, arguments); + }; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/parallelLimit.js b/backend/node_modules/mongoose/lib/helpers/parallelLimit.js new file mode 100644 index 0000000..a2170e4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/parallelLimit.js @@ -0,0 +1,37 @@ +'use strict'; + +module.exports = parallelLimit; + +/*! + * ignore + */ + +async function parallelLimit(params, fn, limit) { + if (limit <= 0) { + throw new Error('Limit must be positive'); + } + + if (params.length === 0) { + return []; + } + + const results = []; + const executing = new Set(); + + for (let index = 0; index < params.length; index++) { + const param = params[index]; + const p = fn(param, index); + results.push(p); + + executing.add(p); + + const clean = () => executing.delete(p); + p.then(clean).catch(clean); + + if (executing.size >= limit) { + await Promise.race(executing); + } + } + + return Promise.all(results); +} diff --git a/backend/node_modules/mongoose/lib/helpers/path/parentPaths.js b/backend/node_modules/mongoose/lib/helpers/path/parentPaths.js new file mode 100644 index 0000000..6822c8c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/path/parentPaths.js @@ -0,0 +1,18 @@ +'use strict'; + +const dotRE = /\./g; +module.exports = function parentPaths(path) { + if (path.indexOf('.') === -1) { + return [path]; + } + const pieces = path.split(dotRE); + const len = pieces.length; + const ret = new Array(len); + let cur = ''; + for (let i = 0; i < len; ++i) { + cur += (cur.length !== 0) ? '.' + pieces[i] : pieces[i]; + ret[i] = cur; + } + + return ret; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/path/setDottedPath.js b/backend/node_modules/mongoose/lib/helpers/path/setDottedPath.js new file mode 100644 index 0000000..b17d549 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/path/setDottedPath.js @@ -0,0 +1,33 @@ +'use strict'; + +const specialProperties = require('../specialProperties'); + + +module.exports = function setDottedPath(obj, path, val) { + if (path.indexOf('.') === -1) { + if (specialProperties.has(path)) { + return; + } + + obj[path] = val; + return; + } + const parts = path.split('.'); + + const last = parts.pop(); + let cur = obj; + for (const part of parts) { + if (specialProperties.has(part)) { + continue; + } + if (cur[part] == null) { + cur[part] = {}; + } + + cur = cur[part]; + } + + if (!specialProperties.has(last)) { + cur[last] = val; + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/pluralize.js b/backend/node_modules/mongoose/lib/helpers/pluralize.js new file mode 100644 index 0000000..b06a8df --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/pluralize.js @@ -0,0 +1,95 @@ +'use strict'; + +module.exports = pluralize; + +/** + * Pluralization rules. + */ + +exports.pluralization = [ + [/human$/gi, 'humans'], + [/(m|wom)an$/gi, '$1en'], + [/(pe)rson$/gi, '$1ople'], + [/(child)$/gi, '$1ren'], + [/^(ox)$/gi, '$1en'], + [/(ax|test)is$/gi, '$1es'], + [/(octop|cact|foc|fung|nucle)us$/gi, '$1i'], + [/(alias|status|virus)$/gi, '$1es'], + [/(bu)s$/gi, '$1ses'], + [/(buffal|tomat|potat)o$/gi, '$1oes'], + [/([ti])um$/gi, '$1a'], + [/sis$/gi, 'ses'], + [/(?:([^f])fe|([lr])f)$/gi, '$1$2ves'], + [/(hive)$/gi, '$1s'], + [/([^aeiouy]|qu)y$/gi, '$1ies'], + [/(x|ch|ss|sh)$/gi, '$1es'], + [/(matr|vert|ind)ix|ex$/gi, '$1ices'], + [/([m|l])ouse$/gi, '$1ice'], + [/(kn|w|l)ife$/gi, '$1ives'], + [/(quiz)$/gi, '$1zes'], + [/^goose$/i, 'geese'], + [/s$/gi, 's'], + [/([^a-z])$/, '$1'], + [/$/gi, 's'] +]; +const rules = exports.pluralization; + +/** + * Uncountable words. + * + * These words are applied while processing the argument to `toCollectionName`. + * @api public + */ + +exports.uncountables = [ + 'advice', + 'energy', + 'excretion', + 'digestion', + 'cooperation', + 'health', + 'justice', + 'labour', + 'machinery', + 'equipment', + 'information', + 'pollution', + 'sewage', + 'paper', + 'money', + 'species', + 'series', + 'rain', + 'rice', + 'fish', + 'sheep', + 'moose', + 'deer', + 'news', + 'expertise', + 'status', + 'media' +]; +const uncountables = exports.uncountables; + +/** + * Pluralize function. + * + * @author TJ Holowaychuk (extracted from _ext.js_) + * @param {string} string to pluralize + * @api private + */ + +function pluralize(str) { + let found; + str = str.toLowerCase(); + if (!~uncountables.indexOf(str)) { + found = rules.filter(function(rule) { + return str.match(rule[0]); + }); + if (found[0]) { + return str.replace(found[0][0], found[0][1]); + } + } + return str; +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js b/backend/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js new file mode 100644 index 0000000..18cd3d3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/assignRawDocsToIdStructure.js @@ -0,0 +1,129 @@ +'use strict'; + +const clone = require('../../helpers/clone'); +const leanPopulateMap = require('./leanPopulateMap'); +const modelSymbol = require('../symbols').modelSymbol; +const utils = require('../../utils'); + +module.exports = assignRawDocsToIdStructure; + +const kHasArray = Symbol('mongoose#assignRawDocsToIdStructure#hasArray'); + +/** + * Assign `vals` returned by mongo query to the `rawIds` + * structure returned from utils.getVals() honoring + * query sort order if specified by user. + * + * This can be optimized. + * + * Rules: + * + * if the value of the path is not an array, use findOne rules, else find. + * for findOne the results are assigned directly to doc path (including null results). + * for find, if user specified sort order, results are assigned directly + * else documents are put back in original order of array if found in results + * + * @param {Array} rawIds + * @param {Array} resultDocs + * @param {Array} resultOrder + * @param {object} options + * @param {boolean} recursed + * @api private + */ + +function assignRawDocsToIdStructure(rawIds, resultDocs, resultOrder, options, recursed) { + // honor user specified sort order, unless we're populating a single + // virtual underneath an array (e.g. populating `employees.mostRecentShift` where + // `mostRecentShift` is a virtual with `justOne`) + const newOrder = []; + const sorting = options.isVirtual && options.justOne && rawIds.length > 1 + ? false : + options.sort && rawIds.length > 1; + const nullIfNotFound = options.$nullIfNotFound; + let doc; + let sid; + let id; + + if (utils.isMongooseArray(rawIds)) { + rawIds = rawIds.__array; + } + + let i = 0; + const len = rawIds.length; + + if (sorting && recursed && options[kHasArray] === undefined) { + options[kHasArray] = false; + for (const key in resultOrder) { + if (Array.isArray(resultOrder[key])) { + options[kHasArray] = true; + break; + } + } + } + + for (i = 0; i < len; ++i) { + id = rawIds[i]; + + if (Array.isArray(id)) { + // handle [ [id0, id2], [id3] ] + assignRawDocsToIdStructure(id, resultDocs, resultOrder, options, true); + newOrder.push(id); + continue; + } + + if (id === null && sorting === false) { + // keep nulls for findOne unless sorting, which always + // removes them (backward compat) + newOrder.push(id); + continue; + } + + sid = String(id); + doc = resultDocs[sid]; + // If user wants separate copies of same doc, use this option + if (options.clone && doc != null) { + if (options.lean) { + const _model = leanPopulateMap.get(doc); + doc = clone(doc); + leanPopulateMap.set(doc, _model); + } else { + doc = doc.constructor.hydrate(doc._doc); + } + } + + if (recursed) { + if (doc) { + if (sorting) { + const _resultOrder = resultOrder[sid]; + if (options[kHasArray]) { + // If result arrays, rely on the MongoDB server response for ordering + newOrder.push(doc); + } else { + newOrder[_resultOrder] = doc; + } + } else { + newOrder.push(doc); + } + } else if (id?.[modelSymbol] != null) { + newOrder.push(id); + } else { + newOrder.push(options.retainNullValues || nullIfNotFound ? null : id); + } + } else { + // apply findOne behavior - if document in results, assign, else assign null + newOrder[i] = doc || null; + } + } + + rawIds.length = 0; + if (newOrder.length) { + // reassign the documents based on corrected order + + // forEach skips over sparse entries in arrays so we + // can safely use this to our advantage dealing with sorted + // result sets too. + newOrder.forEach(function(doc, i) { + rawIds[i] = doc; + }); + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/assignVals.js b/backend/node_modules/mongoose/lib/helpers/populate/assignVals.js new file mode 100644 index 0000000..06b097a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/assignVals.js @@ -0,0 +1,360 @@ +'use strict'; + +const MongooseMap = require('../../types/map'); +const SkipPopulateValue = require('./skipPopulateValue'); +const assignRawDocsToIdStructure = require('./assignRawDocsToIdStructure'); +const get = require('../get'); +const getVirtual = require('./getVirtual'); +const leanPopulateMap = require('./leanPopulateMap'); +const lookupLocalFields = require('./lookupLocalFields'); +const markArraySubdocsPopulated = require('./markArraySubdocsPopulated'); +const mpath = require('mpath'); +const sift = require('sift').default; +const utils = require('../../utils'); +const { populateModelSymbol } = require('../symbols'); + +module.exports = function assignVals(o) { + // Options that aren't explicitly listed in `populateOptions` + const userOptions = Object.assign({}, get(o, 'allOptions.options.options'), get(o, 'allOptions.options')); + // `o.options` contains options explicitly listed in `populateOptions`, like + // `match` and `limit`. + const populateOptions = Object.assign({}, o.options, userOptions, { + justOne: o.justOne, + isVirtual: o.isVirtual + }); + populateOptions.$nullIfNotFound = o.isVirtual; + const populatedModel = o.populatedModel; + + const originalIds = [].concat(o.rawIds); + + // replace the original ids in our intermediate _ids structure + // with the documents found by query + o.allIds = [].concat(o.allIds); + assignRawDocsToIdStructure(o.rawIds, o.rawDocs, o.rawOrder, populateOptions); + + // now update the original documents being populated using the + // result structure that contains real documents. + const docs = o.docs; + const rawIds = o.rawIds; + const options = o.options; + const count = o.count && o.isVirtual; + let i; + let setValueIndex = 0; + + function setValue(val) { + ++setValueIndex; + if (count) { + return val; + } + if (val instanceof SkipPopulateValue) { + return val.val; + } + if (val === void 0) { + return val; + } + + const _allIds = o.allIds[i]; + + if (o.path.endsWith('.$*')) { + // Skip maps re: gh-12494 + return valueFilter(val, options, populateOptions, _allIds); + } + + if (o.justOne === true && Array.isArray(val)) { + // Might be an embedded discriminator (re: gh-9244) with multiple models, so make sure to pick the right + // model before assigning. + const ret = []; + for (const doc of val) { + const _docPopulatedModel = leanPopulateMap.get(doc); + if (_docPopulatedModel == null || _docPopulatedModel === populatedModel) { + ret.push(doc); + } + } + // Since we don't want to have to create a new mongoosearray, make sure to + // modify the array in place + while (val.length > ret.length) { + Array.prototype.pop.apply(val, []); + } + for (let i = 0; i < ret.length; ++i) { + val[i] = ret[i]; + } + + return valueFilter(val[0], options, populateOptions, _allIds); + } else if (o.justOne === false && !Array.isArray(val)) { + return valueFilter([val], options, populateOptions, _allIds); + } else if (o.justOne === true && !Array.isArray(val) && Array.isArray(_allIds)) { + return valueFilter(val, options, populateOptions, val == null ? val : _allIds[setValueIndex - 1]); + } + return valueFilter(val, options, populateOptions, _allIds); + } + + for (i = 0; i < docs.length; ++i) { + setValueIndex = 0; + const _path = o.path.endsWith('.$*') ? o.path.slice(0, -3) : o.path; + const existingVal = mpath.get(_path, docs[i], lookupLocalFields); + if (existingVal == null && !getVirtual(o.originalModel.schema, _path)) { + continue; + } + + let valueToSet; + if (count) { + valueToSet = numDocs(rawIds[i]); + } else if (Array.isArray(o.match)) { + valueToSet = Array.isArray(rawIds[i]) ? + rawIds[i].filter(v => v == null || sift(o.match[i])(v)) : + [rawIds[i]].filter(v => v == null || sift(o.match[i])(v))[0]; + } else { + valueToSet = rawIds[i]; + } + + // If we're populating a map, the existing value will be an object, so + // we need to transform again + const originalSchema = o.originalModel.schema; + const isDoc = get(docs[i], '$__', null) != null; + let isMap = isDoc ? + existingVal instanceof Map : + utils.isPOJO(existingVal); + // If we pass the first check, also make sure the local field's schematype + // is map (re: gh-6460) + isMap = isMap && get(originalSchema._getSchema(_path), '$isSchemaMap'); + if (!o.isVirtual && isMap) { + const _keys = existingVal instanceof Map ? + Array.from(existingVal.keys()) : + Object.keys(existingVal); + valueToSet = valueToSet.reduce((cur, v, i) => { + cur.set(_keys[i], v); + return cur; + }, new Map()); + } + + if (isDoc && Array.isArray(valueToSet)) { + for (const val of valueToSet) { + if (val?.$__ != null) { + val.$__.parent = docs[i]; + } + } + } else if (isDoc && valueToSet?.$__ != null) { + valueToSet.$__.parent = docs[i]; + } + + if (o.isVirtual && isDoc) { + docs[i].$populated(_path, o.justOne ? originalIds[0] : originalIds, o.allOptions); + // If virtual populate and doc is already init-ed, need to walk through + // the actual doc to set rather than setting `_doc` directly + if (Array.isArray(valueToSet)) { + valueToSet = valueToSet.map(v => v == null ? void 0 : v); + } + mpath.set( + _path, + valueToSet, + docs[i], + // Handle setting paths underneath maps using $* by converting arrays into maps of values + function lookup(obj, part, val) { + if (arguments.length >= 3) { + obj[part] = val; + return obj[part]; + } + if (obj instanceof Map && part === '$*') { + return [...obj.values()]; + } + return obj[part]; + }, + setValue, + false + ); + continue; + } + + const parts = _path.split('.'); + let cur = docs[i]; + for (let j = 0; j < parts.length - 1; ++j) { + // If we get to an array with a dotted path, like `arr.foo`, don't set + // `foo` on the array. + if (Array.isArray(cur) && !utils.isArrayIndex(parts[j])) { + break; + } + + if (parts[j] === '$*') { + break; + } + + if (cur[parts[j]] == null) { + // If nothing to set, avoid creating an unnecessary array. Otherwise + // we'll end up with a single doc in the array with only defaults. + // See gh-8342, gh-8455 + const curPath = parts.slice(0, j + 1).join('.'); + const schematype = originalSchema._getSchema(curPath); + if (valueToSet == null && schematype?.$isMongooseArray) { + break; + } + cur[parts[j]] = {}; + } + cur = cur[parts[j]]; + // If the property in MongoDB is a primitive, we won't be able to populate + // the nested path, so skip it. See gh-7545 + if (typeof cur !== 'object') { + break; + } + } + if (docs[i].$__) { + o.allOptions.options[populateModelSymbol] = o.allOptions.model; + docs[i].$populated(_path, o.unpopulatedValues[i], o.allOptions.options); + + if (valueToSet?.$__ != null) { + valueToSet.$__.wasPopulated = { value: o.unpopulatedValues[i] }; + } + + if (valueToSet instanceof Map && !valueToSet.$isMongooseMap) { + valueToSet = new MongooseMap(valueToSet, _path, docs[i], docs[i].schema.path(_path).$__schemaType); + } + } + + // If lean, need to check that each individual virtual respects + // `justOne`, because you may have a populated virtual with `justOne` + // underneath an array. See gh-6867 + mpath.set(_path, valueToSet, docs[i], lookupLocalFields, setValue, false); + + if (docs[i].$__) { + markArraySubdocsPopulated(docs[i], [o.allOptions.options]); + } + } +}; + +function numDocs(v) { + if (Array.isArray(v)) { + // If setting underneath an array of populated subdocs, we may have an + // array of arrays. See gh-7573 + if (v.some(el => Array.isArray(el) || el === null)) { + return v.map(el => { + if (el == null) { + return 0; + } + if (Array.isArray(el)) { + return el.filter(el => el != null).length; + } + return 1; + }); + } + return v.filter(el => el != null).length; + } + return v == null ? 0 : 1; +} + +/** + * 1) Apply backwards compatible find/findOne behavior to sub documents + * + * find logic: + * a) filter out non-documents + * b) remove _id from sub docs when user specified + * + * findOne + * a) if no doc found, set to null + * b) remove _id from sub docs when user specified + * + * 2) Remove _ids when specified by users query. + * + * background: + * _ids are left in the query even when user excludes them so + * that population mapping can occur. + * @param {any} val + * @param {object} assignmentOpts + * @param {object} populateOptions + * @param {Function} [populateOptions.transform] + * @param {boolean} allIds + * @api private + */ + +function valueFilter(val, assignmentOpts, populateOptions, allIds) { + const userSpecifiedTransform = typeof populateOptions.transform === 'function'; + const transform = userSpecifiedTransform ? populateOptions.transform : v => v; + if (Array.isArray(val)) { + // find logic + const ret = []; + const numValues = val.length; + for (let i = 0; i < numValues; ++i) { + let subdoc = val[i]; + const _allIds = Array.isArray(allIds) ? allIds[i] : allIds; + if (!isPopulatedObject(subdoc) && (!populateOptions.retainNullValues || subdoc != null) && !userSpecifiedTransform) { + continue; + } else if (!populateOptions.retainNullValues && subdoc == null) { + continue; + } else if (userSpecifiedTransform) { + subdoc = transform(isPopulatedObject(subdoc) ? subdoc : null, _allIds); + } + maybeRemoveId(subdoc, assignmentOpts); + ret.push(subdoc); + if (assignmentOpts.originalLimit && + ret.length >= assignmentOpts.originalLimit) { + break; + } + } + + const rLen = ret.length; + // Since we don't want to have to create a new mongoosearray, make sure to + // modify the array in place + while (val.length > rLen) { + Array.prototype.pop.apply(val, []); + } + let i = 0; + if (utils.isMongooseArray(val)) { + for (i = 0; i < rLen; ++i) { + val.set(i, ret[i], true); + } + } else { + for (i = 0; i < rLen; ++i) { + val[i] = ret[i]; + } + } + return val; + } + + // findOne + if (isPopulatedObject(val) || utils.isPOJO(val)) { + maybeRemoveId(val, assignmentOpts); + return transform(val, allIds); + } + if (val instanceof Map) { + return val; + } + + if (populateOptions.justOne === false) { + return []; + } + + return val == null ? transform(val, allIds) : transform(null, allIds); +} + +/** + * Remove _id from `subdoc` if user specified "lean" query option + * @param {Document} subdoc + * @param {object} assignmentOpts + * @api private + */ + +function maybeRemoveId(subdoc, assignmentOpts) { + if (subdoc != null && assignmentOpts.excludeId) { + if (typeof subdoc.$__setValue === 'function') { + delete subdoc._doc._id; + } else { + delete subdoc._id; + } + } +} + +/** + * Determine if `obj` is something we can set a populated path to. Can be a + * document, a lean document, or an array/map that contains docs. + * @param {any} obj + * @api private + */ + +function isPopulatedObject(obj) { + if (obj == null) { + return false; + } + + return Array.isArray(obj) || + obj.$isMongooseMap || + obj.$__ != null || + leanPopulateMap.has(obj); +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js b/backend/node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js new file mode 100644 index 0000000..b2f3c96 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/createPopulateQueryFilter.js @@ -0,0 +1,97 @@ +'use strict'; + +const SkipPopulateValue = require('./skipPopulateValue'); +const parentPaths = require('../path/parentPaths'); +const { trusted } = require('../query/trusted'); +const hasDollarKeys = require('../query/hasDollarKeys'); + +module.exports = function createPopulateQueryFilter(ids, _match, _foreignField, model, skipInvalidIds) { + const match = _formatMatch(_match); + + if (_foreignField.size === 1) { + const foreignField = Array.from(_foreignField)[0]; + const foreignSchemaType = model.schema.path(foreignField); + if (foreignField !== '_id' || !match['_id']) { + ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds); + match[foreignField] = trusted({ $in: ids }); + } else if (foreignField === '_id' && match['_id']) { + const userSpecifiedMatch = hasDollarKeys(match[foreignField]) ? + match[foreignField] : + { $eq: match[foreignField] }; + match[foreignField] = { ...trusted({ $in: ids }), ...userSpecifiedMatch }; + } + + const _parentPaths = parentPaths(foreignField); + for (let i = 0; i < _parentPaths.length - 1; ++i) { + const cur = _parentPaths[i]; + if (match[cur] != null && match[cur].$elemMatch != null) { + match[cur].$elemMatch[foreignField.slice(cur.length + 1)] = trusted({ $in: ids }); + delete match[foreignField]; + break; + } + } + } else { + const $or = []; + if (Array.isArray(match.$or)) { + match.$and = [{ $or: match.$or }, { $or: $or }]; + delete match.$or; + } else { + match.$or = $or; + } + for (const foreignField of _foreignField) { + if (foreignField !== '_id' || !match['_id']) { + const foreignSchemaType = model.schema.path(foreignField); + ids = _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds); + $or.push({ [foreignField]: { $in: ids } }); + } else if (foreignField === '_id' && match['_id']) { + const userSpecifiedMatch = hasDollarKeys(match[foreignField]) ? + match[foreignField] : + { $eq: match[foreignField] }; + match[foreignField] = { ...trusted({ $in: ids }), ...userSpecifiedMatch }; + } + } + } + + return match; +}; + +/** + * Optionally filter out invalid ids that don't conform to foreign field's schema + * to avoid cast errors (gh-7706) + * @param {Array} ids + * @param {SchemaType} foreignSchemaType + * @param {boolean} [skipInvalidIds] + * @api private + */ + +function _filterInvalidIds(ids, foreignSchemaType, skipInvalidIds) { + ids = ids.filter(v => !(v instanceof SkipPopulateValue)); + if (!skipInvalidIds) { + return ids; + } + return ids.filter(id => { + try { + foreignSchemaType.cast(id); + return true; + } catch { + return false; + } + }); +} + +/** + * Format `mod.match` given that it may be an array that we need to $or if + * the client has multiple docs with match functions + * @param {Array|any} match + * @api private + */ + +function _formatMatch(match) { + if (Array.isArray(match)) { + if (match.length > 1) { + return { $or: [].concat(match.map(m => Object.assign({}, m))) }; + } + return Object.assign({}, match[0]); + } + return Object.assign({}, match); +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js b/backend/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js new file mode 100644 index 0000000..d475625 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/getModelsMapForPopulate.js @@ -0,0 +1,894 @@ +'use strict'; + +const MongooseError = require('../../error/index'); +const SkipPopulateValue = require('./skipPopulateValue'); +const clone = require('../clone'); +const get = require('../get'); +const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue'); +const getConstructorName = require('../getConstructorName'); +const getSchemaTypes = require('./getSchemaTypes'); +const getVirtual = require('./getVirtual'); +const lookupLocalFields = require('./lookupLocalFields'); +const mpath = require('mpath'); +const modelNamesFromRefPath = require('./modelNamesFromRefPath'); +const utils = require('../../utils'); + +const modelSymbol = require('../symbols').modelSymbol; +const populateModelSymbol = require('../symbols').populateModelSymbol; +const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol; +const StrictPopulate = require('../../error/strictPopulate'); +const numericPathSegmentPattern = '\\.\\d+(?=\\.|$)'; +const hasNumericPathSegmentRE = new RegExp(numericPathSegmentPattern); +const numericPathSegmentRE = new RegExp(numericPathSegmentPattern, 'g'); + +module.exports = function getModelsMapForPopulate(model, docs, options) { + let doc; + const len = docs.length; + const map = []; + const modelNameFromQuery = options.model?.modelName || options.model; + let schema; + let refPath; + let modelNames; + let modelNamesSet; + const available = {}; + + const modelSchema = model.schema; + + // Populating a nested path should always be a no-op re: #9073. + // People shouldn't do this, but apparently they do. + if (options._localModel != null && options._localModel.schema.nested[options.path]) { + return []; + } + + const _virtualRes = getVirtual(model.schema, options.path); + const virtual = _virtualRes == null ? null : _virtualRes.virtual; + if (virtual != null) { + return _virtualPopulate(model, docs, options, _virtualRes); + } + + const parts = modelSchema.paths[options.path]?.splitPath() ?? options.path.split('.'); + let allSchemaTypes = getSchemaTypes(model, modelSchema, null, options.path, parts); + allSchemaTypes = Array.isArray(allSchemaTypes) ? allSchemaTypes : [allSchemaTypes].filter(v => v != null); + + const isStrictPopulateDisabled = options.strictPopulate === false || options.options?.strictPopulate === false; + if (!isStrictPopulateDisabled && allSchemaTypes.length === 0 && options._localModel != null) { + return new StrictPopulate(options._fullPath || options.path); + } + + for (let i = 0; i < len; i++) { + doc = docs[i]; + let justOne = null; + + if (doc.$__ != null && doc.populated(options.path)) { + const forceRepopulate = options.forceRepopulate ?? doc.constructor.base.options.forceRepopulate; + if (forceRepopulate === false) { + continue; + } + } + + const docSchema = doc?.$__ != null ? doc.$__schema : modelSchema; + schema = getSchemaTypes(model, docSchema, doc, options.path, parts); + + // Special case: populating a path that's a DocumentArray unless + // there's an explicit `ref` or `refPath` re: gh-8946 + if (schema != null && + schema.$isMongooseDocumentArray && + schema.options.ref == null && + schema.options.refPath == null) { + continue; + } + const isUnderneathDocArray = schema?.$parentSchemaDocArray; + if (isUnderneathDocArray && get(options, 'options.sort') != null) { + return new MongooseError('Cannot populate with `sort` on path ' + options.path + + ' because it is a subproperty of a document array'); + } + + modelNames = null; + let isRefPath = false; + let normalizedRefPath = null; + let schemaOptions = null; + let modelNamesInOrder = null; + + if (schema?.instance === 'Embedded') { + if (schema.options.ref) { + const data = { + localField: options.path + '._id', + foreignField: '_id', + justOne: true + }; + const res = _getModelNames(doc, schema, modelNameFromQuery, model); + + const unpopulatedValue = mpath.get(options.path, doc); + const id = mpath.get('_id', unpopulatedValue); + addModelNamesToMap(model, map, available, res.modelNames, options, data, id, doc, schemaOptions, unpopulatedValue); + } + // No-op if no `ref` set. See gh-11538 + continue; + } + + if (Array.isArray(schema)) { + const schemasArray = schema; + for (const _schema of schemasArray) { + let _modelNames; + let res; + try { + res = _getModelNames(doc, _schema, modelNameFromQuery, model); + _modelNames = res.modelNames; + isRefPath = isRefPath || res.isRefPath; + normalizedRefPath = normalizedRefPath || res.refPath; + justOne = res.justOne; + } catch (error) { + return error; + } + + if (isRefPath && !res.isRefPath) { + continue; + } + if (!_modelNames) { + continue; + } + modelNames = modelNames || []; + modelNamesSet = modelNamesSet || new Set(); + for (const modelName of _modelNames) { + if (modelNamesSet.has(modelName) === false) { + modelNamesSet.add(modelName); + modelNames.push(modelName); + } + } + } + } else { + try { + const res = _getModelNames(doc, schema, modelNameFromQuery, model); + modelNames = res.modelNames; + isRefPath = res.isRefPath; + normalizedRefPath = normalizedRefPath || res.refPath; + justOne = res.justOne; + schemaOptions = get(schema, 'options.populate', null); + // Dedupe, because `refPath` can return duplicates of the same model name, + // and that causes perf issues. + if (isRefPath) { + modelNamesInOrder = modelNames; + modelNames = Array.from(new Set(modelNames)); + } + } catch (error) { + return error; + } + + if (!modelNames) { + continue; + } + } + + const data = {}; + const localField = options.path; + const foreignField = '_id'; + + // `justOne = null` means we don't know from the schema whether the end + // result should be an array or a single doc. This can result from + // populating a POJO using `Model.populate()` + if ('justOne' in options && options.justOne !== void 0) { + justOne = options.justOne; + } else if (schema && !schema[schemaMixedSymbol]) { + // Skip Mixed types because we explicitly don't do casting on those. + if (options.path.endsWith('.' + schema.path) || options.path === schema.path) { + justOne = Array.isArray(schema) ? + schema.every(schema => !schema.$isMongooseArray) : + !schema.$isMongooseArray; + } + } + + if (!modelNames) { + continue; + } + + data.isVirtual = false; + data.justOne = justOne; + data.localField = localField; + data.foreignField = foreignField; + + // Get local fields + const ret = _getLocalFieldValues(doc, localField, model, options, null, schema); + + const id = String(utils.getValue(foreignField, doc)); + options._docs[id] = Array.isArray(ret) ? ret.slice() : ret; + + let match = get(options, 'match', null); + + const hasMatchFunction = typeof match === 'function'; + if (hasMatchFunction) { + match = match.call(doc, doc); + } + throwOn$where(match); + data.match = match; + data.hasMatchFunction = hasMatchFunction; + data.isRefPath = isRefPath; + data.modelNamesInOrder = modelNamesInOrder; + + if (isRefPath) { + const normalizedRefPathForDiscriminators = typeof normalizedRefPath === 'string' ? + normalizedRefPath.replace(numericPathSegmentRE, '') : + normalizedRefPath; + const embeddedDiscriminatorModelNames = _findRefPathForDiscriminators(doc, + modelSchema, data, options, normalizedRefPathForDiscriminators, ret); + + modelNames = embeddedDiscriminatorModelNames || modelNames; + } + + try { + addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions); + } catch (err) { + return err; + } + } + return map; + + function _getModelNames(doc, schemaType, modelNameFromQuery, model) { + let modelNames; + let isRefPath = false; + let justOne = null; + + const originalSchema = schemaType; + if (schemaType?.instance === 'Array') { + schemaType = schemaType.embeddedSchemaType; + } + if (schemaType?.$isSchemaMap) { + schemaType = schemaType.$__schemaType; + } + + const ref = schemaType?.options?.ref; + refPath = schemaType?.options?.refPath; + if (schemaType != null && + schemaType[schemaMixedSymbol] && + !ref && + !refPath && + !modelNameFromQuery) { + return { modelNames: null }; + } + + if (modelNameFromQuery) { + modelNames = [modelNameFromQuery]; // query options + } else if (refPath != null) { + if (typeof refPath === 'function') { + const res = _getModelNamesFromFunctionRefPath(refPath, doc, schemaType, options.path, modelSchema, options._queryProjection); + modelNames = res.modelNames; + refPath = res.refPath; + } else { + modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection); + } + + isRefPath = true; + } else { + let ref; + let refPath; + let schemaForCurrentDoc; + let discriminatorValue; + let modelForCurrentDoc = model; + const discriminatorKey = model.schema.options.discriminatorKey; + + if (!schemaType && discriminatorKey && (discriminatorValue = utils.getValue(discriminatorKey, doc))) { + // `modelNameForFind` is the discriminator value, so we might need + // find the discriminated model name + const discriminatorModel = getDiscriminatorByValue(model.discriminators, discriminatorValue) || model; + if (discriminatorModel != null) { + modelForCurrentDoc = discriminatorModel; + } else { + try { + modelForCurrentDoc = _getModelFromConn(model.db, discriminatorValue); + } catch (error) { + return error; + } + } + + schemaForCurrentDoc = modelForCurrentDoc.schema._getSchema(options.path); + + if (schemaForCurrentDoc?.embeddedSchemaType) { + schemaForCurrentDoc = schemaForCurrentDoc.embeddedSchemaType; + } + } else { + schemaForCurrentDoc = schemaType; + } + + if (originalSchema && originalSchema.path.endsWith('.$*')) { + justOne = !originalSchema.$isMongooseArray && !originalSchema._arrayPath; + } else if (schemaForCurrentDoc != null) { + justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath; + } + + if ((ref = get(schemaForCurrentDoc, 'options.ref')) != null) { + if (schemaForCurrentDoc != null && + typeof ref === 'function' && + options.path.endsWith('.' + schemaForCurrentDoc.path)) { + // Ensure correct context for ref functions: subdoc, not top-level doc. See gh-8469 + modelNames = new Set(); + + const subdocPath = options.path.slice(0, options.path.length - schemaForCurrentDoc.path.length - 1); + const vals = mpath.get(subdocPath, doc, lookupLocalFields); + const subdocsBeingPopulated = Array.isArray(vals) ? + utils.array.flatten(vals) : + (vals ? [vals] : []); + for (const subdoc of subdocsBeingPopulated) { + modelNames.add(handleRefFunction(ref, subdoc)); + } + + if (subdocsBeingPopulated.length === 0) { + modelNames = [handleRefFunction(ref, doc)]; + } else { + modelNames = Array.from(modelNames); + } + } else { + ref = handleRefFunction(ref, doc); + modelNames = [ref]; + } + } else if ((refPath = get(schemaForCurrentDoc, 'options.refPath')) != null) { + isRefPath = true; + if (typeof refPath === 'function') { + const res = _getModelNamesFromFunctionRefPath(refPath, doc, schemaForCurrentDoc, options.path, modelSchema, options._queryProjection); + modelNames = res.modelNames; + refPath = res.refPath; + } else { + modelNames = modelNamesFromRefPath(refPath, doc, options.path, modelSchema, options._queryProjection); + } + } + } + + if (!modelNames) { + // `Model.populate()` on a POJO with no known local model. Default to using the `Model` + if (options._localModel == null) { + modelNames = [model.modelName]; + } else { + return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath }; + } + } + + if (!Array.isArray(modelNames)) { + modelNames = [modelNames]; + } + + return { modelNames: modelNames, justOne: justOne, isRefPath: isRefPath, refPath: refPath }; + } +}; + +/** + * Resolve model names for function-style `refPath` and return the first + * resolved refPath value so discriminator handling can inspect it later. + * + * @param {Function} refPath + * @param {Document|object} doc + * @param {SchemaType} schema + * @param {string} populatePath + * @param {Schema} modelSchema + * @param {object} queryProjection + * @returns {{modelNames: string[], refPath: string|null}} + * @private + */ +function _getModelNamesFromFunctionRefPath(refPath, doc, schemaType, populatePath, modelSchema, queryProjection) { + const modelNames = []; + let normalizedRefPath = null; + const schemaPath = schemaType?.path; + + if (schemaPath != null && + populatePath.length > schemaPath.length + 1 && + populatePath.charAt(populatePath.length - schemaPath.length - 1) === '.' && + populatePath.slice(populatePath.length - schemaPath.length) === schemaPath + ) { + const subdocPath = populatePath.slice(0, populatePath.length - schemaPath.length - 1); + const segments = subdocPath.indexOf('.') === -1 ? [subdocPath] : subdocPath.split('.'); + let hasSubdoc = false; + + walkSubdocs(doc, '', 0); + if (hasSubdoc) { + return { modelNames, refPath: normalizedRefPath }; + } + + function walkSubdocs(currentSubdoc, indexedPathPrefix, segmentIndex) { + if (currentSubdoc == null) { + return; + } + + if (segmentIndex >= segments.length) { + hasSubdoc = true; + const indexedPath = indexedPathPrefix + '.' + schemaPath; + const subdocRefPath = refPath.call(currentSubdoc, currentSubdoc, indexedPath); + normalizedRefPath = normalizedRefPath || subdocRefPath; + modelNames.push(..._getModelNamesFromRefPath(subdocRefPath, doc, populatePath, indexedPath, modelSchema, queryProjection)); + return; + } + + const segment = segments[segmentIndex]; + const source = currentSubdoc?._doc ?? currentSubdoc; + + if (segment === '$*') { + if (source instanceof Map) { + for (const [key, value] of source.entries()) { + if (value != null) { + const valuePath = indexedPathPrefix.length === 0 ? + key : + indexedPathPrefix + '.' + key; + walkSubdocs(value, valuePath, segmentIndex + 1); + } + } + return; + } + + if (source != null && typeof source === 'object') { + for (const key of Object.keys(source)) { + const value = source[key]; + if (value != null) { + const valuePath = indexedPathPrefix.length === 0 ? + key : + indexedPathPrefix + '.' + key; + walkSubdocs(value, valuePath, segmentIndex + 1); + } + } + } + return; + } + + const child = source[segment]; + const childPath = indexedPathPrefix.length === 0 ? + segment : + indexedPathPrefix + '.' + segment; + + if (Array.isArray(child)) { + for (let i = 0; i < child.length; ++i) { + if (child[i] != null) { + walkSubdocs(child[i], childPath + '.' + i, segmentIndex + 1); + } + } + } else if (child != null) { + walkSubdocs(child, childPath, segmentIndex + 1); + } + } + } + + const topLevelRefPath = refPath.call(doc, doc, populatePath); + normalizedRefPath = normalizedRefPath || topLevelRefPath; + modelNames.push(...modelNamesFromRefPath(topLevelRefPath, doc, populatePath, modelSchema, queryProjection)); + + return { modelNames, refPath: normalizedRefPath }; +} + +/** + * Resolve model names from a refPath, preferring the indexed populated path + * for array subdocuments and falling back to the original populate path if + * normalization fails. + * + * @param {string|Function} refPath + * @param {Document|object} doc + * @param {string} populatePath + * @param {string} indexedPath + * @param {Schema} modelSchema + * @param {object} queryProjection + * @returns {string[]} + * @private + */ +function _getModelNamesFromRefPath(refPath, doc, populatePath, indexedPath, modelSchema, queryProjection) { + const populatedPath = typeof refPath === 'string' && hasNumericPathSegmentRE.test(refPath) ? + populatePath : + indexedPath; + + try { + return modelNamesFromRefPath(refPath, doc, populatedPath, modelSchema, queryProjection); + } catch (error) { + if (populatedPath === indexedPath && + typeof error?.message === 'string' && + error.message.startsWith('Could not normalize ref path')) { + return modelNamesFromRefPath(refPath, doc, populatePath, modelSchema, queryProjection); + } + throw error; + } +} + +/*! + * ignore + */ + +function _virtualPopulate(model, docs, options, _virtualRes) { + const map = []; + const available = {}; + const virtual = _virtualRes.virtual; + + for (const doc of docs) { + let modelNames = null; + const data = {}; + + // localField and foreignField + let localField; + const virtualPrefix = _virtualRes.nestedSchemaPath ? + _virtualRes.nestedSchemaPath + '.' : ''; + if (typeof options.localField === 'string') { + localField = options.localField; + } else if (typeof virtual.options.localField === 'function') { + localField = virtualPrefix + virtual.options.localField.call(doc, doc); + } else if (Array.isArray(virtual.options.localField)) { + localField = virtual.options.localField.map(field => virtualPrefix + field); + } else { + localField = virtualPrefix + virtual.options.localField; + } + data.count = virtual.options.count; + + if (virtual.options.skip != null && !Object.hasOwn(options, 'skip')) { + options.skip = virtual.options.skip; + } + if (virtual.options.limit != null && !Object.hasOwn(options, 'limit')) { + options.limit = virtual.options.limit; + } + if (virtual.options.perDocumentLimit != null && !Object.hasOwn(options, 'perDocumentLimit')) { + options.perDocumentLimit = virtual.options.perDocumentLimit; + } + let foreignField = virtual.options.foreignField; + + if (!localField || !foreignField) { + return new MongooseError(`Cannot populate virtual \`${options.path}\` on model \`${model.modelName}\`, because options \`localField\` and / or \`foreignField\` are missing`); + } + + if (typeof localField === 'function') { + localField = localField.call(doc, doc); + } + if (typeof foreignField === 'function') { + foreignField = foreignField.call(doc, doc); + } + + data.isRefPath = false; + + // `justOne = null` means we don't know from the schema whether the end + // result should be an array or a single doc. This can result from + // populating a POJO using `Model.populate()` + let justOne = null; + if ('justOne' in options && options.justOne !== void 0) { + justOne = options.justOne; + } + + // Use the correct target doc/sub-doc for dynamic ref on nested schema. See gh-12363 + if (_virtualRes.nestedSchemaPath && typeof virtual.options.ref === 'function') { + const subdocs = utils.getValue(_virtualRes.nestedSchemaPath, doc); + modelNames = Array.isArray(subdocs) + ? subdocs.flatMap(subdoc => virtual._getModelNamesForPopulate(subdoc)) + : virtual._getModelNamesForPopulate(subdocs); + } else { + modelNames = virtual._getModelNamesForPopulate(doc); + } + if (virtual.options.refPath) { + justOne = !!virtual.options.justOne; + data.isRefPath = true; + } else if (virtual.options.ref) { + justOne = !!virtual.options.justOne; + } + + data.isVirtual = true; + data.virtual = virtual; + data.justOne = justOne; + + // `match` + const baseMatch = get(data, 'virtual.options.match', null) || + get(data, 'virtual.options.options.match', null); + let match = get(options, 'match', null) || baseMatch; + + let hasMatchFunction = typeof match === 'function'; + if (hasMatchFunction) { + match = match.call(doc, doc, data.virtual); + } + + if (Array.isArray(localField) && Array.isArray(foreignField) && localField.length === foreignField.length) { + match = Object.assign({}, match); + for (let i = 1; i < localField.length; ++i) { + match[foreignField[i]] = convertTo_id(mpath.get(localField[i], doc, lookupLocalFields), model.schema); + hasMatchFunction = true; + } + + localField = localField[0]; + foreignField = foreignField[0]; + } + data.localField = localField; + data.foreignField = foreignField; + data.match = match; + data.hasMatchFunction = hasMatchFunction; + + throwOn$where(match); + + // Get local fields + const ret = _getLocalFieldValues(doc, localField, model, options, virtual); + + try { + addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc); + } catch (err) { + return err; + } + } + + return map; +} + +/*! + * ignore + */ + +function addModelNamesToMap(model, map, available, modelNames, options, data, ret, doc, schemaOptions, unpopulatedValue) { + // `PopulateOptions#connection`: if the model is passed as a string, the + // connection matters because different connections have different models. + const connection = options.connection ?? model.db; + + unpopulatedValue = unpopulatedValue === void 0 ? ret : unpopulatedValue; + if (Array.isArray(unpopulatedValue)) { + unpopulatedValue = utils.cloneArrays(unpopulatedValue); + } + + if (modelNames == null) { + return; + } + + const flatModelNames = utils.array.flatten(modelNames); + let k = flatModelNames.length; + while (k--) { + let modelName = flatModelNames[k]; + if (modelName == null) { + continue; + } + + let Model; + if (options.model && options.model[modelSymbol]) { + Model = options.model; + } else if (modelName[modelSymbol]) { + Model = modelName; + modelName = Model.modelName; + } else { + try { + Model = _getModelFromConn(connection, modelName); + } catch (err) { + if (ret !== void 0) { + throw err; + } + Model = null; + } + } + + let ids = ret; + + const modelNamesForRefPath = data.modelNamesInOrder || modelNames; + if (data.isRefPath && Array.isArray(ret) && ret.length === modelNamesForRefPath.length) { + ids = matchIdsToRefPaths(ret, modelNamesForRefPath, modelName); + } + + const perDocumentLimit = options.perDocumentLimit == null ? + get(options, 'options.perDocumentLimit', null) : + options.perDocumentLimit; + + if (!available[modelName] || perDocumentLimit != null) { + const currentOptions = { + model: Model + }; + if (data.isVirtual && get(data.virtual, 'options.options')) { + currentOptions.options = clone(data.virtual.options.options); + } else if (schemaOptions != null) { + currentOptions.options = Object.assign({}, schemaOptions); + } + utils.merge(currentOptions, options); + + // Used internally for checking what model was used to populate this + // path. + options[populateModelSymbol] = Model; + currentOptions[populateModelSymbol] = Model; + available[modelName] = { + model: Model, + options: currentOptions, + match: data.hasMatchFunction ? [data.match] : data.match, + docs: [doc], + ids: [ids], + allIds: [ret], + unpopulatedValues: [unpopulatedValue], + localField: new Set([data.localField]), + foreignField: new Set([data.foreignField]), + justOne: data.justOne, + isVirtual: data.isVirtual, + virtual: data.virtual, + count: data.count, + [populateModelSymbol]: Model + }; + map.push(available[modelName]); + } else { + available[modelName].localField.add(data.localField); + available[modelName].foreignField.add(data.foreignField); + available[modelName].docs.push(doc); + available[modelName].ids.push(ids); + available[modelName].allIds.push(ret); + available[modelName].unpopulatedValues.push(unpopulatedValue); + if (data.hasMatchFunction) { + available[modelName].match.push(data.match); + } + } + } +} + +function _getModelFromConn(conn, modelName) { + /* If this connection has a parent from `useDb()`, bubble up to parent's models */ + if (conn.models[modelName] == null && conn._parent != null) { + return _getModelFromConn(conn._parent, modelName); + } + + return conn.model(modelName); +} + +function matchIdsToRefPaths(ids, refPaths, refPathToFind) { + if (!Array.isArray(refPaths)) { + return refPaths === refPathToFind + ? Array.isArray(ids) + ? utils.array.flatten(ids) + : [ids] + : []; + } + if (Array.isArray(ids) && Array.isArray(refPaths)) { + return ids.flatMap((id, index) => matchIdsToRefPaths(id, refPaths[index], refPathToFind)); + } + return []; +} + +/*! + * ignore + */ + +function handleRefFunction(ref, doc) { + if (typeof ref === 'function' && !ref[modelSymbol]) { + return ref.call(doc, doc); + } + return ref; +} + +/*! + * ignore + */ + +function _getLocalFieldValues(doc, localField, model, options, virtual, schema) { + // Get Local fields + const localFieldPathType = model.schema._getPathType(localField); + const localFieldPath = localFieldPathType === 'real' ? + model.schema.path(localField) : + localFieldPathType.schema; + const localFieldGetters = localFieldPath?.getters || []; + + localField = localFieldPath?.instance === 'Embedded' ? localField + '._id' : localField; + + const _populateOptions = get(options, 'options', {}); + + const getters = 'getters' in _populateOptions ? + _populateOptions.getters : + get(virtual, 'options.getters', false); + if (localFieldGetters.length !== 0 && getters) { + const hydratedDoc = (doc.$__ != null) ? doc : model.hydrate(doc); + const localFieldValue = utils.getValue(localField, doc); + if (Array.isArray(localFieldValue)) { + const localFieldHydratedValue = utils.getValue(localField.split('.').slice(0, -1), hydratedDoc); + return localFieldValue.map((localFieldArrVal, localFieldArrIndex) => + localFieldPath.applyGetters(localFieldArrVal, localFieldHydratedValue[localFieldArrIndex])); + } else { + return localFieldPath.applyGetters(localFieldValue, hydratedDoc); + } + } else { + return convertTo_id(mpath.get(localField, doc, lookupLocalFields), schema); + } +} + +/** + * Retrieve the _id of `val` if a Document or Array of Documents. + * + * @param {Array|Document|any} val + * @param {Schema} schema + * @return {Array|Document|any} + * @api private + */ + +function convertTo_id(val, schema) { + if (val?.$__ != null) { + return val._doc._id; + } + if (val?._id != null && !schema?.$isSchemaMap) { + return val._id; + } + + if (Array.isArray(val)) { + const rawVal = val.__array ?? val; + for (let i = 0; i < rawVal.length; ++i) { + if (rawVal[i]?.$__ != null) { + rawVal[i] = rawVal[i]._doc._id; + } + } + if (utils.isMongooseArray(val) && val.$schema()) { + return val.$schema()._castForPopulate(val, val.$parent()); + } + + return [].concat(val); + } + + // `populate('map')` may be an object if populating on a doc that hasn't + // been hydrated yet + if (getConstructorName(val) === 'Object' && + // The intent here is we should only flatten the object if we expect + // to get a Map in the end. Avoid doing this for mixed types. + schema?.[schemaMixedSymbol] == null) { + const ret = []; + for (const key of Object.keys(val)) { + ret.push(val[key]); + } + return ret; + } + // If doc has already been hydrated, e.g. `doc.populate('map')` + // then `val` will already be a map + if (val instanceof Map) { + return Array.from(val.values()); + } + + return val; +} + +/*! + * ignore + */ + +function _findRefPathForDiscriminators(doc, modelSchema, data, options, normalizedRefPath, ret) { + // Re: gh-8452. Embedded discriminators may not have `refPath`, so clear + // out embedded discriminator docs that don't have a `refPath` on the + // populated path. + if (!data.isRefPath || normalizedRefPath == null) { + return; + } + + const pieces = normalizedRefPath.split('.'); + let cur = ''; + let modelNames = void 0; + for (let i = 0; i < pieces.length; ++i) { + const piece = pieces[i]; + cur = cur + (cur.length === 0 ? '' : '.') + piece; + const schematype = modelSchema.path(cur); + if (schematype != null && + schematype.$isMongooseDocumentArray && + schematype.Constructor.discriminators != null && + utils.hasOwnKeys(schematype.Constructor.discriminators)) { + const subdocs = utils.getValue(cur, doc); + const remnant = options.path.substring(cur.length + 1); + const discriminatorKey = schematype.Constructor.schema.options.discriminatorKey; + modelNames = []; + for (const subdoc of subdocs) { + const discriminatorName = utils.getValue(discriminatorKey, subdoc); + const discriminator = schematype.Constructor.discriminators[discriminatorName]; + const discriminatorSchema = discriminator?.schema; + if (discriminatorSchema == null) { + continue; + } + const _path = discriminatorSchema.path(remnant); + if (_path == null || _path.options.refPath == null) { + const docValue = utils.getValue(data.localField.substring(cur.length + 1), subdoc); + ret.forEach((v, i) => { + if (v === docValue) { + ret[i] = SkipPopulateValue(v); + } + }); + continue; + } + const modelName = utils.getValue(pieces.slice(i + 1).join('.'), subdoc); + modelNames.push(modelName); + } + } + } + + return modelNames; +} + +/** + * Throw an error if there are any $where keys + */ + +function throwOn$where(match) { + if (match == null) { + return; + } + if (typeof match !== 'object') { + return; + } + for (const key of Object.keys(match)) { + if (key === '$where') { + throw new MongooseError('Cannot use $where filter with populate() match'); + } + if (match[key] != null && typeof match[key] === 'object') { + throwOn$where(match[key]); + } + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js b/backend/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js new file mode 100644 index 0000000..4887b2b --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/getSchemaTypes.js @@ -0,0 +1,229 @@ +'use strict'; + +/*! + * ignore + */ + +const Mixed = require('../../schema/mixed'); +const get = require('../get'); +const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue'); +const leanPopulateMap = require('./leanPopulateMap'); +const mpath = require('mpath'); + +const populateModelSymbol = require('../symbols').populateModelSymbol; + +/** + * Given a model and its schema, find all possible schema types for `path`, + * including searching through discriminators. If `doc` is specified, will + * use the doc's values for discriminator keys when searching, otherwise + * will search all discriminators. + * + * @param {Model} model + * @param {Schema} schema + * @param {object} doc POJO + * @param {string} path + * @param {string[]} [parts] pass in pre-split `path` to avoid extra splitting + * @api private + */ + +module.exports = function getSchemaTypes(model, schema, doc, path, parts) { + const pathschema = schema.path(path); + const topLevelDoc = doc; + if (pathschema) { + return pathschema; + } + + const discriminatorKey = schema.discriminatorMapping?.key; + if (discriminatorKey && model != null) { + if (doc?.[discriminatorKey] != null) { + const discriminator = getDiscriminatorByValue(model.discriminators, doc[discriminatorKey]); + schema = discriminator?.schema || schema; + } else if (model.discriminators != null) { + return Object.keys(model.discriminators).reduce((arr, name) => { + const disc = model.discriminators[name]; + return arr.concat(getSchemaTypes(disc, disc.schema, null, path)); + }, []); + } + } + + function search(parts, schema, subdoc, nestedPath) { + let p = parts.length + 1; + let foundschema; + let trypath; + + while (p--) { + trypath = parts.slice(0, p).join('.'); + foundschema = schema.path(trypath); + if (foundschema == null) { + continue; + } + + if (foundschema.embeddedSchemaType || foundschema.schema) { + // array of Mixed? + if (foundschema.embeddedSchemaType instanceof Mixed) { + return foundschema.embeddedSchemaType; + } + + let schemas = null; + if (foundschema.schema?.discriminators != null) { + const discriminators = foundschema.schema.discriminators; + const discriminatorKeyPath = trypath + '.' + + foundschema.schema.options.discriminatorKey; + const keys = subdoc ? mpath.get(discriminatorKeyPath, subdoc) || [] : []; + schemas = Object.keys(discriminators). + reduce(function(cur, discriminator) { + const tiedValue = discriminators[discriminator].discriminatorMapping.value; + if (doc == null || keys.indexOf(discriminator) !== -1 || keys.indexOf(tiedValue) !== -1) { + cur.push(discriminators[discriminator]); + } + return cur; + }, []); + } + + // Now that we found the array, we need to check if there + // are remaining document paths to look up for casting. + // Also we need to handle array.$.path since schema.path + // doesn't work for that. + // If there is no foundschema.schema we are dealing with + // a path like array.$ + if (p !== parts.length && foundschema.schema) { + let ret; + if (parts[p] === '$') { + if (p + 1 === parts.length) { + // comments.$ + return foundschema; + } + // comments.$.comments.$.title + ret = search( + parts.slice(p + 1), + schema, + subdoc ? mpath.get(trypath, subdoc) : null, + nestedPath.concat(parts.slice(0, p)) + ); + if (ret) { + ret.$parentSchemaDocArray = ret.$parentSchemaDocArray || + (foundschema.schema.$isSingleNested ? null : foundschema); + } + return ret; + } + + if (schemas != null && schemas.length > 0) { + ret = []; + for (const schema of schemas) { + const _ret = search( + parts.slice(p), + schema, + subdoc ? mpath.get(trypath, subdoc) : null, + nestedPath.concat(parts.slice(0, p)) + ); + if (_ret != null) { + _ret.$parentSchemaDocArray = _ret.$parentSchemaDocArray || + (foundschema.schema.$isSingleNested ? null : foundschema); + if (_ret.$parentSchemaDocArray) { + ret.$parentSchemaDocArray = _ret.$parentSchemaDocArray; + } + ret.push(_ret); + } + } + return ret; + } else { + ret = search( + parts.slice(p), + foundschema.schema, + subdoc ? mpath.get(trypath, subdoc) : null, + nestedPath.concat(parts.slice(0, p)) + ); + + if (ret) { + ret.$parentSchemaDocArray = ret.$parentSchemaDocArray || + (foundschema.schema.$isSingleNested ? null : foundschema); + } + return ret; + } + } else if (p !== parts.length && + foundschema.$isMongooseArray && + foundschema.embeddedSchemaType.$isMongooseArray) { + // Nested arrays. Drill down to the bottom of the nested array. + let type = foundschema; + while (type.$isMongooseArray && !type.$isMongooseDocumentArray) { + type = type.embeddedSchemaType; + } + + const ret = search( + parts.slice(p), + type.schema, + null, + nestedPath.concat(parts.slice(0, p)) + ); + if (ret != null) { + return ret; + } + + if (type.schema.discriminators) { + const discriminatorPaths = []; + for (const discriminatorName of Object.keys(type.schema.discriminators)) { + const _schema = type.schema.discriminators[discriminatorName] || type.schema; + const ret = search(parts.slice(p), _schema, null, nestedPath.concat(parts.slice(0, p))); + if (ret != null) { + discriminatorPaths.push(ret); + } + } + if (discriminatorPaths.length > 0) { + return discriminatorPaths; + } + } + } + } else if (foundschema.$isSchemaMap && foundschema.$__schemaType instanceof Mixed) { + return foundschema.$__schemaType; + } + + const fullPath = nestedPath.concat([trypath]).join('.'); + if (topLevelDoc?.$__ && topLevelDoc.$populated(fullPath, true) && p < parts.length) { + const model = topLevelDoc.$populated(fullPath, true).options[populateModelSymbol]; + if (model != null) { + const ret = search( + parts.slice(p), + model.schema, + subdoc ? mpath.get(trypath, subdoc) : null, + nestedPath.concat(parts.slice(0, p)) + ); + + return ret; + } + } + + const _val = get(topLevelDoc, trypath); + if (_val != null) { + const model = Array.isArray(_val) && _val.length > 0 ? + leanPopulateMap.get(_val[0]) : + leanPopulateMap.get(_val); + // Populated using lean, `leanPopulateMap` value is the foreign model + const schema = model?.schema ?? null; + if (schema != null) { + const ret = search( + parts.slice(p), + schema, + subdoc ? mpath.get(trypath, subdoc) : null, + nestedPath.concat(parts.slice(0, p)) + ); + + if (ret != null) { + ret.$parentSchemaDocArray = ret.$parentSchemaDocArray || + (schema.$isSingleNested ? null : schema); + return ret; + } + } + } + return foundschema; + } + } + // look for arrays + parts = parts || path.split('.'); + for (let i = 0; i < parts.length; ++i) { + if (parts[i] === '$') { + // Re: gh-5628, because `schema.path()` doesn't take $ into account. + parts[i] = '0'; + } + } + return search(parts, schema, doc, []); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/getVirtual.js b/backend/node_modules/mongoose/lib/helpers/populate/getVirtual.js new file mode 100644 index 0000000..00f958e --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/getVirtual.js @@ -0,0 +1,103 @@ +'use strict'; + +module.exports = getVirtual; + +/*! + * ignore + */ + +function getVirtual(schema, name) { + if (schema.virtuals[name]) { + return { virtual: schema.virtuals[name], path: void 0 }; + } + + const parts = name.split('.'); + let cur = ''; + let nestedSchemaPath = ''; + for (let i = 0; i < parts.length; ++i) { + cur += (cur.length > 0 ? '.' : '') + parts[i]; + if (schema.virtuals[cur]) { + if (i === parts.length - 1) { + return { virtual: schema.virtuals[cur], path: nestedSchemaPath }; + } + continue; + } + + if (schema.nested[cur]) { + continue; + } + + if (schema.paths[cur] && schema.paths[cur].schema) { + schema = schema.paths[cur].schema; + const rest = parts.slice(i + 1).join('.'); + + if (schema.virtuals[rest]) { + if (i === parts.length - 2) { + return { + virtual: schema.virtuals[rest], + nestedSchemaPath: [nestedSchemaPath, cur].filter(v => !!v).join('.') + }; + } + continue; + } + + if (i + 1 < parts.length && schema.discriminators) { + for (const key of Object.keys(schema.discriminators)) { + const res = getVirtual(schema.discriminators[key], rest); + if (res != null) { + const _path = [nestedSchemaPath, cur, res.nestedSchemaPath]. + filter(v => !!v).join('.'); + return { + virtual: res.virtual, + nestedSchemaPath: _path + }; + } + } + } + + nestedSchemaPath += (nestedSchemaPath.length > 0 ? '.' : '') + cur; + cur = ''; + continue; + } else if (schema.paths[cur]?.$isSchemaMap && schema.paths[cur].$__schemaType?.schema) { + schema = schema.paths[cur].$__schemaType.schema; + ++i; + const rest = parts.slice(i + 1).join('.'); + + if (schema.virtuals[rest]) { + if (i === parts.length - 2) { + return { + virtual: schema.virtuals[rest], + nestedSchemaPath: [nestedSchemaPath, cur, '$*'].filter(v => !!v).join('.') + }; + } + continue; + } + + if (i + 1 < parts.length && schema.discriminators) { + for (const key of Object.keys(schema.discriminators)) { + const res = getVirtual(schema.discriminators[key], rest); + if (res != null) { + const _path = [nestedSchemaPath, cur, res.nestedSchemaPath, '$*']. + filter(v => !!v).join('.'); + return { + virtual: res.virtual, + nestedSchemaPath: _path + }; + } + } + } + + nestedSchemaPath += (nestedSchemaPath.length > 0 ? '.' : '') + '$*' + cur; + cur = ''; + } + + if (schema.discriminators) { + for (const discriminatorKey of Object.keys(schema.discriminators)) { + const virtualFromDiscriminator = getVirtual(schema.discriminators[discriminatorKey], name); + if (virtualFromDiscriminator) return virtualFromDiscriminator; + } + } + + return null; + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js b/backend/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js new file mode 100644 index 0000000..9ff9b13 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/leanPopulateMap.js @@ -0,0 +1,7 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = new WeakMap(); diff --git a/backend/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js b/backend/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js new file mode 100644 index 0000000..b85d8d7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/lookupLocalFields.js @@ -0,0 +1,40 @@ +'use strict'; + +module.exports = function lookupLocalFields(cur, path, val) { + if (cur == null) { + return cur; + } + + if (cur._doc != null) { + cur = cur._doc; + } + + if (arguments.length >= 3) { + if (typeof cur !== 'object') { + return void 0; + } + if (val === void 0) { + return void 0; + } + if (cur instanceof Map) { + cur.set(path, val); + } else { + cur[path] = val; + } + return val; + } + + + // Support populating paths under maps using `map.$*.subpath` + if (path === '$*') { + return cur instanceof Map ? + Array.from(cur.values()) : + Object.keys(cur).map(key => cur[key]); + } + + if (cur instanceof Map) { + return cur.get(path); + } + + return cur[path]; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js b/backend/node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js new file mode 100644 index 0000000..5967385 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/markArraySubdocsPopulated.js @@ -0,0 +1,49 @@ +'use strict'; + +const utils = require('../../utils'); + +/** + * If populating a path within a document array, make sure each + * subdoc within the array knows its subpaths are populated. + * + * #### Example: + * + * const doc = await Article.findOne().populate('comments.author'); + * doc.comments[0].populated('author'); // Should be set + * + * @param {Document} doc + * @param {object} [populated] + * @api private + */ + +module.exports = function markArraySubdocsPopulated(doc, populated) { + if (doc._doc._id == null || populated == null || populated.length === 0) { + return; + } + + const id = String(doc._doc._id); + for (const item of populated) { + if (item.isVirtual) { + continue; + } + const path = item.path; + const pieces = path.split('.'); + for (let i = 0; i < pieces.length - 1; ++i) { + const subpath = pieces.slice(0, i + 1).join('.'); + const rest = pieces.slice(i + 1).join('.'); + const val = doc.get(subpath); + if (val == null) { + continue; + } + + if (utils.isMongooseDocumentArray(val)) { + for (let j = 0; j < val.length; ++j) { + if (val[j]) { + val[j].populated(rest, item._docs[id] == null ? void 0 : item._docs[id][j], item); + } + } + break; + } + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js b/backend/node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js new file mode 100644 index 0000000..ff8a1a0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/modelNamesFromRefPath.js @@ -0,0 +1,74 @@ +'use strict'; + +const MongooseError = require('../../error/mongooseError'); +const isPathExcluded = require('../projection/isPathExcluded'); +const lookupLocalFields = require('./lookupLocalFields'); +const mpath = require('mpath'); +const util = require('util'); +const utils = require('../../utils'); + +const hasNumericPropRE = /(\.\d+$|\.\d+\.)/g; + +module.exports = function modelNamesFromRefPath(refPath, doc, populatedPath, modelSchema, queryProjection) { + if (refPath == null) { + return []; + } + + if (typeof refPath !== 'string') { + throw new MongooseError('`refPath` must be a string or a function that returns a string, got ' + util.inspect(refPath)); + } + + if (queryProjection != null && isPathExcluded(queryProjection, refPath)) { + throw new MongooseError('refPath `' + refPath + '` must not be excluded in projection, got ' + + util.inspect(queryProjection)); + } + + // If populated path has numerics, the end `refPath` should too. For example, + // if populating `a.0.b` instead of `a.b` and `b` has `refPath = a.c`, we + // should return `a.0.c` for the refPath. + + if (hasNumericPropRE.test(populatedPath)) { + const chunks = populatedPath.split(hasNumericPropRE); + + if (chunks[chunks.length - 1] === '') { + throw new Error('Can\'t populate individual element in an array'); + } + + let _refPath = ''; + let _remaining = refPath; + // 2nd, 4th, etc. will be numeric props. For example: `[ 'a', '.0.', 'b' ]` + for (let i = 0; i < chunks.length; i += 2) { + const chunk = chunks[i]; + if ( + _remaining.length >= chunk.length + 1 && + _remaining.charAt(chunk.length) === '.' && + _remaining.slice(0, chunk.length) === chunk + ) { + _refPath += _remaining.substring(0, chunk.length) + chunks[i + 1]; + _remaining = _remaining.substring(chunk.length + 1); + } else if (i === chunks.length - 1) { + _refPath += _remaining; + _remaining = ''; + break; + } else { + throw new Error('Could not normalize ref path, chunk ' + chunk + ' not in populated path'); + } + } + + const refValue = mpath.get(_refPath, doc, lookupLocalFields); + let modelNames = Array.isArray(refValue) ? refValue : [refValue]; + modelNames = utils.array.flatten(modelNames); + return modelNames; + } + + const refValue = mpath.get(refPath, doc, lookupLocalFields); + + let modelNames; + if (modelSchema != null && Object.hasOwn(modelSchema.virtuals, refPath)) { + modelNames = [modelSchema.virtuals[refPath].applyGetters(void 0, doc)]; + } else { + modelNames = Array.isArray(refValue) ? refValue : [refValue]; + } + + return modelNames; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js b/backend/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js new file mode 100644 index 0000000..069adec --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/removeDeselectedForeignField.js @@ -0,0 +1,31 @@ +'use strict'; + +const get = require('../get'); +const mpath = require('mpath'); +const parseProjection = require('../projection/parseProjection'); + +/*! + * ignore + */ + +module.exports = function removeDeselectedForeignField(foreignFields, options, docs) { + const projection = parseProjection(get(options, 'select', null), true) || + parseProjection(get(options, 'options.select', null), true); + + if (projection == null) { + return; + } + for (const foreignField of foreignFields) { + if (!Object.hasOwn(projection, '-' + foreignField)) { + continue; + } + + for (const val of docs) { + if (val.$__ != null) { + mpath.unset(foreignField, val._doc); + } else { + mpath.unset(foreignField, val); + } + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/setPopulatedVirtualValue.js b/backend/node_modules/mongoose/lib/helpers/populate/setPopulatedVirtualValue.js new file mode 100644 index 0000000..c629ab1 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/setPopulatedVirtualValue.js @@ -0,0 +1,33 @@ +'use strict'; + +/** + * Set a populated virtual value on a document's `$$populatedVirtuals` value + * + * @param {object} populatedVirtuals A document's `$$populatedVirtuals` + * @param {string} name The virtual name + * @param {any} v The result of the populate query + * @param {object} options The populate options. This function handles `justOne` and `count` options. + * @returns {Document[]|Document|object|object[]} the populated virtual value that was set + */ + +module.exports = function setPopulatedVirtualValue(populatedVirtuals, name, v, options) { + if (options.justOne || options.count) { + populatedVirtuals[name] = Array.isArray(v) ? + v[0] : + v; + + if (typeof populatedVirtuals[name] !== 'object') { + populatedVirtuals[name] = options.count ? v : null; + } + } else { + populatedVirtuals[name] = Array.isArray(v) ? + v : + v == null ? [] : [v]; + + populatedVirtuals[name] = populatedVirtuals[name].filter(function(doc) { + return doc && typeof doc === 'object'; + }); + } + + return populatedVirtuals[name]; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/skipPopulateValue.js b/backend/node_modules/mongoose/lib/helpers/populate/skipPopulateValue.js new file mode 100644 index 0000000..38f3f39 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/skipPopulateValue.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = function SkipPopulateValue(val) { + if (!(this instanceof SkipPopulateValue)) { + return new SkipPopulateValue(val); + } + + this.val = val; + return this; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/populate/validateRef.js b/backend/node_modules/mongoose/lib/helpers/populate/validateRef.js new file mode 100644 index 0000000..2b58e16 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/populate/validateRef.js @@ -0,0 +1,19 @@ +'use strict'; + +const MongooseError = require('../../error/mongooseError'); +const util = require('util'); + +module.exports = validateRef; + +function validateRef(ref, path) { + if (typeof ref === 'string') { + return; + } + + if (typeof ref === 'function') { + return; + } + + throw new MongooseError('Invalid ref at path "' + path + '". Got ' + + util.inspect(ref, { depth: 0 })); +} diff --git a/backend/node_modules/mongoose/lib/helpers/printJestWarning.js b/backend/node_modules/mongoose/lib/helpers/printJestWarning.js new file mode 100644 index 0000000..c0c9a0a --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/printJestWarning.js @@ -0,0 +1,21 @@ +'use strict'; + +const utils = require('../utils'); + +if (typeof jest !== 'undefined' && !process.env.SUPPRESS_JEST_WARNINGS) { + if (typeof window !== 'undefined') { + utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' + + 'with Jest\'s default jsdom test environment. Please make sure you read ' + + 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' + + 'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' + + 'to hide this warning.'); + } + + if (typeof setTimeout.clock?.Date === 'function') { + utils.warn('Mongoose: looks like you\'re trying to test a Mongoose app ' + + 'with Jest\'s mock timers enabled. Please make sure you read ' + + 'Mongoose\'s docs on configuring Jest to test Node.js apps: ' + + 'https://mongoosejs.com/docs/jest.html. Set the SUPPRESS_JEST_WARNINGS to true ' + + 'to hide this warning.'); + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/processConnectionOptions.js b/backend/node_modules/mongoose/lib/helpers/processConnectionOptions.js new file mode 100644 index 0000000..e99f951 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/processConnectionOptions.js @@ -0,0 +1,65 @@ +'use strict'; + +const clone = require('./clone'); +const MongooseError = require('../error/index'); + +function processConnectionOptions(uri, options) { + const opts = options ? options : {}; + const readPreference = opts.readPreference + ? opts.readPreference + : getUriReadPreference(uri); + + const clonedOpts = clone(opts); + const resolvedOpts = (readPreference && readPreference !== 'primary' && readPreference !== 'primaryPreferred') + ? resolveOptsConflicts(readPreference, clonedOpts) + : clonedOpts; + + return resolvedOpts; +} + +function resolveOptsConflicts(pref, opts) { + // don't silently override user-provided indexing options + if (setsIndexOptions(opts) && setsSecondaryRead(pref)) { + throwReadPreferenceError(); + } + + // if user has not explicitly set any auto-indexing options, + // we can silently default them all to false + else { + return defaultIndexOptsToFalse(opts); + } +} + +function setsIndexOptions(opts) { + const configIdx = opts.config?.autoIndex; + const { autoCreate, autoIndex } = opts; + return !!(configIdx || autoCreate || autoIndex); +} + +function setsSecondaryRead(prefString) { + return !!(prefString === 'secondary' || prefString === 'secondaryPreferred'); +} + +function getUriReadPreference(connectionString) { + const exp = /(?:&|\?)readPreference=(\w+)(?:&|$)/; + const match = exp.exec(connectionString); + return match ? match[1] : null; +} + +function defaultIndexOptsToFalse(opts) { + opts.config = { autoIndex: false }; + opts.autoCreate = false; + opts.autoIndex = false; + return opts; +} + +function throwReadPreferenceError() { + throw new MongooseError( + 'MongoDB prohibits index creation on connections that read from ' + + 'non-primary replicas. Connections that set "readPreference" to "secondary" or ' + + '"secondaryPreferred" may not opt-in to the following connection options: ' + + 'autoCreate, autoIndex' + ); +} + +module.exports = processConnectionOptions; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/applyProjection.js b/backend/node_modules/mongoose/lib/helpers/projection/applyProjection.js new file mode 100644 index 0000000..7b8b1a3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/applyProjection.js @@ -0,0 +1,83 @@ +'use strict'; + +const hasIncludedChildren = require('./hasIncludedChildren'); +const isExclusive = require('./isExclusive'); +const isInclusive = require('./isInclusive'); +const isPOJO = require('../../utils').isPOJO; + +module.exports = function applyProjection(doc, projection, _hasIncludedChildren) { + if (projection == null) { + return doc; + } + if (doc == null) { + return doc; + } + + let exclude = null; + if (isInclusive(projection)) { + exclude = false; + } else if (isExclusive(projection)) { + exclude = true; + } + + if (exclude == null) { + return doc; + } else if (exclude) { + _hasIncludedChildren = _hasIncludedChildren || hasIncludedChildren(projection); + return applyExclusiveProjection(doc, projection, _hasIncludedChildren); + } else { + _hasIncludedChildren = _hasIncludedChildren || hasIncludedChildren(projection); + return applyInclusiveProjection(doc, projection, _hasIncludedChildren); + } +}; + +function applyExclusiveProjection(doc, projection, hasIncludedChildren, projectionLimb, prefix) { + if (doc == null || typeof doc !== 'object') { + return doc; + } + if (Array.isArray(doc)) { + return doc.map(el => applyExclusiveProjection(el, projection, hasIncludedChildren, projectionLimb, prefix)); + } + const ret = { ...doc }; + projectionLimb = prefix ? (projectionLimb || {}) : projection; + + for (const key of Object.keys(ret)) { + const fullPath = prefix ? prefix + '.' + key : key; + if (Object.hasOwn(projection, fullPath) || Object.hasOwn(projectionLimb, key)) { + if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) { + ret[key] = applyExclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath); + } else { + delete ret[key]; + } + } else if (hasIncludedChildren[fullPath]) { + ret[key] = applyExclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath); + } + } + return ret; +} + +function applyInclusiveProjection(doc, projection, hasIncludedChildren, projectionLimb, prefix) { + if (doc == null || typeof doc !== 'object') { + return doc; + } + if (Array.isArray(doc)) { + return doc.map(el => applyInclusiveProjection(el, projection, hasIncludedChildren, projectionLimb, prefix)); + } + const ret = { ...doc }; + projectionLimb = prefix ? (projectionLimb || {}) : projection; + + for (const key of Object.keys(ret)) { + const fullPath = prefix ? prefix + '.' + key : key; + if (Object.hasOwn(projection, fullPath) || Object.hasOwn(projectionLimb, key)) { + if (isPOJO(projection[fullPath]) || isPOJO(projectionLimb[key])) { + ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath); + } + continue; + } else if (hasIncludedChildren[fullPath]) { + ret[key] = applyInclusiveProjection(ret[key], projection, hasIncludedChildren, projectionLimb[key], fullPath); + } else { + delete ret[key]; + } + } + return ret; +} diff --git a/backend/node_modules/mongoose/lib/helpers/projection/hasIncludedChildren.js b/backend/node_modules/mongoose/lib/helpers/projection/hasIncludedChildren.js new file mode 100644 index 0000000..8198345 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/hasIncludedChildren.js @@ -0,0 +1,41 @@ +'use strict'; + +/** + * Creates an object that precomputes whether a given path has child fields in + * the projection. + * + * #### Example: + * + * const res = hasIncludedChildren({ 'a.b.c': 0 }); + * res.a; // 1 + * res['a.b']; // 1 + * res['a.b.c']; // 1 + * res['a.c']; // undefined + * + * @param {object} fields + * @api private + */ + +module.exports = function hasIncludedChildren(fields) { + const hasIncludedChildren = {}; + const keys = Object.keys(fields); + + for (const key of keys) { + + if (key.indexOf('.') === -1) { + hasIncludedChildren[key] = 1; + continue; + } + const parts = key.split('.'); + let c = parts[0]; + + for (let i = 0; i < parts.length; ++i) { + hasIncludedChildren[c] = 1; + if (i + 1 < parts.length) { + c = c + '.' + parts[i + 1]; + } + } + } + + return hasIncludedChildren; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js b/backend/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js new file mode 100644 index 0000000..67dfb39 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isDefiningProjection.js @@ -0,0 +1,18 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function isDefiningProjection(val) { + if (val == null) { + // `undefined` or `null` become exclusive projections + return true; + } + if (typeof val === 'object') { + // Only cases where a value does **not** define whether the whole projection + // is inclusive or exclusive are `$meta` and `$slice`. + return !('$meta' in val) && !('$slice' in val); + } + return true; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isExclusive.js b/backend/node_modules/mongoose/lib/helpers/projection/isExclusive.js new file mode 100644 index 0000000..e6ca3ca --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isExclusive.js @@ -0,0 +1,37 @@ +'use strict'; + +const isDefiningProjection = require('./isDefiningProjection'); +const isPOJO = require('../isPOJO'); + +/*! + * ignore + */ + +module.exports = function isExclusive(projection) { + if (projection == null) { + return null; + } + + const keys = Object.keys(projection); + let exclude = null; + + if (keys.length === 1 && keys[0] === '_id') { + exclude = !projection._id; + } else { + for (let ki = 0; ki < keys.length; ++ki) { + // Does this projection explicitly define inclusion/exclusion? + // Explicitly avoid `$meta` and `$slice` + const key = keys[ki]; + if (key !== '_id' && isDefiningProjection(projection[key])) { + exclude = isPOJO(projection[key]) ? + (isExclusive(projection[key]) ?? exclude) : + !projection[key]; + if (exclude != null) { + break; + } + } + } + } + + return exclude; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isInclusive.js b/backend/node_modules/mongoose/lib/helpers/projection/isInclusive.js new file mode 100644 index 0000000..c53bac0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isInclusive.js @@ -0,0 +1,39 @@ +'use strict'; + +const isDefiningProjection = require('./isDefiningProjection'); +const isPOJO = require('../isPOJO'); + +/*! + * ignore + */ + +module.exports = function isInclusive(projection) { + if (projection == null) { + return false; + } + + const props = Object.keys(projection); + const numProps = props.length; + if (numProps === 0) { + return false; + } + + for (let i = 0; i < numProps; ++i) { + const prop = props[i]; + // Plus paths can't define the projection (see gh-7050) + if (prop.startsWith('+')) { + continue; + } + // If field is truthy (1, true, etc.) and not an object, then this + // projection must be inclusive. If object, assume its $meta, $slice, etc. + if (isDefiningProjection(projection[prop]) && !!projection[prop]) { + if (isPOJO(projection[prop])) { + return isInclusive(projection[prop]); + } else { + return !!projection[prop]; + } + } + } + + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isNestedProjection.js b/backend/node_modules/mongoose/lib/helpers/projection/isNestedProjection.js new file mode 100644 index 0000000..f53d9cd --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isNestedProjection.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = function isNestedProjection(val) { + if (val == null || typeof val !== 'object') { + return false; + } + return val.$slice == null && val.$elemMatch == null && val.$meta == null && val.$ == null; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js b/backend/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js new file mode 100644 index 0000000..698e1c4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isPathExcluded.js @@ -0,0 +1,40 @@ +'use strict'; + +const isDefiningProjection = require('./isDefiningProjection'); + +/** + * Determines if `path` is excluded by `projection` + * + * @param {object} projection + * @param {string} path + * @return {boolean} + * @api private + */ + +module.exports = function isPathExcluded(projection, path) { + if (projection == null) { + return false; + } + + if (path === '_id') { + return projection._id === 0; + } + + const paths = Object.keys(projection); + let type = null; + + for (const _path of paths) { + if (isDefiningProjection(projection[_path])) { + type = projection[path] === 1 ? 'inclusive' : 'exclusive'; + break; + } + } + + if (type === 'inclusive') { + return projection[path] !== 1; + } + if (type === 'exclusive') { + return projection[path] === 0; + } + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js b/backend/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js new file mode 100644 index 0000000..8a05fc9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isPathSelectedInclusive.js @@ -0,0 +1,28 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function isPathSelectedInclusive(fields, path) { + const chunks = path.split('.'); + let cur = ''; + let j; + let keys; + let numKeys; + for (let i = 0; i < chunks.length; ++i) { + cur += cur.length ? '.' : '' + chunks[i]; + if (fields[cur]) { + keys = Object.keys(fields); + numKeys = keys.length; + for (j = 0; j < numKeys; ++j) { + if (keys[i].indexOf(cur + '.') === 0 && keys[i].indexOf(path) !== 0) { + continue; + } + } + return true; + } + } + + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/isSubpath.js b/backend/node_modules/mongoose/lib/helpers/projection/isSubpath.js new file mode 100644 index 0000000..635065d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/isSubpath.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Determines if `path2` is a subpath of or equal to `path1` + * + * @param {string} path1 + * @param {string} path2 + * @return {boolean} + * @api private + */ + +module.exports = function isSubpath(path1, path2) { + return path1 === path2 || path2.startsWith(path1 + '.'); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/projection/parseProjection.js b/backend/node_modules/mongoose/lib/helpers/projection/parseProjection.js new file mode 100644 index 0000000..432de16 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/projection/parseProjection.js @@ -0,0 +1,38 @@ +'use strict'; + +/** + * Convert a string or array into a projection object. Treats `-foo` as + * equivalent to `foo: 0` depending on `retainMinusPaths`. If `retainMinusPaths` + * is true, then `-foo` will be included in the projection as `'-foo': 0`. + * + * @param {object|string|string[]} v + * @param {boolean} [retainMinusPaths] + * @return {object} + */ + +module.exports = function parseProjection(v, retainMinusPaths) { + const type = typeof v; + + if (type === 'string') { + v = v.split(/\s+/); + } + if (!Array.isArray(v) && Object.prototype.toString.call(v) !== '[object Arguments]') { + return v; + } + + const len = v.length; + const ret = {}; + for (let i = 0; i < len; ++i) { + let field = v[i]; + if (!field) { + continue; + } + const include = field.charAt(0) === '-' ? 0 : 1; + if (!retainMinusPaths && include === 0) { + field = field.slice(1); + } + ret[field] = include; + } + + return ret; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/applyGlobalOption.js b/backend/node_modules/mongoose/lib/helpers/query/applyGlobalOption.js new file mode 100644 index 0000000..e93fa73 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/applyGlobalOption.js @@ -0,0 +1,29 @@ +'use strict'; + +const utils = require('../../utils'); + +function applyGlobalMaxTimeMS(options, connectionOptions, baseOptions) { + applyGlobalOption(options, connectionOptions, baseOptions, 'maxTimeMS'); +} + +function applyGlobalDiskUse(options, connectionOptions, baseOptions) { + applyGlobalOption(options, connectionOptions, baseOptions, 'allowDiskUse'); +} + +module.exports = { + applyGlobalMaxTimeMS, + applyGlobalDiskUse +}; + + +function applyGlobalOption(options, connectionOptions, baseOptions, optionName) { + if (utils.hasUserDefinedProperty(options, optionName)) { + return; + } + + if (utils.hasUserDefinedProperty(connectionOptions, optionName)) { + options[optionName] = connectionOptions[optionName]; + } else if (utils.hasUserDefinedProperty(baseOptions, optionName)) { + options[optionName] = baseOptions[optionName]; + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/query/cast$expr.js b/backend/node_modules/mongoose/lib/helpers/query/cast$expr.js new file mode 100644 index 0000000..dfbfd47 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/cast$expr.js @@ -0,0 +1,287 @@ +'use strict'; + +const CastError = require('../../error/cast'); +const StrictModeError = require('../../error/strict'); +const castNumber = require('../../cast/number'); +const omitUndefined = require('../omitUndefined'); + +const booleanComparison = new Set(['$and', '$or']); +const comparisonOperator = new Set(['$cmp', '$eq', '$lt', '$lte', '$gt', '$gte']); +const arithmeticOperatorArray = new Set([ + // avoid casting '$add' or '$subtract', because expressions can be either number or date, + // and we don't have a good way of inferring which arguments should be numbers and which should + // be dates. + '$multiply', + '$divide', + '$log', + '$mod', + '$trunc', + '$avg', + '$max', + '$min', + '$stdDevPop', + '$stdDevSamp', + '$sum' +]); +const arithmeticOperatorNumber = new Set([ + '$abs', + '$exp', + '$ceil', + '$floor', + '$ln', + '$log10', + '$sqrt', + '$sin', + '$cos', + '$tan', + '$asin', + '$acos', + '$atan', + '$atan2', + '$asinh', + '$acosh', + '$atanh', + '$sinh', + '$cosh', + '$tanh', + '$degreesToRadians', + '$radiansToDegrees' +]); +const arrayElementOperators = new Set([ + '$arrayElemAt', + '$first', + '$last' +]); +const dateOperators = new Set([ + '$year', + '$month', + '$week', + '$dayOfMonth', + '$dayOfYear', + '$hour', + '$minute', + '$second', + '$isoDayOfWeek', + '$isoWeekYear', + '$isoWeek', + '$millisecond' +]); +const expressionOperator = new Set(['$not']); + +module.exports = function cast$expr(val, schema, strictQuery) { + if (typeof val === 'boolean') { + return val; + } + if (typeof val !== 'object' || val === null) { + throw new Error('`$expr` must be an object or boolean literal'); + } + + return _castExpression(val, schema, strictQuery); +}; + +function _castExpression(val, schema, strictQuery) { + // Preserve the value if it represents a path or if it's null + if (isPath(val) || val === null) { + return val; + } + + if (val.$cond != null) { + if (Array.isArray(val.$cond)) { + val.$cond = val.$cond.map(expr => _castExpression(expr, schema, strictQuery)); + } else { + val.$cond.if = _castExpression(val.$cond.if, schema, strictQuery); + val.$cond.then = _castExpression(val.$cond.then, schema, strictQuery); + val.$cond.else = _castExpression(val.$cond.else, schema, strictQuery); + } + } else if (val.$ifNull != null) { + val.$ifNull.map(v => _castExpression(v, schema, strictQuery)); + } else if (val.$switch != null) { + if (Array.isArray(val.$switch.branches)) { + val.$switch.branches = val.$switch.branches.map(v => _castExpression(v, schema, strictQuery)); + } + if ('default' in val.$switch) { + val.$switch.default = _castExpression(val.$switch.default, schema, strictQuery); + } + } + + const keys = Object.keys(val); + for (const key of keys) { + if (booleanComparison.has(key)) { + val[key] = val[key].map(v => _castExpression(v, schema, strictQuery)); + } else if (comparisonOperator.has(key)) { + val[key] = castComparison(val[key], schema, strictQuery); + } else if (arithmeticOperatorArray.has(key)) { + val[key] = castArithmetic(val[key], schema, strictQuery); + } else if (arithmeticOperatorNumber.has(key)) { + val[key] = castNumberOperator(val[key], schema, strictQuery); + } else if (expressionOperator.has(key)) { + val[key] = _castExpression(val[key], schema, strictQuery); + } + } + + if (val.$in) { + val.$in = castIn(val.$in, schema, strictQuery); + } + if (val.$size) { + val.$size = castNumberOperator(val.$size, schema, strictQuery); + } + if (val.$round) { + const $round = val.$round; + if (!Array.isArray($round) || $round.length < 1 || $round.length > 2) { + throw new CastError('Array', $round, '$round'); + } + val.$round = $round.map(v => castNumberOperator(v, schema, strictQuery)); + } + + omitUndefined(val); + + return val; +} + +// { $op: } +function castNumberOperator(val) { + if (!isLiteral(val)) { + return val; + } + + try { + return castNumber(val); + } catch { + throw new CastError('Number', val); + } +} + +function castIn(val, schema, strictQuery) { + const path = val[1]; + if (!isPath(path)) { + return val; + } + const search = val[0]; + + const schematype = schema.path(path.slice(1)); + if (schematype === null) { + if (strictQuery === false) { + return val; + } else if (strictQuery === 'throw') { + throw new StrictModeError('$in'); + } + + return void 0; + } + + if (!schematype.$isMongooseArray) { + throw new Error('Path must be an array for $in'); + } + + return [ + schematype.embeddedSchemaType.cast(search), + path + ]; +} + +// { $op: [, ] } +function castArithmetic(val) { + if (!Array.isArray(val)) { + if (!isLiteral(val)) { + return val; + } + try { + return castNumber(val); + } catch { + throw new CastError('Number', val); + } + } + + return val.map(v => { + if (!isLiteral(v)) { + return v; + } + try { + return castNumber(v); + } catch { + throw new CastError('Number', v); + } + }); +} + +// { $op: [expression, expression] } +function castComparison(val, schema, strictQuery) { + if (!Array.isArray(val) || val.length !== 2) { + throw new Error('Comparison operator must be an array of length 2'); + } + + val[0] = _castExpression(val[0], schema, strictQuery); + const lhs = val[0]; + + if (isLiteral(val[1])) { + let path = null; + let schematype = null; + let caster = null; + if (isPath(lhs)) { + path = lhs.slice(1); + schematype = schema.path(path); + } else if (typeof lhs === 'object' && lhs != null) { + for (const key of Object.keys(lhs)) { + if (dateOperators.has(key) && isPath(lhs[key])) { + path = lhs[key].slice(1) + '.' + key; + caster = castNumber; + } else if (arrayElementOperators.has(key) && isPath(lhs[key])) { + path = lhs[key].slice(1) + '.' + key; + schematype = schema.path(lhs[key].slice(1)); + if (schematype != null) { + if (schematype.$isMongooseArray) { + schematype = schematype.embeddedSchemaType; + } + } + } + } + } + + const is$literal = typeof val[1] === 'object' && val[1] != null && val[1].$literal != null; + if (schematype != null) { + if (is$literal) { + val[1] = { $literal: schematype.cast(val[1].$literal) }; + } else { + val[1] = schematype.cast(val[1]); + } + } else if (caster != null) { + if (is$literal) { + try { + val[1] = { $literal: caster(val[1].$literal) }; + } catch { + throw new CastError(caster.name.replace(/^cast/, ''), val[1], path + '.$literal'); + } + } else { + try { + val[1] = caster(val[1]); + } catch { + throw new CastError(caster.name.replace(/^cast/, ''), val[1], path); + } + } + } else if (path != null && strictQuery === true) { + return void 0; + } else if (path != null && strictQuery === 'throw') { + throw new StrictModeError(path); + } + } else { + val[1] = _castExpression(val[1]); + } + + return val; +} + +function isPath(val) { + return typeof val === 'string' && val[0] === '$'; +} + +function isLiteral(val) { + if (typeof val === 'string' && val[0] === '$') { + return false; + } + if (typeof val === 'object' && val !== null && Object.keys(val).find(key => key[0] === '$')) { + // The `$literal` expression can make an object a literal + // https://www.mongodb.com/docs/manual/reference/operator/aggregation/literal/#mongodb-expression-exp.-literal + return val.$literal != null; + } + return true; +} diff --git a/backend/node_modules/mongoose/lib/helpers/query/castFilterPath.js b/backend/node_modules/mongoose/lib/helpers/query/castFilterPath.js new file mode 100644 index 0000000..5303852 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/castFilterPath.js @@ -0,0 +1,54 @@ +'use strict'; + +const isOperator = require('./isOperator'); + +module.exports = function castFilterPath(ctx, schematype, val) { + const any$conditionals = Object.keys(val).some(isOperator); + + if (!any$conditionals) { + return schematype.castForQuery( + null, + val, + ctx + ); + } + + const ks = Object.keys(val); + + let k = ks.length; + + while (k--) { + const $cond = ks[k]; + const nested = val[$cond]; + + if ($cond === '$not') { + if (nested && schematype && !schematype.embeddedSchemaType && !schematype.Constructor) { + const _keys = Object.keys(nested); + if (_keys.length && isOperator(_keys[0])) { + for (const key of Object.keys(nested)) { + nested[key] = schematype.castForQuery( + key, + nested[key], + ctx + ); + } + } else { + val[$cond] = schematype.castForQuery( + $cond, + nested, + ctx + ); + } + continue; + } + } else { + val[$cond] = schematype.castForQuery( + $cond, + nested, + ctx + ); + } + } + + return val; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/castUpdate.js b/backend/node_modules/mongoose/lib/helpers/query/castUpdate.js new file mode 100644 index 0000000..da8ea4b --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/castUpdate.js @@ -0,0 +1,643 @@ +'use strict'; + +const CastError = require('../../error/cast'); +const MongooseError = require('../../error/mongooseError'); +const SchemaString = require('../../schema/string'); +const StrictModeError = require('../../error/strict'); +const ValidationError = require('../../error/validation'); +const castNumber = require('../../cast/number'); +const cast = require('../../cast'); +const getConstructorName = require('../getConstructorName'); +const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue'); +const getEmbeddedDiscriminatorPath = require('./getEmbeddedDiscriminatorPath'); +const handleImmutable = require('./handleImmutable'); +const moveImmutableProperties = require('../update/moveImmutableProperties'); +const schemaMixedSymbol = require('../../schema/symbols').schemaMixedSymbol; +const setDottedPath = require('../path/setDottedPath'); +const utils = require('../../utils'); +const { internalToObjectOptions } = require('../../options'); + +const mongodbUpdateOperators = new Set([ + '$currentDate', + '$inc', + '$min', + '$max', + '$mul', + '$rename', + '$set', + '$setOnInsert', + '$unset', + '$addToSet', + '$pop', + '$pull', + '$push', + '$pullAll', + '$bit' +]); + +/** + * Casts an update op based on the given schema + * + * @param {Schema} schema + * @param {object} obj + * @param {object} [options] + * @param {boolean|'throw'} [options.strict] defaults to true + * @param {Query} context passed to setters + * @return {boolean} true iff the update is non-empty + * @api private + */ +module.exports = function castUpdate(schema, obj, options, context, filter) { + if (obj == null) { + return undefined; + } + options = options || {}; + // Update pipeline + if (Array.isArray(obj)) { + const len = obj.length; + for (let i = 0; i < len; ++i) { + const ops = Object.keys(obj[i]); + for (const op of ops) { + obj[i][op] = castPipelineOperator(op, obj[i][op]); + } + } + return obj; + } + + if (schema != null && + filter != null && + utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) && + typeof filter[schema.options.discriminatorKey] !== 'object' && + schema.discriminators != null) { + const discriminatorValue = filter[schema.options.discriminatorKey]; + const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue); + schema = schema.discriminators[discriminatorValue] || + byValue?.schema || + schema; + } else if (schema != null && + options.overwriteDiscriminatorKey && + utils.hasUserDefinedProperty(obj, schema.options.discriminatorKey) && + schema.discriminators != null) { + const discriminatorValue = obj[schema.options.discriminatorKey]; + const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue); + schema = schema.discriminators[discriminatorValue] || + byValue?.schema || + schema; + } else if (schema != null && + options.overwriteDiscriminatorKey && + obj.$set != null && + utils.hasUserDefinedProperty(obj.$set, schema.options.discriminatorKey) && + schema.discriminators != null) { + const discriminatorValue = obj.$set[schema.options.discriminatorKey]; + const byValue = getDiscriminatorByValue(context.model.discriminators, discriminatorValue); + schema = schema.discriminators[discriminatorValue] || + byValue?.schema || + schema; + } + + if (options.upsert) { + moveImmutableProperties(schema, obj, context); + } + + const ops = Object.keys(obj); + let i = ops.length; + const ret = {}; + let val; + let hasDollarKey = false; + + filter = filter || {}; + while (i--) { + const op = ops[i]; + if (!mongodbUpdateOperators.has(op)) { + // fix up $set sugar + if (!ret.$set) { + if (obj.$set) { + ret.$set = obj.$set; + } else { + ret.$set = {}; + } + } + ret.$set[op] = obj[op]; + ops.splice(i, 1); + if (!~ops.indexOf('$set')) ops.push('$set'); + } else if (op === '$set') { + if (!ret.$set) { + ret[op] = obj[op]; + } + } else { + ret[op] = obj[op]; + } + } + // cast each value + i = ops.length; + while (i--) { + const op = ops[i]; + val = ret[op]; + hasDollarKey = hasDollarKey || op.startsWith('$'); + if (val?.$__) { + val = val.toObject(internalToObjectOptions); + ret[op] = val; + } + if (val && + typeof val === 'object' && + !Buffer.isBuffer(val) && + mongodbUpdateOperators.has(op)) { + walkUpdatePath(schema, val, op, options, context, filter); + } else { + const msg = 'Invalid atomic update value for ' + op + '. ' + + 'Expected an object, received ' + typeof val; + throw new Error(msg); + } + + if (op.startsWith('$') && utils.isEmptyObject(val)) { + delete ret[op]; + } + } + + if (utils.hasOwnKeys(ret) === false && + options.upsert && + utils.hasOwnKeys(filter)) { + // Trick the driver into allowing empty upserts to work around + // https://github.com/mongodb/node-mongodb-native/pull/2490 + // Shallow clone to avoid passing defaults in re: gh-13962 + return { $setOnInsert: { ...filter } }; + } + return ret; +}; + +/*! + * ignore + */ + +function castPipelineOperator(op, val) { + if (op === '$unset') { + if (typeof val !== 'string' && (!Array.isArray(val) || val.find(v => typeof v !== 'string'))) { + throw new MongooseError('Invalid $unset in pipeline, must be ' + + ' a string or an array of strings'); + } + return val; + } + if (op === '$project') { + if (val == null || typeof val !== 'object') { + throw new MongooseError('Invalid $project in pipeline, must be an object'); + } + return val; + } + if (op === '$addFields' || op === '$set') { + if (val == null || typeof val !== 'object') { + throw new MongooseError('Invalid ' + op + ' in pipeline, must be an object'); + } + return val; + } else if (op === '$replaceRoot' || op === '$replaceWith') { + if (val == null || typeof val !== 'object') { + throw new MongooseError('Invalid ' + op + ' in pipeline, must be an object'); + } + return val; + } + + throw new MongooseError('Invalid update pipeline operator: "' + op + '"'); +} + +/** + * Walk each path of obj and cast its values + * according to its schema. + * + * @param {Schema} schema + * @param {object} obj part of a query + * @param {string} op the atomic operator ($pull, $set, etc) + * @param {object} [options] + * @param {boolean|'throw'} [options.strict] + * @param {Query} context + * @param {object} filter + * @param {string} pref path prefix (internal only) + * @return {Bool} true if this path has keys to update + * @api private + */ + +function walkUpdatePath(schema, obj, op, options, context, filter, prefix) { + const strict = options.strict; + prefix = prefix ? prefix + '.' : ''; + const keys = Object.keys(obj); + let i = keys.length; + let hasKeys = false; + let schematype; + let key; + let val; + + let aggregatedError = null; + + const strictMode = strict ?? schema.options.strict; + + while (i--) { + key = keys[i]; + val = obj[key]; + + // `$pull` is special because we need to cast the RHS as a query, not as + // an update. + if (op === '$pull') { + schematype = schema._getSchema(prefix + key); + if (schematype == null) { + const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, prefix + key, options); + if (_res.schematype != null) { + schematype = _res.schematype; + } + } + if (schematype?.schema != null) { + obj[key] = cast(schematype.schema, obj[key], options, context); + hasKeys = true; + continue; + } + } + + const discriminatorKey = (prefix ? prefix + key : key); + if ( + schema.discriminatorMapping != null && + discriminatorKey === schema.options.discriminatorKey && + schema.discriminatorMapping.value !== obj[key] && + !options.overwriteDiscriminatorKey + ) { + if (strictMode === 'throw') { + const err = new Error('Can\'t modify discriminator key "' + discriminatorKey + '" on discriminator model'); + aggregatedError = _appendError(err, context, discriminatorKey, aggregatedError); + continue; + } else if (strictMode) { + delete obj[key]; + continue; + } + } + + if (getConstructorName(val) === 'Object') { + // watch for embedded doc schemas + schematype = schema._getSchema(prefix + key); + + if (schematype == null) { + const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, prefix + key, options); + if (_res.schematype != null) { + schematype = _res.schematype; + } + } + + if (op !== '$setOnInsert' && + handleImmutable(schematype, strict, obj, key, prefix + key, options, context)) { + continue; + } + + if (schematype && (schematype.embeddedSchemaType || schematype.Constructor) && op in castOps) { + // embedded doc schema + if ('$each' in val) { + hasKeys = true; + try { + obj[key] = { + $each: castUpdateVal(schematype, val.$each, op, key, context, prefix + key) + }; + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + + if (val.$slice != null) { + obj[key].$slice = val.$slice | 0; + } + + if (val.$sort) { + obj[key].$sort = val.$sort; + } + + if (val.$position != null) { + obj[key].$position = castNumber(val.$position); + } + } else { + if (schematype?.$isSingleNested) { + const _strict = strict == null ? schematype.schema.options.strict : strict; + try { + obj[key] = schematype.castForQuery(null, val, context, { strict: _strict }); + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + } else { + try { + obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key); + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + } + + if (obj[key] === void 0) { + delete obj[key]; + continue; + } + + hasKeys = true; + } + } else if ((op === '$currentDate') || (op in castOps && schematype)) { + // $currentDate can take an object + try { + obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key); + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + + if (obj[key] === void 0) { + delete obj[key]; + continue; + } + + hasKeys = true; + } else if (op === '$rename') { + const schematype = new SchemaString(`${prefix}${key}.$rename`); + try { + obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key); + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + + if (obj[key] === void 0) { + delete obj[key]; + continue; + } + + hasKeys = true; + } else { + const pathToCheck = (prefix + key); + const v = schema._getPathType(pathToCheck); + let _strict = strict; + if (v?.schema && _strict == null) { + _strict = v.schema.options.strict; + } + + if (v.pathType === 'undefined') { + if (_strict === 'throw') { + throw new StrictModeError(pathToCheck); + } else if (_strict) { + delete obj[key]; + continue; + } + } + + // gh-2314 + // we should be able to set a schema-less field + // to an empty object literal + hasKeys |= walkUpdatePath(schema, val, op, options, context, filter, prefix + key) || + (utils.isObject(val) && utils.hasOwnKeys(val) === false); + } + } else { + const isModifier = (key === '$each' || key === '$or' || key === '$and' || key === '$in'); + if (isModifier && !prefix) { + throw new MongooseError('Invalid update: Unexpected modifier "' + key + '" as a key in operator. ' + + 'Did you mean something like { $addToSet: { fieldName: { $each: [...] } } }? ' + + 'Modifiers such as "$each", "$or", "$and", "$in" must appear under a valid field path.'); + } + const checkPath = isModifier ? prefix : prefix + key; + schematype = schema._getSchema(checkPath); + + // You can use `$setOnInsert` with immutable keys + if (op !== '$setOnInsert' && + handleImmutable(schematype, strict, obj, key, prefix + key, options, context)) { + continue; + } + + let pathDetails = schema._getPathType(checkPath); + + // If no schema type, check for embedded discriminators because the + // filter or update may imply an embedded discriminator type. See #8378 + if (schematype == null) { + const _res = getEmbeddedDiscriminatorPath(schema, obj, filter, checkPath, options); + if (_res.schematype != null) { + schematype = _res.schematype; + pathDetails = _res.type; + } + } + + let isStrict = strict; + if (pathDetails?.schema && strict == null) { + isStrict = pathDetails.schema.options.strict; + } + + const skip = isStrict && + !schematype && + !/real|nested/.test(pathDetails.pathType); + + if (skip) { + // Even if strict is `throw`, avoid throwing an error because of + // virtuals because of #6731 + if (isStrict === 'throw' && schema.virtuals[checkPath] == null) { + throw new StrictModeError(prefix + key); + } else { + delete obj[key]; + } + } else { + if (op === '$rename') { + if (obj[key] == null) { + throw new CastError('String', obj[key], `${prefix}${key}.$rename`); + } + const schematype = new SchemaString(`${prefix}${key}.$rename`, null, null, schema); + obj[key] = schematype.castForQuery(null, obj[key], context); + continue; + } + + try { + if (prefix.length === 0 || key.indexOf('.') === -1) { + obj[key] = castUpdateVal(schematype, val, op, key, context, prefix + key); + } else if (isStrict !== false || schematype != null) { + // Setting a nested dotted path that's in the schema. We don't allow paths with '.' in + // a schema, so replace the dotted path with a nested object to avoid ending up with + // dotted properties in the updated object. See (gh-10200) + setDottedPath(obj, key, castUpdateVal(schematype, val, op, key, context, prefix + key)); + delete obj[key]; + } + } catch (error) { + aggregatedError = _appendError(error, context, key, aggregatedError); + } + + if (Array.isArray(obj[key]) && (op === '$addToSet' || op === '$push') && key !== '$each') { + if (schematype && + schematype.embeddedSchemaType && + !schematype.embeddedSchemaType.$isMongooseArray && + !schematype.embeddedSchemaType[schemaMixedSymbol]) { + obj[key] = { $each: obj[key] }; + } + } + + if (obj[key] === void 0) { + delete obj[key]; + continue; + } + + hasKeys = true; + } + } + } + + if (aggregatedError != null) { + throw aggregatedError; + } + + return hasKeys; +} + +/*! + * ignore + */ + +function _appendError(error, query, key, aggregatedError) { + if (typeof query !== 'object' || !query.options.multipleCastError) { + throw error; + } + aggregatedError = aggregatedError || new ValidationError(); + aggregatedError.addError(key, error); + return aggregatedError; +} + +/** + * These operators should be cast to numbers instead + * of their path schema type. + * @api private + */ + +const numberOps = { + $pop: 1, + $inc: 1 +}; + +/** + * These ops require no casting because the RHS doesn't do anything. + * @api private + */ + +const noCastOps = { + $unset: 1 +}; + +/** + * These operators require casting docs + * to real Documents for Update operations. + * @api private + */ + +const castOps = { + $push: 1, + $addToSet: 1, + $set: 1, + $setOnInsert: 1 +}; + +/*! + * ignore + */ + +const overwriteOps = { + $set: 1, + $setOnInsert: 1 +}; + +/** + * Casts `val` according to `schema` and atomic `op`. + * + * @param {SchemaType} schema + * @param {object} val + * @param {string} op the atomic operator ($pull, $set, etc) + * @param {string} $conditional + * @param {Query} context + * @param {string} path + * @api private + */ + +function castUpdateVal(schema, val, op, $conditional, context, path) { + if (!schema) { + // non-existing schema path + if (op in numberOps) { + try { + return castNumber(val); + } catch { + throw new CastError('number', val, path); + } + } + return val; + } + + const cond = schema.$isMongooseArray + && op in castOps + && (utils.isObject(val) || Array.isArray(val)); + if (cond && !overwriteOps[op]) { + // Cast values for ops that add data to MongoDB. + // Ensures embedded documents get ObjectIds etc. + let schemaArrayDepth = 0; + let cur = schema; + while (cur.$isMongooseArray) { + ++schemaArrayDepth; + cur = cur.embeddedSchemaType; + } + let arrayDepth = 0; + let _val = val; + while (Array.isArray(_val)) { + ++arrayDepth; + _val = _val[0]; + } + + const additionalNesting = schemaArrayDepth - arrayDepth; + while (arrayDepth < schemaArrayDepth) { + val = [val]; + ++arrayDepth; + } + + let tmp = schema.applySetters(Array.isArray(val) ? val : [val], context); + + for (let i = 0; i < additionalNesting; ++i) { + tmp = tmp[0]; + } + return tmp; + } + + if (op in noCastOps) { + return val; + } + if (op in numberOps) { + // Null and undefined not allowed for $pop, $inc + if (val == null) { + throw new CastError('number', val, schema.path); + } + if (op === '$inc') { + // Support `$inc` with long, int32, etc. (gh-4283) + return schema.castForQuery( + null, + val, + context + ); + } + try { + return castNumber(val); + } catch { + throw new CastError('number', val, schema.path); + } + } + if (op === '$currentDate') { + if (typeof val === 'object') { + return { $type: val.$type }; + } + return Boolean(val); + } + + if (mongodbUpdateOperators.has($conditional)) { + return schema.castForQuery( + $conditional, + val, + context + ); + } + + if (overwriteOps[op]) { + const skipQueryCastForUpdate = val != null + && schema.$isMongooseArray + && schema.$fullPath != null + && !schema.$fullPath.match(/\d+$/); + const applySetters = schema[schemaMixedSymbol] != null; + if (skipQueryCastForUpdate || applySetters) { + return schema.applySetters(val, context); + } + return schema.castForQuery( + null, + val, + context + ); + } + + return schema.castForQuery(null, val, context); +} diff --git a/backend/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js b/backend/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js new file mode 100644 index 0000000..3b468e4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/getEmbeddedDiscriminatorPath.js @@ -0,0 +1,103 @@ +'use strict'; + +const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); +const get = require('../get'); +const getDiscriminatorByValue = require('../discriminator/getDiscriminatorByValue'); +const updatedPathsByArrayFilter = require('../update/updatedPathsByArrayFilter'); + +/** + * Like `schema.path()`, except with a document, because impossible to + * determine path type without knowing the embedded discriminator key. + * @param {Schema} schema + * @param {object} [update] + * @param {object} [filter] + * @param {string} path + * @param {object} [options] + * @api private + */ + +module.exports = function getEmbeddedDiscriminatorPath(schema, update, filter, path, options) { + const parts = path.indexOf('.') === -1 ? [path] : path.split('.'); + let schematype = null; + let type = 'adhocOrUndefined'; + + filter = filter || {}; + update = update || {}; + const arrayFilters = Array.isArray(options?.arrayFilters) ? + options.arrayFilters : []; + const updatedPathsByFilter = updatedPathsByArrayFilter(update); + let startIndex = 0; + + for (let i = 0; i < parts.length; ++i) { + const originalSubpath = parts.slice(startIndex, i + 1).join('.'); + const subpath = cleanPositionalOperators(originalSubpath); + schematype = schema.path(subpath); + if (schematype == null) { + continue; + } + + type = schema.pathType(subpath); + if ((schematype.$isSingleNested || schematype.$isMongooseDocumentArrayElement) && + schematype.schema.discriminators != null) { + const key = get(schematype, 'schema.options.discriminatorKey'); + const discriminatorValuePath = subpath + '.' + key; + const discriminatorFilterPath = + discriminatorValuePath.replace(/\.\d+\./, '.'); + let discriminatorKey = null; + + if (discriminatorValuePath in filter) { + discriminatorKey = filter[discriminatorValuePath]; + } + if (discriminatorFilterPath in filter) { + discriminatorKey = filter[discriminatorFilterPath]; + } + + const wrapperPath = subpath.replace(/\.\d+$/, ''); + if (schematype.$isMongooseDocumentArrayElement && + get(filter[wrapperPath], '$elemMatch.' + key) != null) { + discriminatorKey = filter[wrapperPath].$elemMatch[key]; + } + + const discriminatorKeyUpdatePath = originalSubpath + '.' + key; + if (discriminatorKeyUpdatePath in update) { + discriminatorKey = update[discriminatorKeyUpdatePath]; + } + + if (discriminatorValuePath in update) { + discriminatorKey = update[discriminatorValuePath]; + } + + for (const filterKey of Object.keys(updatedPathsByFilter)) { + const schemaKey = updatedPathsByFilter[filterKey] + '.' + key; + const arrayFilterKey = filterKey + '.' + key; + if (schemaKey === discriminatorFilterPath) { + const filter = arrayFilters.find(filter => Object.hasOwn(filter, arrayFilterKey)); + if (filter != null) { + discriminatorKey = filter[arrayFilterKey]; + } + } + } + + if (discriminatorKey == null) { + continue; + } + + const discriminator = getDiscriminatorByValue(schematype.Constructor.discriminators, discriminatorKey); + const discriminatorSchema = discriminator?.schema; + if (discriminatorSchema == null) { + continue; + } + + const rest = parts.slice(i + 1).join('.'); + schematype = discriminatorSchema._getSchema(rest); + schema = discriminatorSchema; + startIndex = i + 1; + if (schematype != null) { + type = discriminatorSchema._getPathType(rest); + break; + } + } + } + + return { type: type, schematype: schematype }; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/handleImmutable.js b/backend/node_modules/mongoose/lib/helpers/query/handleImmutable.js new file mode 100644 index 0000000..65d29aa --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/handleImmutable.js @@ -0,0 +1,44 @@ +'use strict'; + +const StrictModeError = require('../../error/strict'); + +/** + * Handle immutable option for a given path when casting updates based on options + * + * @param {SchemaType} schematype the resolved schematype for this path + * @param {boolean | 'throw' | null} strict whether strict mode is set for this query + * @param {object} obj the object containing the value being checked so we can delete + * @param {string} key the key in `obj` which we are checking for immutability + * @param {string} fullPath the full path being checked + * @param {object} options the query options + * @param {Query} ctx the query. Passed as `this` and first param to the `immutable` option, if `immutable` is a function + * @returns {boolean} true if field was removed, false otherwise + */ + +module.exports = function handleImmutable(schematype, strict, obj, key, fullPath, options, ctx) { + if (!schematype?.options?.immutable) { + return false; + } + let immutable = schematype.options.immutable; + + if (typeof immutable === 'function') { + immutable = immutable.call(ctx, ctx); + } + if (!immutable) { + return false; + } + + if (options?.overwriteImmutable) { + return false; + } + if (strict === false) { + return false; + } + if (strict === 'throw') { + throw new StrictModeError(null, + `Field ${fullPath} is immutable and strict = 'throw'`); + } + + delete obj[key]; + return true; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/handleReadPreferenceAliases.js b/backend/node_modules/mongoose/lib/helpers/query/handleReadPreferenceAliases.js new file mode 100644 index 0000000..c3af278 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/handleReadPreferenceAliases.js @@ -0,0 +1,23 @@ +'use strict'; + +module.exports = function handleReadPreferenceAliases(pref) { + switch (pref) { + case 'p': + pref = 'primary'; + break; + case 'pp': + pref = 'primaryPreferred'; + break; + case 's': + pref = 'secondary'; + break; + case 'sp': + pref = 'secondaryPreferred'; + break; + case 'n': + pref = 'nearest'; + break; + } + + return pref; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js b/backend/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js new file mode 100644 index 0000000..3e3b188 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/hasDollarKeys.js @@ -0,0 +1,23 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function hasDollarKeys(obj) { + + if (typeof obj !== 'object' || obj === null) { + return false; + } + + const keys = Object.keys(obj); + const len = keys.length; + + for (let i = 0; i < len; ++i) { + if (keys[i][0] === '$') { + return true; + } + } + + return false; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/isOperator.js b/backend/node_modules/mongoose/lib/helpers/query/isOperator.js new file mode 100644 index 0000000..0448859 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/isOperator.js @@ -0,0 +1,14 @@ +'use strict'; + +const specialKeys = new Set([ + '$ref', + '$id', + '$db' +]); + +module.exports = function isOperator(path) { + return ( + path[0] === '$' && + !specialKeys.has(path) + ); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/sanitizeFilter.js b/backend/node_modules/mongoose/lib/helpers/query/sanitizeFilter.js new file mode 100644 index 0000000..ac6e098 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/sanitizeFilter.js @@ -0,0 +1,41 @@ +'use strict'; + +const MongooseError = require('../../error/mongooseError'); +const hasDollarKeys = require('./hasDollarKeys'); +const { trustedSymbol } = require('./trusted'); + +module.exports = function sanitizeFilter(filter) { + if (filter == null || typeof filter !== 'object') { + return filter; + } + if (Array.isArray(filter)) { + for (const subfilter of filter) { + sanitizeFilter(subfilter); + } + return filter; + } + + const filterKeys = Object.keys(filter); + for (const key of filterKeys) { + const value = filter[key]; + if (value?.[trustedSymbol]) { + continue; + } + if (key === '$and' || key === '$or' || key === '$nor') { + sanitizeFilter(value); + continue; + } else if (key === '$jsonSchema' || key === '$where' || key === '$expr' || key === '$text') { + throw new MongooseError(key + ' is not allowed with sanitizeFilter'); + } + + if (hasDollarKeys(value)) { + const keys = Object.keys(value); + if (keys.length === 1 && keys[0] === '$eq') { + continue; + } + filter[key] = { $eq: filter[key] }; + } + } + + return filter; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/sanitizeProjection.js b/backend/node_modules/mongoose/lib/helpers/query/sanitizeProjection.js new file mode 100644 index 0000000..0c03487 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/sanitizeProjection.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = function sanitizeProjection(projection) { + if (projection == null) { + return; + } + + const keys = Object.keys(projection); + for (let i = 0; i < keys.length; ++i) { + if (typeof projection[keys[i]] === 'string') { + projection[keys[i]] = 1; + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js b/backend/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js new file mode 100644 index 0000000..dc96caf --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/selectPopulatedFields.js @@ -0,0 +1,62 @@ +'use strict'; + +const isExclusive = require('../projection/isExclusive'); +const isInclusive = require('../projection/isInclusive'); + +/*! + * ignore + */ + +module.exports = function selectPopulatedFields(fields, userProvidedFields, populateOptions) { + if (populateOptions == null) { + return; + } + + const paths = Object.keys(populateOptions); + userProvidedFields = userProvidedFields || {}; + if (isInclusive(fields)) { + for (const path of paths) { + if (!isPathInFields(userProvidedFields, path)) { + fields[path] = 1; + } else if (userProvidedFields[path] === 0) { + delete fields[path]; + } + + const refPath = populateOptions[path]?.refPath; + if (typeof refPath === 'string') { + if (!isPathInFields(userProvidedFields, refPath)) { + fields[refPath] = 1; + } else if (userProvidedFields[refPath] === 0) { + delete fields[refPath]; + } + } + } + } else if (isExclusive(fields)) { + for (const path of paths) { + if (userProvidedFields[path] == null) { + delete fields[path]; + } + const refPath = populateOptions[path]?.refPath; + if (typeof refPath === 'string' && userProvidedFields[refPath] == null) { + delete fields[refPath]; + } + } + } +}; + +/*! + * ignore + */ + +function isPathInFields(userProvidedFields, path) { + const pieces = path.split('.'); + const len = pieces.length; + let cur = pieces[0]; + for (let i = 1; i < len; ++i) { + if (userProvidedFields[cur] != null || userProvidedFields[cur + '.$'] != null) { + return true; + } + cur += '.' + pieces[i]; + } + return userProvidedFields[cur] != null || userProvidedFields[cur + '.$'] != null; +} diff --git a/backend/node_modules/mongoose/lib/helpers/query/trusted.js b/backend/node_modules/mongoose/lib/helpers/query/trusted.js new file mode 100644 index 0000000..87a2cfa --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/trusted.js @@ -0,0 +1,13 @@ +'use strict'; + +const trustedSymbol = Symbol('mongoose#trustedSymbol'); + +exports.trustedSymbol = trustedSymbol; + +exports.trusted = function trusted(obj) { + if (obj == null || (typeof obj !== 'object' && typeof obj !== 'function')) { + return obj; + } + obj[trustedSymbol] = true; + return obj; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/query/validOps.js b/backend/node_modules/mongoose/lib/helpers/query/validOps.js new file mode 100644 index 0000000..2d1aa47 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/query/validOps.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('../../constants').queryMiddlewareFunctions; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/addAutoId.js b/backend/node_modules/mongoose/lib/helpers/schema/addAutoId.js new file mode 100644 index 0000000..82c0c60 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/addAutoId.js @@ -0,0 +1,7 @@ +'use strict'; + +module.exports = function addAutoId(schema) { + const _obj = { _id: { auto: true } }; + _obj._id[schema.options.typeKey] = 'ObjectId'; + schema.add(_obj); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/applyBuiltinPlugins.js b/backend/node_modules/mongoose/lib/helpers/schema/applyBuiltinPlugins.js new file mode 100644 index 0000000..8bd7319 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/applyBuiltinPlugins.js @@ -0,0 +1,12 @@ +'use strict'; + +const builtinPlugins = require('../../plugins'); + +module.exports = function applyBuiltinPlugins(schema) { + for (const plugin of Object.values(builtinPlugins)) { + plugin(schema, { deduplicate: true }); + } + schema.plugins = Object.values(builtinPlugins). + map(fn => ({ fn, opts: { deduplicate: true } })). + concat(schema.plugins); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/applyPlugins.js b/backend/node_modules/mongoose/lib/helpers/schema/applyPlugins.js new file mode 100644 index 0000000..22b7224 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/applyPlugins.js @@ -0,0 +1,55 @@ +'use strict'; + +module.exports = function applyPlugins(schema, plugins, options, cacheKey) { + if (schema[cacheKey]) { + return; + } + schema[cacheKey] = true; + + if (!options?.skipTopLevel) { + let pluginTags = null; + for (const plugin of plugins) { + const tags = plugin[1] == null ? null : plugin[1].tags; + if (!Array.isArray(tags)) { + schema.plugin(plugin[0], plugin[1]); + continue; + } + + pluginTags = pluginTags || new Set(schema.options.pluginTags || []); + if (!tags.find(tag => pluginTags.has(tag))) { + continue; + } + schema.plugin(plugin[0], plugin[1]); + } + } + + options = Object.assign({}, options); + delete options.skipTopLevel; + + if (options.applyPluginsToChildSchemas !== false) { + for (const path of Object.keys(schema.paths)) { + const type = schema.paths[path]; + if (type.schema != null) { + applyPlugins(type.schema, plugins, options, cacheKey); + + // Recompile schema because plugins may have changed it, see gh-7572 + type.Constructor.prototype.$__setSchema(type.schema); + } + } + } + + const discriminators = schema.discriminators; + if (discriminators == null) { + return; + } + + const applyPluginsToDiscriminators = options.applyPluginsToDiscriminators; + + const keys = Object.keys(discriminators); + for (const discriminatorKey of keys) { + const discriminatorSchema = discriminators[discriminatorKey]; + + applyPlugins(discriminatorSchema, plugins, + { skipTopLevel: !applyPluginsToDiscriminators }, cacheKey); + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/applyReadConcern.js b/backend/node_modules/mongoose/lib/helpers/schema/applyReadConcern.js new file mode 100644 index 0000000..1367368 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/applyReadConcern.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = function applyReadConcern(schema, options) { + if (options.readConcern !== undefined) { + return; + } + + // Don't apply default read concern to operations in transactions, + // because you shouldn't set read concern on individual operations + // within a transaction. + // See: https://www.mongodb.com/docs/manual/reference/read-concern/ + if (options?.session?.transaction) { + return; + } + + const level = schema.options?.readConcern?.level; + if (level != null) { + options.readConcern = { level }; + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js b/backend/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js new file mode 100644 index 0000000..2b74137 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/applyWriteConcern.js @@ -0,0 +1,39 @@ +'use strict'; + +const utils = require('../../utils'); + +module.exports = function applyWriteConcern(schema, options) { + if (options.writeConcern != null) { + return; + } + // Don't apply default write concern to operations in transactions, + // because setting write concern on an operation in a transaction is an error + // See: https://www.mongodb.com/docs/manual/reference/write-concern/ + if (options?.session?.transaction) { + return; + } + const writeConcern = schema.options.writeConcern ?? {}; + if (utils.hasOwnKeys(writeConcern)) { + options.writeConcern = {}; + if (!('w' in options) && writeConcern.w != null) { + options.writeConcern.w = writeConcern.w; + } + if (!('j' in options) && writeConcern.j != null) { + options.writeConcern.j = writeConcern.j; + } + if (!('wtimeout' in options) && writeConcern.wtimeout != null) { + options.writeConcern.wtimeout = writeConcern.wtimeout; + } + } + else { + if (!('w' in options) && writeConcern.w != null) { + options.w = writeConcern.w; + } + if (!('j' in options) && writeConcern.j != null) { + options.j = writeConcern.j; + } + if (!('wtimeout' in options) && writeConcern.wtimeout != null) { + options.wtimeout = writeConcern.wtimeout; + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js b/backend/node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js new file mode 100644 index 0000000..f104d03 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/cleanPositionalOperators.js @@ -0,0 +1,12 @@ +'use strict'; + +/** + * For consistency's sake, we replace positional operator `$` and array filters + * `$[]` and `$[foo]` with `0` when looking up schema paths. + */ + +module.exports = function cleanPositionalOperators(path) { + return path. + replace(/\.\$(\[[^\]]*\])?(?=\.)/g, '.0'). + replace(/\.\$(\[[^\]]*\])?$/g, '.0'); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/getIndexes.js b/backend/node_modules/mongoose/lib/helpers/schema/getIndexes.js new file mode 100644 index 0000000..8c79744 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/getIndexes.js @@ -0,0 +1,171 @@ +'use strict'; + +const get = require('../get'); +const helperIsObject = require('../isObject'); +const decorateDiscriminatorIndexOptions = require('../indexes/decorateDiscriminatorIndexOptions'); + +/** + * Gather all indexes defined in the schema, including single nested, + * document arrays, and embedded discriminators. + * @param {Schema} schema + * @api private + */ + +module.exports = function getIndexes(schema) { + let indexes = []; + const schemaStack = new WeakMap(); + const indexTypes = schema.constructor.indexTypes; + const indexByName = new Map(); + + collectIndexes(schema); + return indexes; + + function collectIndexes(schema, prefix, baseSchema) { + // Ignore infinitely nested schemas, if we've already seen this schema + // along this path there must be a cycle + if (schemaStack.has(schema)) { + return; + } + schemaStack.set(schema, true); + + prefix = prefix || ''; + const keys = Object.keys(schema.paths); + + for (const key of keys) { + const path = schema.paths[key]; + if (baseSchema != null && baseSchema.paths[key]) { + // If looking at an embedded discriminator schema, don't look at paths + // that the + continue; + } + + if (path._duplicateKeyErrorMessage != null) { + schema._duplicateKeyErrorMessagesByPath = schema._duplicateKeyErrorMessagesByPath || {}; + schema._duplicateKeyErrorMessagesByPath[key] = path._duplicateKeyErrorMessage; + } + + if (path.$isMongooseDocumentArray || path.$isSingleNested) { + if (get(path, 'options.excludeIndexes') !== true && + get(path, 'schemaOptions.excludeIndexes') !== true && + get(path, 'schema.options.excludeIndexes') !== true) { + collectIndexes(path.schema, prefix + key + '.'); + } + + if (path.schema.discriminators != null) { + const discriminators = path.schema.discriminators; + const discriminatorKeys = Object.keys(discriminators); + for (const discriminatorKey of discriminatorKeys) { + collectIndexes(discriminators[discriminatorKey], + prefix + key + '.', path.schema); + } + } + + // Retained to minimize risk of backwards breaking changes due to + // gh-6113 + if (path.$isMongooseDocumentArray) { + continue; + } + } + + const index = path._index || (path.embeddedSchemaType && path.embeddedSchemaType._index); + + if (index !== false && index != null) { + const field = {}; + const isObject = helperIsObject(index); + const options = isObject ? { ...index } : {}; + const type = typeof index === 'string' ? index : + isObject ? index.type : + false; + + if (type && indexTypes.indexOf(type) !== -1) { + field[prefix + key] = type; + } else if (options.text) { + field[prefix + key] = 'text'; + delete options.text; + } else { + let isDescendingIndex = false; + if (index === 'descending' || index === 'desc') { + isDescendingIndex = true; + } else if (index === 'ascending' || index === 'asc') { + isDescendingIndex = false; + } else { + isDescendingIndex = Number(index) === -1; + } + + field[prefix + key] = isDescendingIndex ? -1 : 1; + } + + delete options.type; + if (schema.options.autoIndex != null) { + options._autoIndex = schema.options.autoIndex; + } + + const indexName = options?.name; + + if (typeof indexName === 'string') { + if (indexByName.has(indexName)) { + Object.assign(indexByName.get(indexName), field); + } else { + indexes.push([field, options]); + indexByName.set(indexName, field); + } + } else { + indexes.push([field, options]); + indexByName.set(indexName, field); + } + } + } + + schemaStack.delete(schema); + + if (prefix) { + fixSubIndexPaths(schema, prefix); + } else { + schema._indexes.forEach(function(index) { + const options = index[1]; + decorateDiscriminatorIndexOptions(schema, options); + }); + indexes = indexes.concat(schema._indexes); + } + } + + /** + * Checks for indexes added to subdocs using Schema.index(). + * These indexes need their paths prefixed properly. + * + * schema._indexes = [ [indexObj, options], [indexObj, options] ..] + * @param {Schema} schema + * @param {string} prefix + * @api private + */ + + function fixSubIndexPaths(schema, prefix) { + const subindexes = schema._indexes; + const len = subindexes.length; + for (let i = 0; i < len; ++i) { + const indexObj = subindexes[i][0]; + const indexOptions = subindexes[i][1]; + const keys = Object.keys(indexObj); + const klen = keys.length; + const newindex = {}; + + // use forward iteration, order matters + for (let j = 0; j < klen; ++j) { + const key = keys[j]; + newindex[prefix + key] = indexObj[key]; + } + + const newIndexOptions = Object.assign({}, indexOptions); + if (indexOptions?.partialFilterExpression != null) { + newIndexOptions.partialFilterExpression = {}; + const partialFilterExpression = indexOptions.partialFilterExpression; + for (const key of Object.keys(partialFilterExpression)) { + newIndexOptions.partialFilterExpression[prefix + key] = + partialFilterExpression[key]; + } + } + + indexes.push([newindex, newIndexOptions]); + } + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/getKeysInSchemaOrder.js b/backend/node_modules/mongoose/lib/helpers/schema/getKeysInSchemaOrder.js new file mode 100644 index 0000000..83b7fd8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/getKeysInSchemaOrder.js @@ -0,0 +1,28 @@ +'use strict'; + +const get = require('../get'); + +module.exports = function getKeysInSchemaOrder(schema, val, path) { + const schemaKeys = path != null ? Object.keys(get(schema.tree, path, {})) : Object.keys(schema.tree); + const valKeys = new Set(Object.keys(val)); + + let keys; + if (valKeys.size > 1) { + keys = new Set(); + for (const key of schemaKeys) { + if (valKeys.has(key)) { + keys.add(key); + } + } + for (const key of valKeys) { + if (!keys.has(key)) { + keys.add(key); + } + } + keys = Array.from(keys); + } else { + keys = Array.from(valKeys); + } + + return keys; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/getPath.js b/backend/node_modules/mongoose/lib/helpers/schema/getPath.js new file mode 100644 index 0000000..ca14520 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/getPath.js @@ -0,0 +1,43 @@ +'use strict'; + +const numberRE = /^\d+$/; + +/** + * Behaves like `Schema#path()`, except for it also digs into arrays without + * needing to put `.0.`, so `getPath(schema, 'docArr.elProp')` works. + * @api private + */ + +module.exports = function getPath(schema, path, discriminatorValueMap) { + let schematype = schema.path(path); + if (schematype != null) { + return schematype; + } + const pieces = path.split('.'); + let cur = ''; + let isArray = false; + + for (const piece of pieces) { + if (isArray && numberRE.test(piece)) { + continue; + } + cur = cur.length === 0 ? piece : cur + '.' + piece; + + schematype = schema.path(cur); + if (schematype?.schema) { + schema = schematype.schema; + if (!isArray && schematype.$isMongooseDocumentArray) { + isArray = true; + } + if (discriminatorValueMap && discriminatorValueMap[cur]) { + schema = schema.discriminators[discriminatorValueMap[cur]] ?? schema; + } + cur = ''; + } else if (schematype?.instance === 'Mixed') { + // If we found a mixed path, no point in digging further, the end result is always Mixed + break; + } + } + + return schematype; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/getSubdocumentStrictValue.js b/backend/node_modules/mongoose/lib/helpers/schema/getSubdocumentStrictValue.js new file mode 100644 index 0000000..ed5a4bd --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/getSubdocumentStrictValue.js @@ -0,0 +1,32 @@ +'use strict'; + +/** + * Find the `strict` mode setting for the deepest subdocument along a given path + * to ensure we have the correct default value for `strict`. When setting values + * underneath a subdocument, we should use the subdocument's `strict` setting by + * default, not the top-level document's. + * + * @param {Schema} schema + * @param {string[]} parts + * @returns {boolean | 'throw' | undefined} + */ + +module.exports = function getSubdocumentStrictValue(schema, parts) { + if (parts.length === 1) { + return undefined; + } + let cur = parts[0]; + let strict = undefined; + for (let i = 0; i < parts.length - 1; ++i) { + const curSchemaType = schema.path(cur); + if (curSchemaType?.schema) { + strict = curSchemaType.schema.options.strict; + schema = curSchemaType.schema; + cur = curSchemaType.$isMongooseDocumentArray && !isNaN(parts[i + 1]) ? '' : parts[i + 1]; + } else { + cur += cur.length ? ('.' + parts[i + 1]) : parts[i + 1]; + } + } + + return strict; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/handleIdOption.js b/backend/node_modules/mongoose/lib/helpers/schema/handleIdOption.js new file mode 100644 index 0000000..0e4264f --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/handleIdOption.js @@ -0,0 +1,20 @@ +'use strict'; + +const addAutoId = require('./addAutoId'); + +module.exports = function handleIdOption(schema, options) { + if (options?._id == null) { + return schema; + } + + schema = schema.clone(); + if (!options._id) { + schema.remove('_id'); + schema.options._id = false; + } else if (!schema.paths['_id']) { + addAutoId(schema); + schema.options._id = true; + } + + return schema; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js b/backend/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js new file mode 100644 index 0000000..0f9c30c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/handleTimestampOption.js @@ -0,0 +1,24 @@ +'use strict'; + +module.exports = handleTimestampOption; + +/*! + * ignore + */ + +function handleTimestampOption(arg, prop) { + if (arg == null) { + return null; + } + + if (typeof arg === 'boolean') { + return prop; + } + if (typeof arg[prop] === 'boolean') { + return arg[prop] ? prop : null; + } + if (!(prop in arg)) { + return prop; + } + return arg[prop]; +} diff --git a/backend/node_modules/mongoose/lib/helpers/schema/idGetter.js b/backend/node_modules/mongoose/lib/helpers/schema/idGetter.js new file mode 100644 index 0000000..7b98c5c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/idGetter.js @@ -0,0 +1,34 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function addIdGetter(schema) { + // ensure the documents receive an id getter unless disabled + const autoIdGetter = !schema.paths['id'] && + schema.paths['_id'] && + schema.options.id; + if (!autoIdGetter) { + return schema; + } + if (schema.aliases?.id) { + return schema; + } + schema.virtual('id').get(idGetter); + + return schema; +}; + +/** + * Returns this documents _id cast to a string. + * @api private + */ + +function idGetter() { + if (this._id != null) { + return this._id.toString(); + } + + return null; +} diff --git a/backend/node_modules/mongoose/lib/helpers/schema/merge.js b/backend/node_modules/mongoose/lib/helpers/schema/merge.js new file mode 100644 index 0000000..e7bb91f --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schema/merge.js @@ -0,0 +1,36 @@ +'use strict'; + +module.exports = function merge(s1, s2, skipConflictingPaths) { + const paths = Object.keys(s2.tree); + const pathsToAdd = {}; + for (const key of paths) { + if (skipConflictingPaths && (s1.paths[key] || s1.nested[key] || s1.singleNestedPaths[key])) { + continue; + } + pathsToAdd[key] = s2.tree[key]; + } + s1.options._isMerging = true; + s1.add(pathsToAdd, null); + delete s1.options._isMerging; + + s1.callQueue = s1.callQueue.concat(s2.callQueue); + s1.method(s2.methods); + s1.static(s2.statics); + + for (const [option, value] of Object.entries(s2._userProvidedOptions)) { + if (!(option in s1._userProvidedOptions)) { + s1.set(option, value); + } + } + + for (const query in s2.query) { + s1.query[query] = s2.query[query]; + } + + for (const virtual in s2.virtuals) { + s1.virtuals[virtual] = s2.virtuals[virtual].clone(); + } + + s1._indexes = s1._indexes.concat(s2._indexes || []); + s1.s.hooks.merge(s2.s.hooks, false); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/schematype/handleImmutable.js b/backend/node_modules/mongoose/lib/helpers/schematype/handleImmutable.js new file mode 100644 index 0000000..4bb5a93 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/schematype/handleImmutable.js @@ -0,0 +1,50 @@ +'use strict'; + +const StrictModeError = require('../../error/strict'); + +/*! + * ignore + */ + +module.exports = function(schematype) { + if (schematype.$immutable) { + schematype.$immutableSetter = createImmutableSetter(schematype.path, + schematype.options.immutable); + schematype.set(schematype.$immutableSetter); + } else if (schematype.$immutableSetter) { + schematype.setters = schematype.setters. + filter(fn => fn !== schematype.$immutableSetter); + delete schematype.$immutableSetter; + } +}; + +function createImmutableSetter(path, immutable) { + return function immutableSetter(v, _priorVal, _doc, options) { + if (this == null || this.$__ == null) { + return v; + } + if (this.isNew) { + return v; + } + if (options?.overwriteImmutable) { + return v; + } + + const _immutable = typeof immutable === 'function' ? + immutable.call(this, this) : + immutable; + if (!_immutable) { + return v; + } + + const _value = this.$__.priorDoc != null ? + this.$__.priorDoc.$__getValue(path) : + this.$__getValue(path); + if (this.$__.strictMode === 'throw' && v !== _value) { + throw new StrictModeError(path, 'Path `' + path + '` is immutable ' + + 'and strict mode is set to throw.', true); + } + + return _value; + }; +} diff --git a/backend/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js b/backend/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js new file mode 100644 index 0000000..e2ab6a0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/setDefaultsOnInsert.js @@ -0,0 +1,174 @@ +'use strict'; +const get = require('./get'); + +/** + * Applies defaults to update and findOneAndUpdate operations. + * + * @param {object} filter + * @param {Schema} schema + * @param {object} castedDoc + * @param {object} options + * @param {object} queryMongooseOptions + * @param {object | null} [context] the context to pass to default functions as `this` + * @method setDefaultsOnInsert + * @api private + */ + +module.exports = function(filter, schema, castedDoc, options, queryMongooseOptions, context) { + const shouldSetDefaultsOnInsert = options?.setDefaultsOnInsert ?? + queryMongooseOptions?.setDefaultsOnInsert ?? + schema.base.options.setDefaultsOnInsert; + + if (!options.upsert || shouldSetDefaultsOnInsert === false) { + return castedDoc; + } + + const keys = Object.keys(castedDoc || {}); + const updatedKeys = Object.create(null); + const updatedValues = {}; + const numKeys = keys.length; + + let hasDollarUpdate = false; + + for (let i = 0; i < numKeys; ++i) { + if (keys[i].charAt(0) === '$') { + hasDollarUpdate = true; + break; + } + } + + const paths = Object.keys(filter); + const numPaths = paths.length; + for (let i = 0; i < numPaths; ++i) { + const path = paths[i]; + const condition = filter[path]; + if (condition && typeof condition === 'object') { + const conditionKeys = Object.keys(condition); + const numConditionKeys = conditionKeys.length; + let hasDollarKey = false; + for (let j = 0; j < numConditionKeys; ++j) { + if (conditionKeys[j].charAt(0) === '$') { + hasDollarKey = true; + break; + } + } + if (hasDollarKey) { + continue; + } + } + updatedKeys[path] = true; + if (path.indexOf('.') === -1) { + continue; + } + // Also mark all parent prefixes so child-path lookups are O(1). + // e.g. 'extraProps.location' also marks 'extraProps' + const pieces = schema.paths[path] ? + // If the SchemaType already split for us, use that to avoid the extra overhead + schema.paths[path].splitPath() : + path.split('.'); + let cur = pieces[0]; + for (let j = 1; j < pieces.length; ++j) { + updatedKeys[cur] = true; + cur += '.' + pieces[j]; + } + } + + if (options?.overwrite && !hasDollarUpdate) { + // Defaults will be set later, since we're overwriting we'll cast + // the whole update to a document + return castedDoc; + } + + schema.eachPath(function(path, schemaType) { + // Skip single nested paths if underneath a map + if (schemaType.path === '_id' && schemaType.options.auto) { + return; + } + const def = schemaType.getDefault(null, true, { context }); + if (typeof def === 'undefined') { + return; + } + const pathPieces = schemaType.splitPath(); + if (pathPieces.includes('$*')) { + // Skip defaults underneath maps. We should never do `$setOnInsert` on a path with `$*` + return; + } + if (isModified(castedDoc, updatedKeys, path, pathPieces, hasDollarUpdate)) { + return; + } + + castedDoc = castedDoc || {}; + castedDoc.$setOnInsert = castedDoc.$setOnInsert || {}; + if (get(castedDoc, path) == null) { + castedDoc.$setOnInsert[path] = def; + } + updatedValues[path] = def; + }); + + return castedDoc; +}; + +function isModified(castedDoc, updatedKeys, path, pathPieces, hasDollarUpdate) { + // Check if path is in filter (updatedKeys) + if (updatedKeys[path]) { + return true; + } + + // Check if any parent path is in filter + let cur = pathPieces[0]; + for (let i = 1; i < pathPieces.length; ++i) { + if (updatedKeys[cur]) { + return true; + } + cur += '.' + pathPieces[i]; + } + + // Check if path is modified in the update + if (hasDollarUpdate) { + // Check each update operator + for (const key in castedDoc) { + if (key.charAt(0) === '$') { + if (pathExistsInUpdate(castedDoc[key], path, pathPieces)) { + return true; + } + } + } + } else { + // No dollar operators, check the castedDoc directly + if (pathExistsInUpdate(castedDoc, path, pathPieces)) { + return true; + } + } + + return false; +} + +function pathExistsInUpdate(update, targetPath, pathPieces) { + if (update == null || typeof update !== 'object') { + return false; + } + + // Check exact match + if (Object.hasOwn(update, targetPath)) { + return true; + } + + // Check if any parent path exists + let cur = pathPieces[0]; + for (let i = 1; i < pathPieces.length; ++i) { + if (Object.hasOwn(update, cur)) { + return true; + } + cur += '.' + pathPieces[i]; + } + + // Check if any child path exists (e.g., path is "a" and update has "a.b") + const prefix = targetPath + '.'; + for (const key in update) { + if (key.startsWith(prefix)) { + return true; + } + } + + return false; +} diff --git a/backend/node_modules/mongoose/lib/helpers/specialProperties.js b/backend/node_modules/mongoose/lib/helpers/specialProperties.js new file mode 100644 index 0000000..8a961e4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/specialProperties.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = new Set(['__proto__', 'constructor', 'prototype']); diff --git a/backend/node_modules/mongoose/lib/helpers/symbols.js b/backend/node_modules/mongoose/lib/helpers/symbols.js new file mode 100644 index 0000000..a9af890 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/symbols.js @@ -0,0 +1,20 @@ +'use strict'; + +exports.arrayAtomicsBackupSymbol = Symbol('mongoose#Array#atomicsBackup'); +exports.arrayAtomicsSymbol = Symbol('mongoose#Array#_atomics'); +exports.arrayParentSymbol = Symbol('mongoose#Array#_parent'); +exports.arrayPathSymbol = Symbol('mongoose#Array#_path'); +exports.arraySchemaSymbol = Symbol('mongoose#Array#_schema'); +exports.documentArrayParent = Symbol('mongoose#documentArrayParent'); +exports.documentIsSelected = Symbol('mongoose#Document#isSelected'); +exports.documentIsModified = Symbol('mongoose#Document#isModified'); +exports.documentModifiedPaths = Symbol('mongoose#Document#modifiedPaths'); +exports.documentSchemaSymbol = Symbol('mongoose#Document#schema'); +exports.getSymbol = Symbol('mongoose#Document#get'); +exports.modelSymbol = Symbol('mongoose#Model'); +exports.objectIdSymbol = Symbol('mongoose#ObjectId'); +exports.populateModelSymbol = Symbol('mongoose#PopulateOptions#Model'); +exports.schemaTypeSymbol = Symbol('mongoose#schemaType'); +exports.sessionNewDocuments = Symbol('mongoose#ClientSession#newDocuments'); +exports.scopeSymbol = Symbol('mongoose#Document#scope'); +exports.validatorErrorSymbol = Symbol('mongoose#validatorError'); diff --git a/backend/node_modules/mongoose/lib/helpers/timers.js b/backend/node_modules/mongoose/lib/helpers/timers.js new file mode 100644 index 0000000..eb7e645 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/timers.js @@ -0,0 +1,3 @@ +'use strict'; + +exports.setTimeout = setTimeout; diff --git a/backend/node_modules/mongoose/lib/helpers/timestamps/setDocumentTimestamps.js b/backend/node_modules/mongoose/lib/helpers/timestamps/setDocumentTimestamps.js new file mode 100644 index 0000000..c4af1f0 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/timestamps/setDocumentTimestamps.js @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = function setDocumentTimestamps(doc, timestampOption, currentTime, createdAt, updatedAt) { + const skipUpdatedAt = timestampOption?.updatedAt === false; + const skipCreatedAt = timestampOption?.createdAt === false; + + const defaultTimestamp = currentTime != null ? + currentTime() : + doc.ownerDocument().constructor.base.now(); + + if (!skipCreatedAt && + (doc.isNew || doc.$isSubdocument) && + createdAt && + !doc.$__getValue(createdAt) && + doc.$__isSelected(createdAt)) { + doc.$set(createdAt, defaultTimestamp, undefined, { overwriteImmutable: true }); + } + + if (!skipUpdatedAt && updatedAt && (doc.isNew || doc.$isModified())) { + let ts = defaultTimestamp; + if (doc.isNew && createdAt != null) { + ts = doc.$__getValue(createdAt); + } + doc.$set(updatedAt, ts); + } +}; diff --git a/backend/node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js b/backend/node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js new file mode 100644 index 0000000..721f417 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/timestamps/setupTimestamps.js @@ -0,0 +1,132 @@ +'use strict'; + +const applyTimestampsToChildren = require('../update/applyTimestampsToChildren'); +const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate'); +const handleTimestampOption = require('../schema/handleTimestampOption'); +const setDocumentTimestamps = require('./setDocumentTimestamps'); +const symbols = require('../../schema/symbols'); + +const replaceOps = new Set([ + 'replaceOne', + 'findOneAndReplace' +]); + +module.exports = function setupTimestamps(schema, timestamps) { + const childHasTimestamp = schema.childSchemas.find(withTimestamp); + function withTimestamp(s) { + const ts = s.schema.options.timestamps; + return !!ts; + } + if (!timestamps && !childHasTimestamp) { + return; + } + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ? + timestamps.currentTime : + null; + const schemaAdditions = {}; + + schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt }; + + if (createdAt && !schema.paths[createdAt]) { + const baseImmutableCreatedAt = schema.base?.get('timestamps.createdAt.immutable') ?? null; + const immutable = baseImmutableCreatedAt ?? true; + schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable }; + } + + if (updatedAt && !schema.paths[updatedAt]) { + schemaAdditions[updatedAt] = Date; + } + + schema.add(schemaAdditions); + + schema.pre('save', timestampsPreSave); + + schema.methods.initializeTimestamps = function(timestampsOptions) { + if (timestampsOptions === false) { + return this; + } + const ts = currentTime != null ? + currentTime() : this.constructor.base.now(); + + const initTimestampsCreatedAt = timestampsOptions != null ? + handleTimestampOption(timestampsOptions, 'createdAt') : + createdAt; + const initTimestampsUpdatedAt = timestampsOptions != null ? + handleTimestampOption(timestampsOptions, 'updatedAt') : + updatedAt; + + if (initTimestampsCreatedAt && !this.get(initTimestampsCreatedAt)) { + this.$set(createdAt, ts); + } + if (initTimestampsUpdatedAt && !this.get(initTimestampsUpdatedAt)) { + this.$set(updatedAt, ts); + } + if (this.$isSubdocument) { + return this; + } + + const subdocs = this.$getAllSubdocs(); + for (const subdoc of subdocs) { + if (subdoc.initializeTimestamps) { + subdoc.initializeTimestamps(timestampsOptions); + } + } + + return this; + }; + + const opts = { query: true, model: false }; + schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate); + schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate); + schema.pre('replaceOne', opts, _setTimestampsOnUpdate); + schema.pre('update', opts, _setTimestampsOnUpdate); + schema.pre('updateOne', opts, _setTimestampsOnUpdate); + schema.pre('updateMany', opts, _setTimestampsOnUpdate); +}; + +function timestampsPreSave() { + const timestampOption = this.$__?.saveOptions?.timestamps; + if (timestampOption === false) { + return; + } + + const schema = this.$__schema; + const { createdAt, updatedAt } = schema.$timestamps; + const timestamps = schema.options.timestamps; + const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ? + timestamps.currentTime : + null; + + setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt); +} + +function _setTimestampsOnUpdate() { + const schema = this.model.schema; + const { createdAt, updatedAt } = schema.$timestamps; + const timestamps = schema.options.timestamps; + const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ? + timestamps.currentTime : + null; + + const now = currentTime != null ? + currentTime() : + this.model.base.now(); + // Replacing with null update should still trigger timestamps + if (replaceOps.has(this.op) && this.getUpdate() == null) { + this.setUpdate({}); + } + applyTimestampsToUpdate( + now, + createdAt, + updatedAt, + this.getUpdate(), + this._mongooseOptions, + replaceOps.has(this.op) + ); + applyTimestampsToChildren(now, this.getUpdate(), this.model.schema); +} + +timestampsPreSave[symbols.builtInMiddleware] = true; +_setTimestampsOnUpdate[symbols.builtInMiddleware] = true; diff --git a/backend/node_modules/mongoose/lib/helpers/topology/allServersUnknown.js b/backend/node_modules/mongoose/lib/helpers/topology/allServersUnknown.js new file mode 100644 index 0000000..45c40ba --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/topology/allServersUnknown.js @@ -0,0 +1,12 @@ +'use strict'; + +const getConstructorName = require('../getConstructorName'); + +module.exports = function allServersUnknown(topologyDescription) { + if (getConstructorName(topologyDescription) !== 'TopologyDescription') { + return false; + } + + const servers = Array.from(topologyDescription.servers.values()); + return servers.length > 0 && servers.every(server => server.type === 'Unknown'); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/topology/isAtlas.js b/backend/node_modules/mongoose/lib/helpers/topology/isAtlas.js new file mode 100644 index 0000000..445a8b4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/topology/isAtlas.js @@ -0,0 +1,31 @@ +'use strict'; + +const getConstructorName = require('../getConstructorName'); + +/** + * @typedef { import('mongodb').TopologyDescription } TopologyDescription + */ + +/** + * Checks if topologyDescription contains servers connected to an atlas instance + * + * @param {TopologyDescription} topologyDescription + * @returns {boolean} + */ +module.exports = function isAtlas(topologyDescription) { + if (getConstructorName(topologyDescription) !== 'TopologyDescription') { + return false; + } + + if (topologyDescription.servers.size === 0) { + return false; + } + + for (const server of topologyDescription.servers.values()) { + if (server.host.endsWith('.mongodb.net') === false || server.port !== 27017) { + return false; + } + } + + return true; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/topology/isSSLError.js b/backend/node_modules/mongoose/lib/helpers/topology/isSSLError.js new file mode 100644 index 0000000..17c1c50 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/topology/isSSLError.js @@ -0,0 +1,16 @@ +'use strict'; + +const getConstructorName = require('../getConstructorName'); + +const nonSSLMessage = 'Client network socket disconnected before secure TLS ' + + 'connection was established'; + +module.exports = function isSSLError(topologyDescription) { + if (getConstructorName(topologyDescription) !== 'TopologyDescription') { + return false; + } + + const descriptions = Array.from(topologyDescription.servers.values()); + return descriptions.length > 0 && + descriptions.every(descr => descr.error && descr.error.message.indexOf(nonSSLMessage) !== -1); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js b/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js new file mode 100644 index 0000000..5b8e7de --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js @@ -0,0 +1,193 @@ +'use strict'; + +const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); +const handleTimestampOption = require('../schema/handleTimestampOption'); + +module.exports = applyTimestampsToChildren; + +/*! + * ignore + */ + +function applyTimestampsToChildren(now, update, schema) { + if (update == null) { + return; + } + + const keys = Object.keys(update); + const hasDollarKey = keys.some(key => key[0] === '$'); + + if (hasDollarKey) { + if (update.$push) { + _applyTimestampToUpdateOperator(update.$push); + } + if (update.$addToSet) { + _applyTimestampToUpdateOperator(update.$addToSet); + } + if (update.$set != null) { + const keys = Object.keys(update.$set); + for (const key of keys) { + applyTimestampsToUpdateKey(schema, key, update.$set, now); + } + } + if (update.$setOnInsert != null) { + const keys = Object.keys(update.$setOnInsert); + for (const key of keys) { + applyTimestampsToUpdateKey(schema, key, update.$setOnInsert, now); + } + } + } + + const updateKeys = Object.keys(update).filter(key => key[0] !== '$'); + for (const key of updateKeys) { + applyTimestampsToUpdateKey(schema, key, update, now); + } + + function _applyTimestampToUpdateOperator(op) { + for (const key of Object.keys(op)) { + const $path = schema.path(key.replace(/\.\$\./i, '.').replace(/.\$$/, '')); + if (op[key] && + $path && + $path.$isMongooseDocumentArray && + $path.schema.options.timestamps) { + const timestamps = $path.schema.options.timestamps; + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + if (op[key].$each) { + op[key].$each.forEach(function(subdoc) { + if (updatedAt != null) { + subdoc[updatedAt] = now; + } + if (createdAt != null) { + subdoc[createdAt] = now; + } + + applyTimestampsToChildren(now, subdoc, $path.schema); + }); + } else { + if (updatedAt != null) { + op[key][updatedAt] = now; + } + if (createdAt != null) { + op[key][createdAt] = now; + } + + applyTimestampsToChildren(now, op[key], $path.schema); + } + } + } + } +} + +function applyTimestampsToDocumentArray(arr, schematype, now) { + const timestamps = schematype.schema.options.timestamps; + + const len = arr.length; + + if (!timestamps) { + for (let i = 0; i < len; ++i) { + applyTimestampsToChildren(now, arr[i], schematype.schema); + } + return; + } + + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + for (let i = 0; i < len; ++i) { + if (updatedAt != null) { + arr[i][updatedAt] = now; + } + if (createdAt != null) { + arr[i][createdAt] = now; + } + + applyTimestampsToChildren(now, arr[i], schematype.schema); + } +} + +function applyTimestampsToSingleNested(subdoc, schematype, now) { + const timestamps = schematype.schema.options.timestamps; + if (!timestamps) { + applyTimestampsToChildren(now, subdoc, schematype.schema); + return; + } + + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + if (updatedAt != null) { + subdoc[updatedAt] = now; + } + if (createdAt != null) { + subdoc[createdAt] = now; + } + + applyTimestampsToChildren(now, subdoc, schematype.schema); +} + +function applyTimestampsToUpdateKey(schema, key, update, now) { + // Replace positional operator `$` and array filters `$[]` and `$[.*]` + const keyToSearch = cleanPositionalOperators(key); + const path = schema.path(keyToSearch); + if (!path) { + return; + } + + const parentSchemaTypes = []; + const pieces = keyToSearch.split('.'); + for (let i = pieces.length - 1; i > 0; --i) { + const s = schema.path(pieces.slice(0, i).join('.')); + if (s != null && + (s.$isMongooseDocumentArray || s.$isSingleNested)) { + parentSchemaTypes.push({ parentPath: key.split('.').slice(0, i).join('.'), parentSchemaType: s }); + } + } + + if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) { + applyTimestampsToDocumentArray(update[key], path, now); + } else if (update[key] && path.$isSingleNested) { + applyTimestampsToSingleNested(update[key], path, now); + } else if (parentSchemaTypes.length > 0) { + for (const item of parentSchemaTypes) { + const parentPath = item.parentPath; + const parentSchemaType = item.parentSchemaType; + const timestamps = parentSchemaType.schema.options.timestamps; + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + + if (!timestamps || updatedAt == null) { + continue; + } + + if (parentSchemaType.$isSingleNested) { + // Single nested is easy + update[parentPath + '.' + updatedAt] = now; + } else if (parentSchemaType.$isMongooseDocumentArray) { + let childPath = key.substring(parentPath.length + 1); + + if (/^\d+$/.test(childPath)) { + update[parentPath + '.' + childPath][updatedAt] = now; + continue; + } + + const firstDot = childPath.indexOf('.'); + childPath = firstDot !== -1 ? childPath.substring(0, firstDot) : childPath; + + update[parentPath + '.' + childPath + '.' + updatedAt] = now; + } + } + } else if (path.schema != null && path.schema != schema && update[key]) { + const timestamps = path.schema.options.timestamps; + const createdAt = handleTimestampOption(timestamps, 'createdAt'); + const updatedAt = handleTimestampOption(timestamps, 'updatedAt'); + + if (!timestamps) { + return; + } + + if (updatedAt != null) { + update[key][updatedAt] = now; + } + if (createdAt != null) { + update[key][createdAt] = now; + } + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js b/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js new file mode 100644 index 0000000..28d2f72 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js @@ -0,0 +1,131 @@ +'use strict'; + +/*! + * ignore + */ + +const get = require('../get'); +const utils = require('../../utils'); + +module.exports = applyTimestampsToUpdate; + +/*! + * ignore + */ + +function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options, isReplace) { + const updates = currentUpdate; + let _updates = updates; + const timestamps = get(options, 'timestamps', true); + + // Support skipping timestamps at the query level, see gh-6980 + if (!timestamps || updates == null) { + return currentUpdate; + } + + const skipCreatedAt = timestamps?.createdAt === false; + const skipUpdatedAt = timestamps?.updatedAt === false; + + if (isReplace) { + if (currentUpdate?.$set) { + currentUpdate = currentUpdate.$set; + updates.$set = {}; + _updates = updates.$set; + } + if (!skipUpdatedAt && updatedAt && !currentUpdate[updatedAt]) { + _updates[updatedAt] = now; + } + if (!skipCreatedAt && createdAt && !currentUpdate[createdAt]) { + _updates[createdAt] = now; + } + return updates; + } + currentUpdate = currentUpdate || {}; + + if (Array.isArray(updates)) { + // Update with aggregation pipeline + if (updatedAt == null) { + return updates; + } + updates.push({ $set: { [updatedAt]: now } }); + return updates; + } + updates.$set = updates.$set || {}; + if (!skipUpdatedAt && updatedAt && + !currentUpdate.$currentDate?.[updatedAt]) { + let timestampSet = false; + if (updatedAt.indexOf('.') !== -1) { + const pieces = updatedAt.split('.'); + for (let i = 1; i < pieces.length; ++i) { + const remnant = pieces.slice(-i).join('.'); + const start = pieces.slice(0, -i).join('.'); + if (currentUpdate[start] != null) { + currentUpdate[start][remnant] = now; + timestampSet = true; + break; + } else if (currentUpdate.$set?.[start]) { + currentUpdate.$set[start][remnant] = now; + timestampSet = true; + break; + } + } + } + + if (!timestampSet) { + updates.$set[updatedAt] = now; + } + + if (Object.hasOwn(updates, updatedAt)) { + delete updates[updatedAt]; + } + } + + if (!skipCreatedAt && createdAt) { + const overwriteImmutable = get(options, 'overwriteImmutable', false); + const hasUserCreatedAt = currentUpdate[createdAt] != null || currentUpdate.$set?.[createdAt] != null; + + // If overwriteImmutable is true and user provided createdAt, keep their value + if (overwriteImmutable && hasUserCreatedAt) { + // Move createdAt from top-level to $set if needed + if (currentUpdate[createdAt] != null) { + updates.$set[createdAt] = currentUpdate[createdAt]; + delete currentUpdate[createdAt]; + } + // User's value is already in $set, nothing more to do + } else { + if (currentUpdate[createdAt]) { + delete currentUpdate[createdAt]; + } + if (currentUpdate.$set?.[createdAt]) { + delete currentUpdate.$set[createdAt]; + } + let timestampSet = false; + if (createdAt.indexOf('.') !== -1) { + const pieces = createdAt.split('.'); + for (let i = 1; i < pieces.length; ++i) { + const remnant = pieces.slice(-i).join('.'); + const start = pieces.slice(0, -i).join('.'); + if (currentUpdate[start] != null) { + currentUpdate[start][remnant] = now; + timestampSet = true; + break; + } else if (currentUpdate.$set?.[start]) { + currentUpdate.$set[start][remnant] = now; + timestampSet = true; + break; + } + } + } + + if (!timestampSet) { + updates.$setOnInsert = updates.$setOnInsert || {}; + updates.$setOnInsert[createdAt] = now; + } + } + } + + if (utils.hasOwnKeys(updates.$set) === false) { + delete updates.$set; + } + return updates; +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/castArrayFilters.js b/backend/node_modules/mongoose/lib/helpers/update/castArrayFilters.js new file mode 100644 index 0000000..381efe7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/castArrayFilters.js @@ -0,0 +1,113 @@ +'use strict'; + +const castFilterPath = require('../query/castFilterPath'); +const cleanPositionalOperators = require('../schema/cleanPositionalOperators'); +const getPath = require('../schema/getPath'); +const updatedPathsByArrayFilter = require('./updatedPathsByArrayFilter'); +const utils = require('../../utils'); + +module.exports = function castArrayFilters(query) { + const arrayFilters = query.options.arrayFilters; + if (!Array.isArray(arrayFilters)) { + return; + } + const update = query.getUpdate(); + const schema = query.schema; + const updatedPathsByFilter = updatedPathsByArrayFilter(update); + + let strictQuery = schema.options.strict; + if (query._mongooseOptions.strict != null) { + strictQuery = query._mongooseOptions.strict; + } + if (query.model?.base.options.strictQuery != null) { + strictQuery = query.model.base.options.strictQuery; + } + if (schema._userProvidedOptions.strictQuery != null) { + strictQuery = schema._userProvidedOptions.strictQuery; + } + if (query._mongooseOptions.strictQuery != null) { + strictQuery = query._mongooseOptions.strictQuery; + } + + _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query); +}; + +function _castArrayFilters(arrayFilters, schema, strictQuery, updatedPathsByFilter, query) { + // Map to store discriminator values for embedded documents in the array filters. + // This is used to handle cases where array filters target specific embedded document types. + const discriminatorValueMap = {}; + + for (const filter of arrayFilters) { + if (filter == null) { + throw new Error(`Got null array filter in ${arrayFilters}`); + } + const keys = Object.keys(filter).filter(key => filter[key] != null); + if (keys.length === 0) { + continue; + } + + const firstKey = keys[0]; + if (firstKey === '$and' || firstKey === '$or') { + for (const key of keys) { + _castArrayFilters(filter[key], schema, strictQuery, updatedPathsByFilter, query); + } + continue; + } + const dot = firstKey.indexOf('.'); + const filterWildcardPath = dot === -1 ? firstKey : firstKey.substring(0, dot); + if (updatedPathsByFilter[filterWildcardPath] == null) { + continue; + } + const baseFilterPath = cleanPositionalOperators( + updatedPathsByFilter[filterWildcardPath] + ); + + const baseSchematype = getPath(schema, baseFilterPath, discriminatorValueMap); + let filterBaseSchema = baseSchematype != null ? baseSchematype.schema : null; + if (filterBaseSchema?.discriminators != null && + filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]) { + filterBaseSchema = filterBaseSchema.discriminators[filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]] || filterBaseSchema; + discriminatorValueMap[baseFilterPath] = filter[filterWildcardPath + '.' + filterBaseSchema.options.discriminatorKey]; + } + + for (const key of keys) { + if (updatedPathsByFilter[key] === null) { + continue; + } + if (utils.hasOwnKeys(updatedPathsByFilter) === false) { + continue; + } + const dot = key.indexOf('.'); + + let filterPathRelativeToBase = dot === -1 ? null : key.substring(dot); + let schematype; + if (filterPathRelativeToBase == null || filterBaseSchema == null) { + schematype = baseSchematype; + } else { + // If there are multiple array filters in the path being updated, make sure + // to replace them so we can get the schema path. + filterPathRelativeToBase = cleanPositionalOperators(filterPathRelativeToBase); + schematype = getPath(filterBaseSchema, filterPathRelativeToBase, discriminatorValueMap); + } + + if (schematype == null) { + if (!strictQuery) { + return; + } + const filterPath = filterPathRelativeToBase == null ? + baseFilterPath + '.0' : + baseFilterPath + '.0' + filterPathRelativeToBase; + // For now, treat `strictQuery = true` and `strictQuery = 'throw'` as + // equivalent for casting array filters. `strictQuery = true` doesn't + // quite work in this context because we never want to silently strip out + // array filters, even if the path isn't in the schema. + throw new Error(`Could not find path "${filterPath}" in schema`); + } + if (typeof filter[key] === 'object') { + filter[key] = castFilterPath(query, schematype, filter[key]); + } else { + filter[key] = schematype.castForQuery(null, filter[key]); + } + } + } +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/decorateUpdateWithVersionKey.js b/backend/node_modules/mongoose/lib/helpers/update/decorateUpdateWithVersionKey.js new file mode 100644 index 0000000..266996c --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/decorateUpdateWithVersionKey.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * Decorate the update with a version key, if necessary + * @api private + */ + +module.exports = function decorateUpdateWithVersionKey(update, options, versionKey) { + if (!versionKey || !options?.upsert) { + return; + } + + if (options.overwrite) { + if (!hasKey(update, versionKey)) { + update[versionKey] = 0; + } + } else if ( + !hasKey(update, versionKey) && + !hasKey(update?.$set, versionKey) && + !hasKey(update?.$inc, versionKey) && + !hasKey(update?.$setOnInsert, versionKey) + ) { + if (!update.$setOnInsert) { + update.$setOnInsert = {}; + } + update.$setOnInsert[versionKey] = 0; + } +}; + +function hasKey(obj, key) { + if (obj == null || typeof obj !== 'object') { + return false; + } + return Object.hasOwn(obj, key); +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/modifiedPaths.js b/backend/node_modules/mongoose/lib/helpers/update/modifiedPaths.js new file mode 100644 index 0000000..916456d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/modifiedPaths.js @@ -0,0 +1,33 @@ +'use strict'; + +const _modifiedPaths = require('../common').modifiedPaths; + +/** + * Given an update document with potential update operators (`$set`, etc.) + * returns an object whose keys are the directly modified paths. + * + * If there are any top-level keys that don't start with `$`, we assume those + * will get wrapped in a `$set`. The Mongoose Query is responsible for wrapping + * top-level keys in `$set`. + * + * @param {object} update + * @return {object} modified + */ + +module.exports = function modifiedPaths(update) { + const keys = Object.keys(update); + const res = {}; + + const withoutDollarKeys = {}; + for (const key of keys) { + if (key.startsWith('$')) { + _modifiedPaths(update[key], '', res); + continue; + } + withoutDollarKeys[key] = update[key]; + } + + _modifiedPaths(withoutDollarKeys, '', res); + + return res; +}; diff --git a/backend/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js b/backend/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js new file mode 100644 index 0000000..81a62a8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js @@ -0,0 +1,53 @@ +'use strict'; + +const get = require('../get'); + +/** + * Given an update, move all $set on immutable properties to $setOnInsert. + * This should only be called for upserts, because $setOnInsert bypasses the + * strictness check for immutable properties. + */ + +module.exports = function moveImmutableProperties(schema, update, ctx) { + if (update == null) { + return; + } + + const keys = Object.keys(update); + for (const key of keys) { + const isDollarKey = key.startsWith('$'); + + if (key === '$set') { + const updatedPaths = Object.keys(update[key]); + for (const path of updatedPaths) { + _walkUpdatePath(schema, update[key], path, update, ctx); + } + } else if (!isDollarKey) { + _walkUpdatePath(schema, update, key, update, ctx); + } + + } +}; + +function _walkUpdatePath(schema, op, path, update, ctx) { + const schematype = schema.path(path); + if (schematype == null) { + return; + } + + let immutable = get(schematype, 'options.immutable', null); + if (immutable == null) { + return; + } + if (typeof immutable === 'function') { + immutable = immutable.call(ctx, ctx); + } + + if (!immutable) { + return; + } + + update.$setOnInsert = update.$setOnInsert || {}; + update.$setOnInsert[path] = op[path]; + delete op[path]; +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js b/backend/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js new file mode 100644 index 0000000..eaa9a3d --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js @@ -0,0 +1,37 @@ +'use strict'; + +/** + * MongoDB throws an error if there's unused array filters. That is, if `options.arrayFilters` defines + * a filter, but none of the `update` keys use it. This should be enough to filter out all unused array + * filters. + */ + +module.exports = function removeUnusedArrayFilters(update, options) { + const arrayFilters = options?.arrayFilters; + if (!Array.isArray(arrayFilters)) { + return; + } + + const updateKeys = Object.keys(update). + map(key => Object.keys(update[key])). + reduce((cur, arr) => cur.concat(arr), []); + options.arrayFilters = options.arrayFilters.filter(obj => { + return _checkSingleFilterKey(obj, updateKeys); + }); +}; + +function _checkSingleFilterKey(arrayFilter, updateKeys) { + const firstKey = Object.keys(arrayFilter)[0]; + + if (firstKey === '$and' || firstKey === '$or') { + if (!Array.isArray(arrayFilter[firstKey])) { + return false; + } + return arrayFilter[firstKey].find(filter => _checkSingleFilterKey(filter, updateKeys)) != null; + } + + const firstDot = firstKey.indexOf('.'); + const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot); + + return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null; +} diff --git a/backend/node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js b/backend/node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js new file mode 100644 index 0000000..fe7d3b6 --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/update/updatedPathsByArrayFilter.js @@ -0,0 +1,27 @@ +'use strict'; + +const modifiedPaths = require('./modifiedPaths'); + +module.exports = function updatedPathsByArrayFilter(update) { + if (update == null) { + return {}; + } + const updatedPaths = modifiedPaths(update); + + return Object.keys(updatedPaths).reduce((cur, path) => { + const matches = path.match(/\$\[[^\]]+\]/g); + if (matches == null) { + return cur; + } + for (const match of matches) { + const firstMatch = path.indexOf(match); + if (firstMatch !== path.lastIndexOf(match)) { + throw new Error(`Path '${path}' contains the same array filter multiple times`); + } + cur[match.substring(2, match.length - 1)] = path. + substring(0, firstMatch - 1). + replace(/\$\[[^\]]+\]/g, '0'); + } + return cur; + }, {}); +}; diff --git a/backend/node_modules/mongoose/lib/helpers/updateValidators.js b/backend/node_modules/mongoose/lib/helpers/updateValidators.js new file mode 100644 index 0000000..e87d9ca --- /dev/null +++ b/backend/node_modules/mongoose/lib/helpers/updateValidators.js @@ -0,0 +1,186 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const ValidationError = require('../error/validation'); +const cleanPositionalOperators = require('./schema/cleanPositionalOperators'); +const flatten = require('./common').flatten; + +/** + * Applies validators and defaults to update and findOneAndUpdate operations, + * specifically passing a null doc as `this` to validators and defaults + * + * @param {Query} query + * @param {Schema} schema + * @param {object} castedDoc + * @param {object} options + * @method runValidatorsOnUpdate + * @api private + */ + +module.exports = async function updateValidators(query, schema, castedDoc, options) { + const keys = Object.keys(castedDoc || {}); + let updatedKeys = {}; + let updatedValues = {}; + const isPull = {}; + const arrayAtomicUpdates = {}; + const numKeys = keys.length; + let hasDollarUpdate = false; + let currentUpdate; + let key; + + for (let i = 0; i < numKeys; ++i) { + if (keys[i].startsWith('$')) { + hasDollarUpdate = true; + if (keys[i] === '$push' || keys[i] === '$addToSet') { + const _keys = Object.keys(castedDoc[keys[i]]); + for (let ii = 0; ii < _keys.length; ++ii) { + currentUpdate = castedDoc[keys[i]][_keys[ii]]; + if (currentUpdate?.$each) { + arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []). + concat(currentUpdate.$each); + } else { + arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []). + concat([currentUpdate]); + } + } + continue; + } + const flat = flatten(castedDoc[keys[i]], null, null, schema); + const paths = Object.keys(flat); + const numPaths = paths.length; + for (let j = 0; j < numPaths; ++j) { + const updatedPath = cleanPositionalOperators(paths[j]); + key = keys[i]; + // With `$pull` we might flatten `$in`. Skip stuff nested under `$in` + // for the rest of the logic, it will get handled later. + if (updatedPath.includes('$')) { + continue; + } + if (key === '$set' || key === '$setOnInsert' || + key === '$pull' || key === '$pullAll') { + updatedValues[updatedPath] = flat[paths[j]]; + isPull[updatedPath] = key === '$pull' || key === '$pullAll'; + } else if (key === '$unset') { + updatedValues[updatedPath] = undefined; + } + updatedKeys[updatedPath] = true; + } + } + } + + if (!hasDollarUpdate) { + updatedValues = flatten(castedDoc, null, null, schema); + updatedKeys = Object.keys(updatedValues); + } + + const updates = Object.keys(updatedValues); + const numUpdates = updates.length; + const validatorsToExecute = []; + const validationErrors = []; + + const alreadyValidated = []; + + const context = query; + for (let i = 0; i < numUpdates; ++i) { + const v = updatedValues[updates[i]]; + const schemaPath = schema._getSchema(updates[i]); + if (schemaPath == null) { + continue; + } + if (schemaPath.instance === 'Mixed' && schemaPath.path !== updates[i]) { + continue; + } + + if (v && Array.isArray(v.$in)) { + v.$in.forEach((v, i) => { + validatorsToExecute.push( + schemaPath.doValidate(v, context, { updateValidator: true }).catch(err => { + err.path = updates[i] + '.$in.' + i; + validationErrors.push(err); + }) + ); + }); + } else { + if (isPull[updates[i]] && + schemaPath.$isMongooseArray) { + continue; + } + + if (schemaPath.$isMongooseDocumentArrayElement && v?.$__ != null) { + alreadyValidated.push(updates[i]); + validatorsToExecute.push( + schemaPath.doValidate(v, context, { updateValidator: true }).catch(err => { + if (err.errors) { + for (const key of Object.keys(err.errors)) { + const _err = err.errors[key]; + _err.path = updates[i] + '.' + key; + validationErrors.push(_err); + } + } else { + err.path = updates[i]; + validationErrors.push(err); + } + }) + ); + } else { + const isAlreadyValidated = alreadyValidated.find(path => updates[i].startsWith(path + '.')); + if (isAlreadyValidated) { + continue; + } + if (schemaPath.$isSingleNested) { + alreadyValidated.push(updates[i]); + } + validatorsToExecute.push( + schemaPath.doValidate(v, context, { updateValidator: true }).catch(err => { + if (schemaPath.schema != null && + schemaPath.schema.options.storeSubdocValidationError === false && + err instanceof ValidationError) { + return; + } + + if (err) { + err.path = updates[i]; + validationErrors.push(err); + } + }) + ); + } + } + } + + const arrayUpdates = Object.keys(arrayAtomicUpdates); + for (const arrayUpdate of arrayUpdates) { + let schemaPath = schema._getSchema(arrayUpdate); + if (schemaPath && schemaPath.$isMongooseDocumentArray) { + validatorsToExecute.push( + schemaPath.doValidate( + arrayAtomicUpdates[arrayUpdate], + options?.context === 'query' ? query : null + ).catch(err => { + err.path = arrayUpdate; + validationErrors.push(err); + }) + ); + } else { + schemaPath = schema._getSchema(arrayUpdate + '.0'); + for (const atomicUpdate of arrayAtomicUpdates[arrayUpdate]) { + validatorsToExecute.push( + schemaPath.doValidate( + atomicUpdate, + options?.context === 'query' ? query : null, + { updateValidator: true } + ).catch(err => { + err.path = arrayUpdate; + validationErrors.push(err); + }) + ); + } + } + } + + await Promise.all(validatorsToExecute); + return validationErrors; +}; diff --git a/backend/node_modules/mongoose/lib/index.js b/backend/node_modules/mongoose/lib/index.js new file mode 100644 index 0000000..67534e9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/index.js @@ -0,0 +1,17 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const mongodbDriver = require('./drivers/node-mongodb-native'); + +require('./driver').set(mongodbDriver); + +const mongoose = require('./mongoose'); + +mongoose.setDriver(mongodbDriver); + +mongoose.Mongoose.prototype.mongo = require('mongodb'); + +module.exports = mongoose; diff --git a/backend/node_modules/mongoose/lib/internal.js b/backend/node_modules/mongoose/lib/internal.js new file mode 100644 index 0000000..8de1b27 --- /dev/null +++ b/backend/node_modules/mongoose/lib/internal.js @@ -0,0 +1,46 @@ +/*! + * Dependencies + */ + +'use strict'; + +const StateMachine = require('./stateMachine'); +const ActiveRoster = StateMachine.ctor('require', 'modify', 'init', 'default', 'ignore'); + +module.exports = exports = InternalCache; + +function InternalCache() { + this.activePaths = new ActiveRoster(); +} + +InternalCache.prototype.strictMode = true; + +InternalCache.prototype.fullPath = undefined; +InternalCache.prototype.selected = undefined; +InternalCache.prototype.shardval = undefined; +InternalCache.prototype.saveError = undefined; +InternalCache.prototype.validationError = undefined; +InternalCache.prototype.adhocPaths = undefined; +InternalCache.prototype.removing = undefined; +InternalCache.prototype.inserting = undefined; +InternalCache.prototype.saving = undefined; +InternalCache.prototype.version = undefined; +InternalCache.prototype._id = undefined; +InternalCache.prototype.ownerDocument = undefined; +InternalCache.prototype.populate = undefined; // what we want to populate in this doc +InternalCache.prototype.populated = undefined;// the _ids that have been populated +InternalCache.prototype.primitiveAtomics = undefined; + +/** + * If `false`, this document was not the result of population. + * If `true`, this document is a populated doc underneath another doc + * If an object, this document is a populated doc and the `value` property of the + * object contains the original depopulated value. + */ +InternalCache.prototype.wasPopulated = false; + +InternalCache.prototype.scope = undefined; + +InternalCache.prototype.session = null; +InternalCache.prototype.pathsToScopes = null; +InternalCache.prototype.cachedRequired = null; diff --git a/backend/node_modules/mongoose/lib/model.js b/backend/node_modules/mongoose/lib/model.js new file mode 100644 index 0000000..daaa5bd --- /dev/null +++ b/backend/node_modules/mongoose/lib/model.js @@ -0,0 +1,5202 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const Aggregate = require('./aggregate'); +const ChangeStream = require('./cursor/changeStream'); +const Document = require('./document'); +const DocumentNotFoundError = require('./error/notFound'); +const EventEmitter = require('events').EventEmitter; +const Kareem = require('kareem'); +const MongooseBulkWriteError = require('./error/bulkWriteError'); +const MongooseError = require('./error/index'); +const ObjectParameterError = require('./error/objectParameter'); +const OverwriteModelError = require('./error/overwriteModel'); +const Query = require('./query'); +const SaveOptions = require('./options/saveOptions'); +const Schema = require('./schema'); +const ValidationError = require('./error/validation'); +const VersionError = require('./error/version'); +const ParallelSaveError = require('./error/parallelSave'); +const applyDefaultsHelper = require('./helpers/document/applyDefaults'); +const isInPathsToSave = require('./helpers/document/isInPathsToSave'); +const applyDefaultsToPOJO = require('./helpers/model/applyDefaultsToPOJO'); +const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators'); +const applyHooks = require('./helpers/model/applyHooks'); +const applyMethods = require('./helpers/model/applyMethods'); +const applyProjection = require('./helpers/projection/applyProjection'); +const applyReadConcern = require('./helpers/schema/applyReadConcern'); +const applySchemaCollation = require('./helpers/indexes/applySchemaCollation'); +const applyStaticHooks = require('./helpers/model/applyStaticHooks'); +const applyStatics = require('./helpers/model/applyStatics'); +const applyTimestampsHelper = require('./helpers/document/applyTimestamps'); +const applyWriteConcern = require('./helpers/schema/applyWriteConcern'); +const applyVirtualsHelper = require('./helpers/document/applyVirtuals'); +const assignVals = require('./helpers/populate/assignVals'); +const castBulkWrite = require('./helpers/model/castBulkWrite'); +const clone = require('./helpers/clone'); +const createPopulateQueryFilter = require('./helpers/populate/createPopulateQueryFilter'); +const decorateUpdateWithVersionKey = require('./helpers/update/decorateUpdateWithVersionKey'); +const getDefaultBulkwriteResult = require('./helpers/getDefaultBulkwriteResult'); +const getSchemaDiscriminatorByValue = require('./helpers/discriminator/getSchemaDiscriminatorByValue'); +const discriminator = require('./helpers/model/discriminator'); +const each = require('./helpers/each'); +const get = require('./helpers/get'); +const getConstructorName = require('./helpers/getConstructorName'); +const getDiscriminatorByValue = require('./helpers/discriminator/getDiscriminatorByValue'); +const getModelsMapForPopulate = require('./helpers/populate/getModelsMapForPopulate'); +const immediate = require('./helpers/immediate'); +const internalToObjectOptions = require('./options').internalToObjectOptions; +const isDefaultIdIndex = require('./helpers/indexes/isDefaultIdIndex'); +const isIndexEqual = require('./helpers/indexes/isIndexEqual'); +const isIndexSpecEqual = require('./helpers/indexes/isIndexSpecEqual'); +const isTimeseriesIndex = require('./helpers/indexes/isTimeseriesIndex'); +const { + getRelatedDBIndexes, + getRelatedSchemaIndexes +} = require('./helpers/indexes/getRelatedIndexes'); +const decorateDiscriminatorIndexOptions = require('./helpers/indexes/decorateDiscriminatorIndexOptions'); +const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive'); +const leanPopulateMap = require('./helpers/populate/leanPopulateMap'); +const parallelLimit = require('./helpers/parallelLimit'); +const parseProjection = require('./helpers/projection/parseProjection'); +const prepareDiscriminatorPipeline = require('./helpers/aggregate/prepareDiscriminatorPipeline'); +const pushNestedArrayPaths = require('./helpers/model/pushNestedArrayPaths'); +const removeDeselectedForeignField = require('./helpers/populate/removeDeselectedForeignField'); +const setDottedPath = require('./helpers/path/setDottedPath'); +const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter'); +const util = require('util'); +const utils = require('./utils'); +const minimize = require('./helpers/minimize'); +const MongooseBulkSaveIncompleteError = require('./error/bulkSaveIncompleteError'); +const ObjectExpectedError = require('./error/objectExpected'); +const decorateBulkWriteResult = require('./helpers/model/decorateBulkWriteResult'); +const modelCollectionSymbol = Symbol('mongoose#Model#collection'); +const modelDbSymbol = Symbol('mongoose#Model#db'); +const { + arrayAtomicsBackupSymbol, + arrayAtomicsSymbol, + modelSymbol +} = require('./helpers/symbols'); +const subclassedSymbol = Symbol('mongoose#Model#subclassed'); + +const { VERSION_INC, VERSION_WHERE, VERSION_ALL } = Document; + +const saveToObjectOptions = Object.assign({}, internalToObjectOptions, { + bson: true +}); + +/** + * A Model is a class that's your primary tool for interacting with MongoDB. + * An instance of a Model is called a [Document](https://mongoosejs.com/docs/api/document.html#Document). + * + * In Mongoose, the term "Model" refers to subclasses of the `mongoose.Model` + * class. You should not use the `mongoose.Model` class directly. The + * [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.model()) and + * [`connection.model()`](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.model()) functions + * create subclasses of `mongoose.Model` as shown below. + * + * #### Example: + * + * // `UserModel` is a "Model", a subclass of `mongoose.Model`. + * const UserModel = mongoose.model('User', new Schema({ name: String })); + * + * // You can use a Model to create new documents using `new`: + * const userDoc = new UserModel({ name: 'Foo' }); + * await userDoc.save(); + * + * // You also use a model to create queries: + * const userFromDb = await UserModel.findOne({ name: 'Foo' }); + * + * @param {object} doc values for initial set + * @param {object} [fields] optional object containing the fields that were selected in the query which returned this document. You do **not** need to set this parameter to ensure Mongoose handles your [query projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()). + * @param {object} [options] optional object containing the options for the document. + * @param {boolean} [options.defaults=true] if `false`, skip applying default values to this document. + * @param {boolean} [options.skipId=false] By default, Mongoose document if one is not provided and the document's schema does not override Mongoose's default `_id`. Set `skipId` to `true` to skip this generation step. + * @inherits Document https://mongoosejs.com/docs/api/document.html + * @event `error`: If listening to this event, 'error' is emitted when a document was saved and an `error` occurred. If not listening, the event bubbles to the connection used to create this Model. + * @event `index`: Emitted after `Model#ensureIndexes` completes. If an error occurred it is passed with the event. + * @event `index-single-start`: Emitted when an individual index starts within `Model#ensureIndexes`. The fields and options being used to build the index are also passed with the event. + * @event `index-single-done`: Emitted when an individual index finishes within `Model#ensureIndexes`. If an error occurred it is passed with the event. The fields, options, and index name are also passed. + * @api public + */ + +function Model(doc, fields, options) { + if (fields instanceof Schema) { + throw new TypeError('2nd argument to `Model` constructor must be a POJO or string, ' + + '**not** a schema. Make sure you\'re calling `mongoose.model()`, not ' + + '`mongoose.Model()`.'); + } + if (typeof doc === 'string') { + throw new TypeError('First argument to `Model` constructor must be an object, ' + + '**not** a string. Make sure you\'re calling `mongoose.model()`, not ' + + '`mongoose.Model()`.'); + } + Document.call(this, doc, fields, options); +} + +/** + * Inherits from Document. + * + * All Model.prototype features are available on + * top level (non-sub) documents. + * @api private + */ + +Object.setPrototypeOf(Model.prototype, Document.prototype); +Model.prototype.$isMongooseModelPrototype = true; + +/** + * Connection the model uses. + * + * @api public + * @property db + * @memberOf Model + * @instance + */ + +Model.prototype.db; + +/** + * Changes the Connection instance this model uses to make requests to MongoDB. + * This function is most useful for changing the Connection that a Model defined using `mongoose.model()` uses + * after initialization. + * + * #### Example: + * + * await mongoose.connect('mongodb://127.0.0.1:27017/db1'); + * const UserModel = mongoose.model('User', mongoose.Schema({ name: String })); + * UserModel.connection === mongoose.connection; // true + * + * const conn2 = await mongoose.createConnection('mongodb://127.0.0.1:27017/db2').asPromise(); + * UserModel.useConnection(conn2); // `UserModel` now stores documents in `db2`, not `db1` + * + * UserModel.connection === mongoose.connection; // false + * UserModel.connection === conn2; // true + * + * conn2.model('User') === UserModel; // true + * mongoose.model('User'); // Throws 'MissingSchemaError' + * + * Note: `useConnection()` does **not** apply any [connection-level plugins](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.plugin()) from the new connection. + * If you use `useConnection()` to switch a model's connection, the model will still have the old connection's plugins. + * + * @function useConnection + * @param {Connection} connection The new connection to use + * @return {Model} this + * @api public + */ + +Model.useConnection = function useConnection(connection) { + if (!connection) { + throw new MongooseError('Please provide a connection.'); + } + if (this.db) { + delete this.db.models[this.modelName]; + delete this.prototype.db; + delete this.prototype[modelDbSymbol]; + delete this.prototype.collection; + delete this.prototype.$collection; + delete this.prototype[modelCollectionSymbol]; + } + + this.db = connection; + const collection = connection.collection(this.collection.collectionName, connection.options); + this.prototype.collection = collection; + this.prototype.$collection = collection; + this.prototype[modelCollectionSymbol] = collection; + this.prototype.db = connection; + this.prototype[modelDbSymbol] = connection; + this.collection = collection; + this.$__collection = collection; + connection.models[this.modelName] = this; + + return this; +}; + +/** + * The collection instance this model uses. + * A Mongoose collection is a thin wrapper around a [MongoDB Node.js driver collection]([MongoDB Node.js driver collection](https://mongodb.github.io/node-mongodb-native/Next/classes/Collection.html)). + * Using `Model.collection` means you bypass Mongoose middleware, validation, and casting. + * + * This property is read-only. Modifying this property is a no-op. + * + * @api public + * @property collection + * @memberOf Model + * @instance + */ + +Model.prototype.collection; + +/** + * Internal collection the model uses. + * + * This property is read-only. Modifying this property is a no-op. + * + * @api private + * @property collection + * @memberOf Model + * @instance + */ + + +Model.prototype.$__collection; + +/** + * The name of the model + * + * @api public + * @property modelName + * @memberOf Model + * @instance + */ + +Model.prototype.modelName; + +/** + * Additional properties to attach to the query when calling `save()` and + * `isNew` is false. + * + * @api public + * @property $where + * @memberOf Model + * @instance + */ + +Model.prototype.$where; + +/** + * If this is a discriminator model, `baseModelName` is the name of + * the base model. + * + * @api public + * @property baseModelName + * @memberOf Model + * @instance + */ + +Model.prototype.baseModelName; + +/** + * Event emitter that reports any errors that occurred. Useful for global error + * handling. + * + * #### Example: + * + * MyModel.events.on('error', err => console.log(err.message)); + * + * // Prints a 'CastError' because of the above handler + * await MyModel.findOne({ _id: 'Not a valid ObjectId' }).catch(noop); + * + * @api public + * @property events + * @fires error whenever any query or model function errors + * @memberOf Model + * @static + */ + +Model.events; + +/** + * Compiled middleware for this model. Set in `applyHooks()`. + * + * @api private + * @property _middleware + * @memberOf Model + * @static + */ + +Model._middleware; + +/*! + * ignore + */ + +function _applyCustomWhere(doc, where) { + if (doc.$where == null) { + return; + } + for (const key of Object.keys(doc.$where)) { + where[key] = doc.$where[key]; + } +} + +/*! + * ignore + */ +function _createSaveOptions(doc, options) { + const saveOptions = {}; + + applyWriteConcern(doc.$__schema, options); + if (typeof options.writeConcern !== 'undefined') { + saveOptions.writeConcern = {}; + if ('w' in options.writeConcern) { + saveOptions.writeConcern.w = options.writeConcern.w; + } + if ('j' in options.writeConcern) { + saveOptions.writeConcern.j = options.writeConcern.j; + } + if ('wtimeout' in options.writeConcern) { + saveOptions.writeConcern.wtimeout = options.writeConcern.wtimeout; + } + } else { + if ('w' in options) { + saveOptions.w = options.w; + } + if ('j' in options) { + saveOptions.j = options.j; + } + if ('wtimeout' in options) { + saveOptions.wtimeout = options.wtimeout; + } + } + if ('checkKeys' in options) { + saveOptions.checkKeys = options.checkKeys; + } + + const session = doc.$session(); + const asyncLocalStorage = doc[modelDbSymbol].base.transactionAsyncLocalStorage?.getStore(); + if (session != null) { + saveOptions.session = session; + } else if (!Object.hasOwn(options, 'session') && asyncLocalStorage?.session != null) { + // Only set session from asyncLocalStorage if `session` option wasn't originally passed in options + saveOptions.session = asyncLocalStorage.session; + } + + return saveOptions; +} + +/*! + * ignore + */ + +Model.prototype.$__save = async function $__save(options) { + try { + const hasValidateBeforeSaveOption = options && + (typeof options === 'object') && + ('validateBeforeSave' in options); + const shouldValidateBeforeSave = hasValidateBeforeSaveOption ? + !!options.validateBeforeSave : + this.$__schema.options.validateBeforeSave; + if (shouldValidateBeforeSave) { + const hasValidateModifiedOnlyOption = options != null && + typeof options === 'object' && + Object.hasOwn(options, 'validateModifiedOnly'); + const validateOptions = hasValidateModifiedOnlyOption ? + { validateModifiedOnly: options.validateModifiedOnly } : + null; + await this.$validate(validateOptions); + this.$op = 'save'; + } + + await this._execDocumentPreHooks('save', options, [options]); + } catch (error) { + await this._execDocumentPostHooks('save', options, error); + return; + } + + + let result = null; + let where = null; + try { + const saveOptions = _createSaveOptions(this, options); + + if (this.$isNew) { + // send entire doc + const obj = this.$__hasOnlyPrimitiveValues() ? + this.$__toObjectShallow() : + this.toObject(saveToObjectOptions); + if ((obj || {})._id === void 0) { + // documents must have an _id else mongoose won't know + // what to update later if more changes are made. the user + // wouldn't know what _id was generated by mongodb either + // nor would the ObjectId generated by mongodb necessarily + // match the schema definition. + throw new MongooseError('document must have an _id before saving'); + } + + this.$__version(true, obj); + this.$__reset(); + _setIsNew(this, false); + // Make it possible to retry the insert + this.$__.inserting = true; + result = await this[modelCollectionSymbol].insertOne(obj, saveOptions).catch(err => { + _setIsNew(this, true); + throw err; + }); + } else { + // Make sure we don't treat it as a new object on error, + // since it already exists + this.$__.inserting = false; + const pathsToSave = Array.isArray(options.pathsToSave) ? options.pathsToSave : null; + const pathsToSaveSet = pathsToSave != null ? new Set(pathsToSave) : null; + const delta = this.$__delta(pathsToSave, pathsToSaveSet); + const unsavedDirty = pathsToSave != null ? (delta != null ? delta[2] : this.$__dirty()) : null; + const unsavedDefaultPaths = pathsToSave != null + ? Object.keys(this.$__.activePaths.getStatePaths('default')).filter(path => !isInPathsToSave(path, pathsToSaveSet, pathsToSave)) + : null; + + if (delta) { + where = this.$__where(delta[0]); + _applyCustomWhere(this, where); + + const update = delta[1]; + if (this.$__schema.options.minimize) { + for (const updateOp of Object.values(update)) { + if (updateOp == null) { + continue; + } + for (const key of Object.keys(updateOp)) { + if (updateOp[key] == null || typeof updateOp[key] !== 'object') { + continue; + } + if (!utils.isPOJO(updateOp[key])) { + continue; + } + minimize(updateOp[key]); + if (utils.hasOwnKeys(updateOp[key]) === false) { + delete updateOp[key]; + update.$unset = update.$unset || {}; + update.$unset[key] = 1; + } + } + } + } + + // store the modified paths before the document is reset + this.$__.modifiedPaths = this.modifiedPaths(); + this.$__reset(); + restoreUnsavedState(this, unsavedDirty, unsavedDefaultPaths); + + _setIsNew(this, false); + result = await this[modelCollectionSymbol].updateOne(where, update, saveOptions).catch(err => { + this.$__undoReset(); + throw err; + }); + } else { + where = this.$__where(); + _applyCustomWhere(this, where); + if (this.$__.version) { + this.$__version(where, delta); + } + + applyReadConcern(this.$__schema, saveOptions); + result = await this.constructor.collection.findOne(where, { ...saveOptions, projection: { _id: 1 } }) + .then(documentExists => ({ matchedCount: !documentExists ? 0 : 1 })); + } + } + } catch (err) { + const error = this.$__schema._transformDuplicateKeyError(err); + await this._execDocumentPostHooks('save', options, error); + return; + } + + let numAffected = 0; + const writeConcern = options != null ? + options.writeConcern != null ? + options.writeConcern.w : + options.w : + 0; + if (writeConcern !== 0) { + // Skip checking if write succeeded if writeConcern is set to + // unacknowledged writes, because otherwise `numAffected` will always be 0 + if (result != null) { + if (Array.isArray(result)) { + numAffected = result.length; + } else if (result.matchedCount != null) { + numAffected = result.matchedCount; + } else { + numAffected = result; + } + } + + const versionBump = this.$__.version; + // was this an update that required a version bump? + if (versionBump && !this.$__.inserting) { + const doIncrement = VERSION_INC === (VERSION_INC & this.$__.version); + this.$__.version = undefined; + const key = this.$__schema.options.versionKey; + const version = this.$__getValue(key) || 0; + if (numAffected <= 0) { + // the update failed. pass an error back + this.$__undoReset(); + const err = this.$__.$versionError || + new VersionError(this, version, this.$__.modifiedPaths); + await this._execDocumentPostHooks('save', options, err); + return; + } + + // increment version if was successful + if (doIncrement) { + this.$__setValue(key, version + 1); + } + } + if (result != null && numAffected <= 0) { + this.$__undoReset(); + const error = new DocumentNotFoundError(where, this.constructor.modelName, numAffected, result); + await this._execDocumentPostHooks('save', options, error); + return; + } + } + this.$__.saving = undefined; + this.$__.savedState = {}; + this.$emit('save', this, numAffected); + this.constructor.emit('save', this, numAffected); + await this._execDocumentPostHooks('save', options); +}; + +/*! + * Restores $__.activePaths state and any atomics for paths that failed + * to save. + * + * @param {Document} doc + * @param {object[]} unsavedDirty + * @param {string[]} unsavedDefaultPaths + */ + +function restoreUnsavedState(doc, unsavedDirty, unsavedDefaultPaths) { + if (unsavedDirty == null) { + return; + } + + for (const dirty of unsavedDirty) { + doc.$__.activePaths.modify(dirty.path); + if (dirty.value?.[arrayAtomicsBackupSymbol]) { + dirty.value[arrayAtomicsSymbol] = dirty.value[arrayAtomicsBackupSymbol]; + dirty.value[arrayAtomicsBackupSymbol] = null; + } + } + + for (const path of unsavedDefaultPaths) { + doc.$__.activePaths.default(path); + } +} + +/*! + * ignore + */ + +function generateVersionError(doc, modifiedPaths, defaultPaths) { + const key = doc.$__schema.options.versionKey; + if (!key) { + return null; + } + const version = doc.$__getValue(key) || 0; + return new VersionError(doc, version, modifiedPaths.concat(defaultPaths)); +} + +/** + * Saves this document by inserting a new document into the database if [document.isNew](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew) is `true`, + * or sends an [updateOne](https://mongoosejs.com/docs/api/document.html#Document.prototype.updateOne()) operation with just the modified paths if `isNew` is `false`. + * + * #### Example: + * + * product.sold = Date.now(); + * product = await product.save(); + * + * If save is successful, the returned promise will fulfill with the document + * saved. + * + * #### Example: + * + * const newProduct = await product.save(); + * newProduct === product; // true + * + * @param {object} [options] options optional options + * @param {Session} [options.session=null] the [session](https://www.mongodb.com/docs/manual/reference/server-sessions/) associated with this save operation. If not specified, defaults to the [document's associated session](https://mongoosejs.com/docs/api/document.html#Document.prototype.session()). + * @param {object} [options.safe] (DEPRECATED) overrides [schema's safe option](https://mongoosejs.com/docs/guide.html#safe). Use the `w` option instead. + * @param {boolean} [options.validateBeforeSave] set to false to save without validating. + * @param {boolean} [options.validateModifiedOnly=false] if `true`, Mongoose will only validate modified paths, as opposed to modified paths and `required` paths. + * @param {number|string} [options.w] set the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.j] set to true for MongoDB to wait until this `save()` has been [journaled before resolving the returned promise](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {number} [options.wtimeout] sets a [timeout for the write concern](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). Overrides the [schema-level `writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern). + * @param {boolean} [options.checkKeys=true] the MongoDB driver prevents you from saving keys that start with '$' or contain '.' by default. Set this option to `false` to skip that check. See [restrictions on field names](https://docs.mongodb.com/manual/reference/limits/#mongodb-limit-Restrictions-on-Field-Names) + * @param {boolean} [options.timestamps=true] if `false` and [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this `save()`. + * @param {Array} [options.pathsToSave] An array of paths that tell mongoose to only validate and save the paths in `pathsToSave`. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @throws {DocumentNotFoundError} if this [save updates an existing document](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew) but the document doesn't exist in the database. For example, you will get this error if the document is [deleted between when you retrieved the document and when you saved it](documents.html#updating). + * @return {Promise} + * @api public + * @see middleware https://mongoosejs.com/docs/middleware.html + */ + +Model.prototype.save = async function save(options) { + if (typeof options === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.prototype.save() no longer accepts a callback'); + } + + let parallelSave; + this.$op = 'save'; + + if (this.$__.saving) { + parallelSave = new ParallelSaveError(this); + } else { + this.$__.saving = new ParallelSaveError(this); + } + + options = new SaveOptions(options); + if (Object.hasOwn(options, 'session')) { + this.$session(options.session); + } + if (this.$__.timestamps != null) { + options.timestamps = this.$__.timestamps; + } + if (!this.$isNew) { + this.$__.$versionError = generateVersionError( + this, + this.modifiedPaths(), + Object.keys(this.$__.activePaths.getStatePaths('default')) + ); + } + + if (parallelSave) { + this.$__handleReject(parallelSave); + throw parallelSave; + } + + this.$__.saveOptions = options; + + try { + await this.$__save(options); + } catch (error) { + this.$__handleReject(error); + throw error; + } finally { + this.$__.saving = null; + this.$__.saveOptions = null; + this.$__.$versionError = null; + this.$op = null; + } + + return this; +}; + +Model.prototype.$save = Model.prototype.save; + +/** + * Appends versioning to the where and update clauses. + * + * @api private + * @method $__version + * @memberOf Model + * @instance + */ + +Model.prototype.$__version = function(where, delta) { + const key = this.$__schema.options.versionKey; + if (where === true) { + // this is an insert + if (key) { + setDottedPath(delta, key, 0); + this.$__setValue(key, 0); + } + return; + } + + if (key === false) { + return; + } + + // updates + + // only apply versioning if our versionKey was selected. else + // there is no way to select the correct version. we could fail + // fast here and force them to include the versionKey but + // thats a bit intrusive. can we do this automatically? + + if (!this.$__isSelected(key)) { + return; + } + + // $push $addToSet don't need the where clause set + if (VERSION_WHERE === (VERSION_WHERE & this.$__.version)) { + const value = this.$__getValue(key); + if (value != null) where[key] = value; + } + + if (VERSION_INC === (VERSION_INC & this.$__.version)) { + if (get(delta.$set, key, null) != null) { + // Version key is getting set, means we'll increment the doc's version + // after a successful save, so we should set the incremented version so + // future saves don't fail (gh-5779) + ++delta.$set[key]; + } else { + delta.$inc = delta.$inc || {}; + delta.$inc[key] = 1; + } + } +}; + +/** + * Signal that we desire an increment of this documents version. + * + * #### Example: + * + * const doc = await Model.findById(id); + * doc.increment(); + * await doc.save(); + * + * @see versionKeys https://mongoosejs.com/docs/guide.html#versionKey + * @memberOf Model + * @method increment + * @api public + */ + +Model.prototype.increment = function increment() { + this.$__.version = VERSION_ALL; + return this; +}; + +/** + * Returns a query object + * + * @api private + * @method $__where + * @memberOf Model + * @instance + */ + +Model.prototype.$__where = function _where(where) { + where || (where = {}); + + if (!where._id) { + where._id = this._doc._id; + } + + if (this._doc._id === void 0) { + throw new MongooseError('No _id found on document!'); + } + + return where; +}; + +/** + * Delete this document from the db. Returns a Query instance containing a `deleteOne` operation by this document's `_id`. + * + * #### Example: + * + * await product.deleteOne(); + * await Product.findById(product._id); // null + * + * Since `deleteOne()` returns a Query, the `deleteOne()` will **not** execute unless you use either `await`, `.then()`, `.catch()`, or [`.exec()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.exec()) + * + * #### Example: + * + * product.deleteOne(); // Doesn't do anything + * product.deleteOne().exec(); // Deletes the document, returns a promise + * + * @return {Query} Query + * @api public + */ + +Model.prototype.deleteOne = function deleteOne(options) { + if (typeof options === 'function' || + typeof arguments[1] === 'function') { + throw new MongooseError('Model.prototype.deleteOne() no longer accepts a callback'); + } + + if (!options) { + options = {}; + } + + if (Object.hasOwn(options, 'session')) { + this.$session(options.session); + } + + const self = this; + const where = this.$__where(); + const query = self.constructor.deleteOne(); + + if (this.$session() != null) { + if (!('session' in query.options)) { + query.options.session = this.$session(); + } + } + + const preFilter = buildMiddlewareFilter(options, 'pre'); + const postFilter = buildMiddlewareFilter(options, 'post'); + + query.pre(async function queryPreDeleteOne() { + const res = await self.constructor._middleware.execPre('deleteOne', self, [self, options], { filter: preFilter }); + // `self` is passed to pre hooks as argument for backwards compatibility, but that + // isn't the actual arguments passed to the wrapped function. + if (res[0] !== self || res[1] !== options) { + throw new MongooseError('Document deleteOne pre hooks cannot overwrite arguments'); + } + query.deleteOne(where, options); + // Apply custom where conditions _after_ document deleteOne middleware for + // consistency with save() - sharding plugin needs to set $where + if (self.$where != null) { + this.where(self.$where); + } + return res; + }); + query.pre(function callSubdocPreHooks() { + return Promise.all(self.$getAllSubdocs().map(subdoc => subdoc.constructor._middleware.execPre('deleteOne', subdoc, [subdoc], { filter: preFilter }))); + }); + query.pre(function skipIfAlreadyDeleted() { + if (self.$__.isDeleted) { + throw new Kareem.skipWrappedFunction(); + } + }); + query.post(function callSubdocPostHooks() { + return Promise.all(self.$getAllSubdocs().map(subdoc => subdoc.constructor._middleware.execPost('deleteOne', subdoc, [subdoc], { filter: postFilter }))); + }); + query.post(function queryPostDeleteOne() { + return self.constructor._middleware.execPost('deleteOne', self, [self], { filter: postFilter }); + }); + query.transform(function setIsDeleted(result) { + if (result?.deletedCount > 0) { + self.$isDeleted(true); + } + return result; + }); + + return query; +}; + +/** + * Returns the model instance used to create this document if no `name` specified. + * If `name` specified, returns the model with the given `name`. + * + * #### Example: + * + * const doc = new Tank({}); + * doc.$model() === Tank; // true + * await doc.$model('User').findById(id); + * + * @param {string} [name] model name + * @method $model + * @api public + * @return {Model} + */ + +Model.prototype.$model = function $model(name) { + if (arguments.length === 0) { + return this.constructor; + } + return this[modelDbSymbol].model(name); +}; + +/** + * Returns the model instance used to create this document if no `name` specified. + * If `name` specified, returns the model with the given `name`. + * + * #### Example: + * + * const doc = new Tank({}); + * doc.$model() === Tank; // true + * await doc.$model('User').findById(id); + * + * @param {string} [name] model name + * @method model + * @api public + * @return {Model} + */ + +Model.prototype.model = Model.prototype.$model; + +/** + * Returns a document with `_id` only if at least one document exists in the database that matches + * the given `filter`, and `null` otherwise. + * + * Under the hood, `MyModel.exists({ answer: 42 })` is equivalent to + * `MyModel.findOne({ answer: 42 }).select({ _id: 1 }).lean()` + * + * #### Example: + * + * await Character.deleteMany({}); + * await Character.create({ name: 'Jean-Luc Picard' }); + * + * await Character.exists({ name: /picard/i }); // { _id: ... } + * await Character.exists({ name: /riker/i }); // null + * + * This function triggers the following middleware. + * + * - `findOne()` + * + * @param {object} filter + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @return {Query} + */ + +Model.exists = function exists(filter, options) { + _checkContext(this, 'exists'); + if (typeof arguments[2] === 'function') { + throw new MongooseError('Model.exists() no longer accepts a callback'); + } + + const query = this.findOne(filter). + select({ _id: 1 }). + lean(). + setOptions(options); + + return query; +}; + +/** + * Adds a discriminator type. + * + * #### Example: + * + * function BaseSchema() { + * Schema.apply(this, arguments); + * + * this.add({ + * name: String, + * createdAt: Date + * }); + * } + * util.inherits(BaseSchema, Schema); + * + * const PersonSchema = new BaseSchema(); + * const BossSchema = new BaseSchema({ department: String }); + * + * const Person = mongoose.model('Person', PersonSchema); + * const Boss = Person.discriminator('Boss', BossSchema); + * new Boss().__t; // "Boss". `__t` is the default `discriminatorKey` + * + * const employeeSchema = new Schema({ boss: ObjectId }); + * const Employee = Person.discriminator('Employee', employeeSchema, 'staff'); + * new Employee().__t; // "staff" because of 3rd argument above + * + * @param {string} name discriminator model name + * @param {Schema} schema discriminator model schema + * @param {object|string} [options] If string, same as `options.value`. + * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter. + * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning. + * @param {boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name. + * @param {boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead. + * @param {boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead. + * @return {Model} The newly created discriminator model + * @api public + */ + +Model.discriminator = function(name, schema, options) { + let model; + if (typeof name === 'function') { + model = name; + name = utils.getFunctionName(model); + if (!(model.prototype instanceof Model)) { + throw new MongooseError('The provided class ' + name + ' must extend Model'); + } + } + + options = options || {}; + const value = utils.isPOJO(options) ? options.value : options; + const clone = typeof options.clone === 'boolean' ? options.clone : true; + const mergePlugins = typeof options.mergePlugins === 'boolean' ? options.mergePlugins : true; + const overwriteModels = typeof options.overwriteModels === 'boolean' ? options.overwriteModels : false; + + _checkContext(this, 'discriminator'); + + if (utils.isObject(schema) && !schema.instanceOfSchema) { + schema = new Schema(schema); + } + if (schema instanceof Schema && clone) { + schema = schema.clone(); + } + + schema = discriminator(this, name, schema, value, mergePlugins, options.mergeHooks, overwriteModels); + if (this.db.models[name] && !schema.options.overwriteModels && !overwriteModels) { + throw new OverwriteModelError(name); + } + + schema.$isRootDiscriminator = true; + schema.$globalPluginsApplied = true; + + model = this.db.model(model || name, schema, this.$__collection.name); + this.discriminators[name] = model; + const d = this.discriminators[name]; + Object.setPrototypeOf(d.prototype, this.prototype); + Object.defineProperty(d, 'baseModelName', { + value: this.modelName, + configurable: true, + writable: false + }); + + // apply methods and statics + applyMethods(d, schema); + applyStatics(d, schema); + + if (this[subclassedSymbol] != null) { + for (const submodel of this[subclassedSymbol]) { + submodel.discriminators = submodel.discriminators || {}; + submodel.discriminators[name] = + model.__subclass(model.db, schema, submodel.collection.name); + } + } + + return d; +}; + +/** + * Make sure `this` is a model + * @api private + */ + +function _checkContext(ctx, fnName) { + // Check context, because it is easy to mistakenly type + // `new Model.discriminator()` and get an incomprehensible error + if (ctx == null || ctx === global) { + throw new MongooseError('`Model.' + fnName + '()` cannot run without a ' + + 'model as `this`. Make sure you are calling `MyModel.' + fnName + '()` ' + + 'where `MyModel` is a Mongoose model.'); + } else if (ctx[modelSymbol] == null) { + throw new MongooseError('`Model.' + fnName + '()` cannot run without a ' + + 'model as `this`. Make sure you are not calling ' + + '`new Model.' + fnName + '()`'); + } +} + +// Model (class) features + +/*! + * Give the constructor the ability to emit events. + */ + +for (const i in EventEmitter.prototype) { + Model[i] = EventEmitter.prototype[i]; +} + +/** + * This function is responsible for initializing the underlying connection in MongoDB based on schema options. + * This function performs the following operations: + * + * - `createCollection()` unless [`autoCreate`](https://mongoosejs.com/docs/guide.html#autoCreate) option is turned off + * - `ensureIndexes()` unless [`autoIndex`](https://mongoosejs.com/docs/guide.html#autoIndex) option is turned off + * - `createSearchIndex()` on all schema search indexes if `autoSearchIndex` is enabled. + * + * Mongoose calls this function automatically when a model is a created using + * [`mongoose.model()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.model()) or + * [`connection.model()`](https://mongoosejs.com/docs/api/connection.html#Connection.prototype.model()), so you + * don't need to call `init()` to trigger index builds. + * + * However, you _may_ need to call `init()` to get back a promise that will resolve when your indexes are finished. + * Calling `await Model.init()` is helpful if you need to wait for indexes to build before continuing. + * For example, if you want to wait for unique indexes to build before continuing with a test case. + * + * #### Example: + * + * const eventSchema = new Schema({ thing: { type: 'string', unique: true } }) + * // This calls `Event.init()` implicitly, so you don't need to call + * // `Event.init()` on your own. + * const Event = mongoose.model('Event', eventSchema); + * + * await Event.init(); + * console.log('Indexes are done building!'); + * + * @api public + * @returns {Promise} + */ + +Model.init = function init() { + _checkContext(this, 'init'); + if (typeof arguments[0] === 'function') { + throw new MongooseError('Model.init() no longer accepts a callback'); + } + + this.schema.emit('init', this); + + if (this.$init != null) { + return this.$init; + } + + const conn = this.db; + const _ensureIndexes = async() => { + const autoIndex = utils.getOption( + 'autoIndex', + this.schema.options, + conn.config, + conn.base.options + ); + if (!autoIndex) { + return; + } + return await this.ensureIndexes({ _automatic: true }); + }; + const _createSearchIndexes = async() => { + const autoSearchIndex = utils.getOption( + 'autoSearchIndex', + this.schema.options, + conn.config, + conn.base.options + ); + if (!autoSearchIndex) { + return; + } + + return await this.createSearchIndexes(); + }; + const _createCollection = async() => { + let autoCreate = utils.getOption( + 'autoCreate', + this.schema.options, + conn.config + // No base.options here because we don't want to take the base value if the connection hasn't + // set it yet + ); + if (autoCreate == null) { + // `autoCreate` may later be set when the connection is opened, so wait for connect before checking + await conn._waitForConnect(true); + autoCreate = utils.getOption( + 'autoCreate', + this.schema.options, + conn.config, + conn.base.options + ); + } + + if (!autoCreate) { + return; + } + + return await this.createCollection(); + }; + + this.$init = _createCollection(). + then(() => _ensureIndexes()). + then(() => _createSearchIndexes()); + + const _catch = this.$init.catch; + const _this = this; + this.$init.catch = function() { + _this.$caught = true; + return _catch.apply(_this.$init, arguments); + }; + + return this.$init; +}; + + +/** + * Create the collection for this model. By default, if no indexes are specified, + * mongoose will not create the collection for the model until any documents are + * created. Use this method to create the collection explicitly. + * + * Note 1: You may need to call this before starting a transaction + * See https://www.mongodb.com/docs/manual/core/transactions/#transactions-and-operations + * + * Note 2: You don't have to call this if your schema contains index or unique field. + * In that case, just use `Model.init()` + * + * #### Example: + * + * const userSchema = new Schema({ name: String }) + * const User = mongoose.model('User', userSchema); + * + * User.createCollection().then(function(collection) { + * console.log('Collection is created!'); + * }); + * + * @api public + * @param {object} [options] see [MongoDB driver docs](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createCollection) + * @returns {Promise} + */ + +Model.createCollection = async function createCollection(options) { + _checkContext(this, 'createCollection'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.createCollection() no longer accepts a callback'); + } + + const preFilter = buildMiddlewareFilter(options, 'pre'); + const postFilter = buildMiddlewareFilter(options, 'post'); + + // Remove middleware option before passing to MongoDB + if (options?.middleware != null) { + options = { ...options }; + delete options.middleware; + } + + [options] = await this.hooks.execPre('createCollection', this, [options], { filter: preFilter }).catch(err => { + if (err instanceof Kareem.skipWrappedFunction) { + return [err]; + } + throw err; + }); + + const collectionOptions = this?.schema?.options?.collectionOptions; + if (collectionOptions != null) { + options = Object.assign({}, collectionOptions, options); + } + + const schemaCollation = this?.schema?.options?.collation; + if (schemaCollation != null) { + options = Object.assign({ collation: schemaCollation }, options); + } + const capped = this?.schema?.options?.capped; + if (capped != null) { + if (typeof capped === 'number') { + options = Object.assign({ capped: true, size: capped }, options); + } else if (typeof capped === 'object') { + options = Object.assign({ capped: true }, capped, options); + } + } + const timeseries = this?.schema?.options?.timeseries; + if (timeseries != null) { + options = Object.assign({ timeseries }, options); + if (options.expireAfterSeconds != null) { + // do nothing + } else if (options.expires != null) { + utils.expires(options); + } else if (this.schema.options.expireAfterSeconds != null) { + options.expireAfterSeconds = this.schema.options.expireAfterSeconds; + } else if (this.schema.options.expires != null) { + options.expires = this.schema.options.expires; + utils.expires(options); + } + } + + const clusteredIndex = this?.schema?.options?.clusteredIndex; + if (clusteredIndex != null) { + options = Object.assign({ clusteredIndex: { ...clusteredIndex, unique: true } }, options); + } + + try { + if (!(options instanceof Kareem.skipWrappedFunction)) { + await this.db.createCollection(this.$__collection.collectionName, options); + } + } catch (err) { + if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) { + await this.hooks.execPost('createCollection', this, [null], { error: err, filter: postFilter }); + } + } + + await this.hooks.execPost('createCollection', this, [this.$__collection], { filter: postFilter }); + + return this.$__collection; +}; + +/** + * Makes the indexes in MongoDB match the indexes defined in this model's + * schema. This function will drop any indexes that are not defined in + * the model's schema except the `_id` index, and build any indexes that + * are in your schema but not in MongoDB. + * + * See the [introductory blog post](https://thecodebarbarian.com/whats-new-in-mongoose-5-2-syncindexes) + * for more information. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, unique: true } }); + * const Customer = mongoose.model('Customer', schema); + * await Customer.collection.createIndex({ age: 1 }); // Index is not in schema + * // Will drop the 'age' index and create an index on `name` + * await Customer.syncIndexes(); + * + * You should be careful about running `syncIndexes()` on production applications under heavy load, + * because index builds are expensive operations, and unexpected index drops can lead to degraded + * performance. Before running `syncIndexes()`, you can use the [`diffIndexes()` function](#Model.diffIndexes()) + * to check what indexes `syncIndexes()` will drop and create. + * + * #### Example: + * + * const { toDrop, toCreate } = await Model.diffIndexes(); + * toDrop; // Array of strings containing names of indexes that `syncIndexes()` will drop + * toCreate; // Array of strings containing names of indexes that `syncIndexes()` will create + * + * @param {object} [options] options to pass to `ensureIndexes()` + * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher + * @return {Promise} + * @api public + */ + +Model.syncIndexes = async function syncIndexes(options) { + _checkContext(this, 'syncIndexes'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.syncIndexes() no longer accepts a callback'); + } + + const autoCreate = options?.autoCreate ?? + this.schema.options?.autoCreate ?? + this.db.config.autoCreate ?? + this.db.base?.options?.autoCreate ?? + true; + + if (autoCreate) { + try { + await this.createCollection(); + } catch (err) { + if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) { + throw err; + } + } + } + + const diffIndexesResult = await this.diffIndexes({ indexOptionsToCreate: true }); + const dropped = await this.cleanIndexes({ ...options, toDrop: diffIndexesResult.toDrop }); + await this.createIndexes({ ...options, toCreate: diffIndexesResult.toCreate }); + + return dropped; +}; + +/** + * Create an [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/). + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, unique: true } }); + * const Customer = mongoose.model('Customer', schema); + * await Customer.createSearchIndex({ name: 'test', definition: { mappings: { dynamic: true } } }); + * + * @param {object} description index options, including `name` and `definition` + * @param {string} description.name + * @param {object} description.definition + * @return {Promise} + * @api public + */ + +Model.createSearchIndex = async function createSearchIndex(description) { + _checkContext(this, 'createSearchIndex'); + + return await this.$__collection.createSearchIndex(description); +}; + +/** + * Update an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/). + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, unique: true } }); + * const Customer = mongoose.model('Customer', schema); + * await Customer.updateSearchIndex('test', { mappings: { dynamic: true } }); + * + * @param {string} name + * @param {object} definition + * @return {Promise} + * @api public + */ + +Model.updateSearchIndex = async function updateSearchIndex(name, definition) { + _checkContext(this, 'updateSearchIndex'); + + return await this.$__collection.updateSearchIndex(name, definition); +}; + +/** + * Delete an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) by name. + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, unique: true } }); + * const Customer = mongoose.model('Customer', schema); + * await Customer.dropSearchIndex('test'); + * + * @param {string} name + * @return {Promise} + * @api public + */ + +Model.dropSearchIndex = async function dropSearchIndex(name) { + _checkContext(this, 'dropSearchIndex'); + + return await this.$__collection.dropSearchIndex(name); +}; + +/** + * List all [Atlas search indexes](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) on this model's collection. + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, unique: true } }); + * const Customer = mongoose.model('Customer', schema); + * + * await Customer.createSearchIndex({ name: 'test', definition: { mappings: { dynamic: true } } }); + * const res = await Customer.listSearchIndexes(); // Includes `[{ name: 'test' }]` + * + * @param {object} [options] + * @return {Promise} + * @api public + */ + +Model.listSearchIndexes = async function listSearchIndexes(options) { + _checkContext(this, 'listSearchIndexes'); + + const cursor = await this.$__collection.listSearchIndexes(options); + + return await cursor.toArray(); +}; + +/** + * Does a dry-run of `Model.syncIndexes()`, returning the indexes that `syncIndexes()` would drop and create if you were to run `syncIndexes()`. + * + * #### Example: + * + * const { toDrop, toCreate } = await Model.diffIndexes(); + * toDrop; // Array of strings containing names of indexes that `syncIndexes()` will drop + * toCreate; // Array of index specs containing the keys of indexes that `syncIndexes()` will create + * + * @param {object} [options] + * @param {boolean} [options.indexOptionsToCreate=false] If true, `toCreate` will include both the index spec and the index options, not just the index spec + * @return {Promise} contains the indexes that would be dropped in MongoDB and indexes that would be created in MongoDB as `{ toDrop: string[], toCreate: string[] }`. + */ + +Model.diffIndexes = async function diffIndexes(options) { + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.syncIndexes() no longer accepts a callback'); + } + + const model = this; + + let dbIndexes = await model.listIndexes().catch(err => { + if (err.codeName == 'NamespaceNotFound') { + return undefined; + } + throw err; + }); + if (dbIndexes === undefined) { + dbIndexes = []; + } + dbIndexes = getRelatedDBIndexes(model, dbIndexes); + + const schema = model.schema; + const schemaIndexes = getRelatedSchemaIndexes(model, schema.indexes()); + + const toDrop = getIndexesToDrop(schema, schemaIndexes, dbIndexes); + const toCreate = getIndexesToCreate(schema, schemaIndexes, dbIndexes, toDrop, options); + + return { toDrop, toCreate }; +}; + +function getIndexesToCreate(schema, schemaIndexes, dbIndexes, toDrop, options) { + const toCreate = []; + const indexOptionsToCreate = options?.indexOptionsToCreate ?? false; + + for (const [schemaIndexKeysObject, schemaIndexOptions] of schemaIndexes) { + let found = false; + + const options = decorateDiscriminatorIndexOptions(schema, clone(schemaIndexOptions)); + + for (const index of dbIndexes) { + if (isDefaultIdIndex(index)) { + continue; + } + if ( + isIndexEqual(schemaIndexKeysObject, options, index) && + !toDrop.includes(index.name) + ) { + found = true; + break; + } + } + + if (!found) { + if (indexOptionsToCreate) { + toCreate.push([schemaIndexKeysObject, schemaIndexOptions]); + } else { + toCreate.push(schemaIndexKeysObject); + } + } + } + + return toCreate; +} + +function getIndexesToDrop(schema, schemaIndexes, dbIndexes) { + const toDrop = []; + + for (const dbIndex of dbIndexes) { + let found = false; + // Never try to drop `_id` index, MongoDB server doesn't allow it + if (isDefaultIdIndex(dbIndex)) { + continue; + } + // Timeseries collections have a default index on { timeField: 1, metaField: 1 }. + if (isTimeseriesIndex(dbIndex, schema.options)) { + continue; + } + + for (const [schemaIndexKeysObject, schemaIndexOptions] of schemaIndexes) { + const options = decorateDiscriminatorIndexOptions(schema, clone(schemaIndexOptions)); + applySchemaCollation(schemaIndexKeysObject, options, schema.options); + + if (isIndexEqual(schemaIndexKeysObject, options, dbIndex)) { + found = true; + break; + } + } + + if (found) { + continue; + } + + toDrop.push(dbIndex.name); + } + + return toDrop; +} +/** + * Deletes all indexes that aren't defined in this model's schema. Used by + * `syncIndexes()`. + * + * The returned promise resolves to a list of the dropped indexes' names as an array + * + * @param {object} [options] + * @param {string[]} [options.toDrop] if specified, contains a list of index names to drop + * @param {boolean} [options.hideIndexes=false] set to `true` to hide indexes instead of dropping. Requires MongoDB server 4.4 or higher + * @return {Promise} list of dropped or hidden index names + * @api public + */ + +Model.cleanIndexes = async function cleanIndexes(options) { + _checkContext(this, 'cleanIndexes'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.cleanIndexes() no longer accepts a callback'); + } + const model = this; + + if (Array.isArray(options?.toDrop)) { + const res = await _dropIndexes(options.toDrop, model, options); + return res; + } + + const res = await model.diffIndexes(); + return await _dropIndexes(res.toDrop, model, options); +}; + +async function _dropIndexes(toDrop, model, options) { + if (toDrop.length === 0) { + return []; + } + + const collection = model.$__collection; + if (options?.hideIndexes) { + await Promise.all(toDrop.map(indexName => { + return model.db.db.command({ + collMod: collection.collectionName, + index: { name: indexName, hidden: true } + }); + })); + } else { + await Promise.all(toDrop.map(indexName => collection.dropIndex(indexName))); + } + + return toDrop; +} + +/** + * Lists the indexes currently defined in MongoDB. This may or may not be + * the same as the indexes defined in your schema depending on whether you + * use the [`autoIndex` option](https://mongoosejs.com/docs/guide.html#autoIndex) and if you + * build indexes manually. + * + * @return {Promise} + * @api public + */ + +Model.listIndexes = async function listIndexes() { + _checkContext(this, 'listIndexes'); + if (typeof arguments[0] === 'function') { + throw new MongooseError('Model.listIndexes() no longer accepts a callback'); + } + + if (this.$__collection.buffer) { + await new Promise(resolve => { + this.$__collection.addQueue(resolve); + }); + } + + return this.$__collection.listIndexes().toArray(); +}; + +/** + * Sends `createIndex` commands to mongo for each index declared in the schema. + * The `createIndex` commands are sent in series. + * + * #### Example: + * + * await Event.ensureIndexes(); + * + * After completion, an `index` event is emitted on this `Model` passing an error if one occurred. + * + * #### Example: + * + * const eventSchema = new Schema({ thing: { type: 'string', unique: true } }) + * const Event = mongoose.model('Event', eventSchema); + * + * Event.on('index', function (err) { + * if (err) console.error(err); // error occurred during index creation + * }); + * + * _NOTE: It is not recommended that you run this in production. Index creation may impact database performance depending on your load. Use with caution._ + * + * @param {object} [options] internal options + * @return {Promise} + * @api public + */ + +Model.ensureIndexes = async function ensureIndexes(options) { + _checkContext(this, 'ensureIndexes'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.ensureIndexes() no longer accepts a callback'); + } + + await new Promise((resolve, reject) => { + _ensureIndexes(this, options, (err) => { + if (err != null) { + return reject(err); + } + resolve(); + }); + }); +}; + +/** + * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/7.0/classes/Db.html#createIndex) + * function. + * + * @param {object} [options] internal options + * @return {Promise} + * @api public + */ + +Model.createIndexes = async function createIndexes(options) { + _checkContext(this, 'createIndexes'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function') { + throw new MongooseError('Model.createIndexes() no longer accepts a callback'); + } + + return this.ensureIndexes(options); +}; + + +/*! + * ignore + */ + +function _ensureIndexes(model, options, callback) { + const indexes = Array.isArray(options?.toCreate) ? options.toCreate : model.schema.indexes(); + let indexError; + + options = options || {}; + const done = function(err) { + if (err && !model.$caught) { + model.emit('error', err); + } + model.emit('index', err || indexError); + callback && callback(err || indexError); + }; + + for (const index of indexes) { + if (isDefaultIdIndex(index)) { + utils.warn('mongoose: Cannot specify a custom index on `_id` for ' + + 'model name "' + model.modelName + '", ' + + 'MongoDB does not allow overwriting the default `_id` index. See ' + + 'https://bit.ly/mongodb-id-index'); + } + } + + // Check for duplicate index definitions (gh-15056) + const seenIndexes = []; + for (const index of indexes) { + const fields = index[0]; + const indexOptions = index[1]; + if (indexOptions.name == null) { + for (const existingIndex of seenIndexes) { + if (existingIndex[1].name == null && isIndexSpecEqual(existingIndex[0], fields)) { + utils.warn('mongoose: Duplicate schema index on ' + JSON.stringify(fields) + + ' for model "' + model.modelName + '". ' + + 'This is often due to declaring an index using both "index: true" and "schema.index()". ' + + 'Please remove the duplicate index definition.'); + break; + } + } + } + seenIndexes.push(index); + } + + if (!indexes.length) { + immediate(function() { + done(); + }); + return; + } + // Indexes are created one-by-one + + const indexSingleDone = function(err, fields, options, name) { + model.emit('index-single-done', err, fields, options, name); + }; + const indexSingleStart = function(fields, options) { + model.emit('index-single-start', fields, options); + }; + + const baseSchema = model.schema._baseSchema; + const baseSchemaIndexes = baseSchema ? baseSchema.indexes() : []; + + immediate(function() { + // If buffering is off, do this manually. + if (options._automatic && !model.collection.collection) { + model.collection.addQueue(create, []); + } else { + create(); + } + }); + + + function create() { + if (options._automatic) { + if (model.schema.options.autoIndex === false || + (model.schema.options.autoIndex == null && model.db.config.autoIndex === false)) { + return done(); + } + } + + const index = indexes.shift(); + if (!index) { + return done(); + } + if (options._automatic && index[1]._autoIndex === false) { + return create(); + } + + if (baseSchemaIndexes.find(i => utils.deepEqual(i, index))) { + return create(); + } + + const indexFields = clone(index[0]); + const indexOptions = clone(index[1]); + + delete indexOptions._autoIndex; + decorateDiscriminatorIndexOptions(model.schema, indexOptions); + applyWriteConcern(model.schema, indexOptions); + applySchemaCollation(indexFields, indexOptions, model.schema.options); + + indexSingleStart(indexFields, options); + + // Just in case `createIndex()` throws a sync error + let promise = null; + try { + promise = model.collection.createIndex(indexFields, indexOptions); + } catch (err) { + if (!indexError) { + indexError = err; + } + if (!model.$caught) { + model.emit('error', err); + } + + indexSingleDone(err, indexFields, indexOptions); + create(); + return; + } + + promise.then( + name => { + indexSingleDone(null, indexFields, indexOptions, name); + create(); + }, + err => { + if (!indexError) { + indexError = err; + } + if (!model.$caught) { + model.emit('error', err); + } + + indexSingleDone(err, indexFields, indexOptions); + create(); + } + ); + } +} + +/** + * Creates all [Atlas search indexes](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) defined in this model's schema. + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const schema = new Schema({ + * name: String, + * description: String + * }); + * schema.searchIndex({ name: 'test', definition: { mappings: { dynamic: true } } }); + * const Product = mongoose.model('Product', schema); + * + * // Creates the search index defined in the schema + * await Product.createSearchIndexes(); + * + * @api public + * @return {Promise} resolves to the results of creating the search indexes + */ + +Model.createSearchIndexes = async function createSearchIndexes() { + _checkContext(this, 'createSearchIndexes'); + const results = []; + for (const searchIndex of this.schema._searchIndexes) { + results.push(await this.createSearchIndex(searchIndex)); + } + return results; +}; + +/** + * Schema the model uses. + * + * @property schema + * @static + * @api public + * @memberOf Model + */ + +Model.schema; + +/** + * Connection instance the model uses. + * + * @property db + * @static + * @api public + * @memberOf Model + */ + +Model.db; + +/** + * Collection the model uses. + * + * @property collection + * @api public + * @memberOf Model + */ + +Model.collection; + +/** + * Internal collection the model uses. + * + * @property collection + * @api private + * @memberOf Model + */ +Model.$__collection; + +/** + * Base Mongoose instance the model uses. + * + * @property base + * @api public + * @memberOf Model + */ + +Model.base; + +/** + * Registered discriminators for this model. + * + * @property discriminators + * @api public + * @memberOf Model + */ + +Model.discriminators; + +/** + * Translate any aliases fields/conditions so the final query or document object is pure + * + * #### Example: + * + * await Character.find(Character.translateAliases({ + * '名': 'Eddard Stark' // Alias for 'name' + * }); + * + * By default, `translateAliases()` overwrites raw fields with aliased fields. + * So if `n` is an alias for `name`, `{ n: 'alias', name: 'raw' }` will resolve to `{ name: 'alias' }`. + * However, you can set the `errorOnDuplicates` option to throw an error if there are potentially conflicting paths. + * The `translateAliases` option for queries uses `errorOnDuplicates`. + * + * #### Note: + * + * Only translate arguments of object type anything else is returned raw + * + * @param {object} fields fields/conditions that may contain aliased keys + * @param {boolean} [errorOnDuplicates] if true, throw an error if there's both a key and an alias for that key in `fields` + * @return {object} the translated 'pure' fields/conditions + */ +Model.translateAliases = function translateAliases(fields, errorOnDuplicates) { + _checkContext(this, 'translateAliases'); + + const translate = (key, value) => { + let alias; + const translated = []; + const fieldKeys = key.split('.'); + let currentSchema = this.schema; + for (const i in fieldKeys) { + const name = fieldKeys[i]; + if (currentSchema?.aliases[name]) { + alias = currentSchema.aliases[name]; + if (errorOnDuplicates && alias in fields) { + throw new MongooseError(`Provided object has both field "${name}" and its alias "${alias}"`); + } + // Alias found, + translated.push(alias); + } else { + alias = name; + // Alias not found, so treat as un-aliased key + translated.push(name); + } + + // Check if aliased path is a schema + if (currentSchema?.paths[alias]) { + currentSchema = currentSchema.paths[alias].schema; + } + else + currentSchema = null; + } + + const translatedKey = translated.join('.'); + if (fields instanceof Map) + fields.set(translatedKey, value); + else + fields[translatedKey] = value; + + if (translatedKey !== key) { + // We'll be using the translated key instead + if (fields instanceof Map) { + // Delete from map + fields.delete(key); + } else { + // Delete from object + delete fields[key]; // We'll be using the translated key instead + } + } + return fields; + }; + + if (typeof fields === 'object') { + // Fields is an object (query conditions or document fields) + if (fields instanceof Map) { + // A Map was supplied + for (const field of new Map(fields)) { + fields = translate(field[0], field[1]); + } + } else { + // Infer a regular object was supplied + for (const key of Object.keys(fields)) { + fields = translate(key, fields[key]); + if (key[0] === '$') { + if (Array.isArray(fields[key])) { + for (const i in fields[key]) { + // Recursively translate nested queries + fields[key][i] = this.translateAliases(fields[key][i]); + } + } else { + this.translateAliases(fields[key]); + } + } + } + } + + return fields; + } else { + // Don't know typeof fields + return fields; + } +}; + +/** + * Deletes the first document that matches `conditions` from the collection. + * It returns an object with the property `deletedCount` indicating how many documents were deleted. + * + * #### Example: + * + * await Character.deleteOne({ name: 'Eddard Stark' }); // returns {deletedCount: 1} + * + * #### Note: + * + * This function triggers `deleteOne` query hooks. Read the + * [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more. + * + * @param {object} conditions + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @api public + */ + +Model.deleteOne = function deleteOne(conditions, options) { + _checkContext(this, 'deleteOne'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.prototype.deleteOne() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.setOptions(options); + + return mq.deleteOne(conditions); +}; + +/** + * Deletes all of the documents that match `conditions` from the collection. + * It returns an object with the property `deletedCount` containing the number of documents deleted. + * + * #### Example: + * + * await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }); // returns {deletedCount: x} where x is the number of documents deleted. + * + * #### Note: + * + * This function triggers `deleteMany` query hooks. Read the + * [middleware docs](https://mongoosejs.com/docs/middleware.html#naming) to learn more. + * + * @param {object} conditions + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @api public + */ + +Model.deleteMany = function deleteMany(conditions, options) { + _checkContext(this, 'deleteMany'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.deleteMany() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.setOptions(options); + + return mq.deleteMany(conditions); +}; + +/** + * Finds documents. + * + * Mongoose casts the `filter` to match the model's schema before the command is sent. + * See our [query casting tutorial](https://mongoosejs.com/docs/tutorials/query_casting.html) for + * more information on how Mongoose casts `filter`. + * + * #### Example: + * + * // find all documents + * await MyModel.find({}); + * + * // find all documents named john and at least 18 + * await MyModel.find({ name: 'john', age: { $gte: 18 } }).exec(); + * + * // executes, name LIKE john and only selecting the "name" and "friends" fields + * await MyModel.find({ name: /john/i }, 'name friends').exec(); + * + * // passing options + * await MyModel.find({ name: /john/i }, null, { skip: 10 }).exec(); + * + * @param {object|ObjectId} filter + * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @see query casting https://mongoosejs.com/docs/tutorials/query_casting.html + * @api public + */ + +Model.find = function find(conditions, projection, options) { + _checkContext(this, 'find'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { + throw new MongooseError('Model.find() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.select(projection); + mq.setOptions(options); + + return mq.find(conditions); +}; + +/** + * Finds a single document by its _id field. `findById(id)` is equivalent to `findOne({ _id: id })`. + * + * The `id` is cast based on the Schema before sending the command. + * + * This function triggers the following middleware. + * + * - `findOne()` + * + * #### Example: + * + * // Find the adventure with the given `id`, or `null` if not found + * await Adventure.findById(id).exec(); + * + * // select only the adventures name and length + * await Adventure.findById(id, 'name length').exec(); + * + * @param {any} id value of `_id` to query by + * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @return {Query} + * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @see lean queries https://mongoosejs.com/docs/tutorials/lean.html + * @see findById in Mongoose https://masteringjs.io/tutorials/mongoose/find-by-id + * @api public + */ + +Model.findById = function findById(id, projection, options) { + _checkContext(this, 'findById'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.findById() no longer accepts a callback'); + } + + return this.findOne({ _id: id }, projection, options); +}; + +/** + * Finds one document. + * + * The `conditions` are cast to their respective SchemaTypes before the command is sent. + * + * *Note:* `conditions` is optional, and if `conditions` is null or undefined, + * mongoose will send an empty `findOne` command to MongoDB, which will return + * an arbitrary document. If you're querying by `_id`, use `findById()` instead. + * + * #### Example: + * + * // Find one adventure whose `country` is 'Croatia', otherwise `null` + * await Adventure.findOne({ country: 'Croatia' }).exec(); + * + * // Model.findOne() no longer accepts a callback + * + * // Select only the adventures name and length + * await Adventure.findOne({ country: 'Croatia' }, 'name length').exec(); + * + * @param {object} [conditions] + * @param {object|string|string[]} [projection] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @see field selection https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @see lean queries https://mongoosejs.com/docs/tutorials/lean.html + * @api public + */ + +Model.findOne = function findOne(conditions, projection, options) { + _checkContext(this, 'findOne'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.findOne() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.select(projection); + mq.setOptions(options); + + return mq.findOne(conditions); +}; + +/** + * Estimates the number of documents in the MongoDB collection. Faster than + * using `countDocuments()` for large collections because + * `estimatedDocumentCount()` uses collection metadata rather than scanning + * the entire collection. + * + * #### Example: + * + * const numAdventures = await Adventure.estimatedDocumentCount(); + * + * @param {object} [options] + * @return {Query} + * @api public + */ + +Model.estimatedDocumentCount = function estimatedDocumentCount(options) { + _checkContext(this, 'estimatedDocumentCount'); + + const mq = new this.Query({}, {}, this, this.$__collection); + + return mq.estimatedDocumentCount(options); +}; + +/** + * Counts number of documents matching `filter` in a database collection. + * + * #### Example: + * + * const count = await Adventure.countDocuments({ type: 'jungle' }); + * console.log('there are %d jungle adventures', count); + * + * If you want to count all documents in a large collection, + * use the [`estimatedDocumentCount()` function](https://mongoosejs.com/docs/api/model.html#Model.estimatedDocumentCount()) + * instead. If you call `countDocuments({})`, MongoDB will always execute + * a full collection scan and **not** use any indexes. + * + * The `countDocuments()` function is similar to `count()`, but there are a + * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments). + * Below are the operators that `count()` supports but `countDocuments()` does not, + * and the suggested replacement: + * + * - `$where`: [`$expr`](https://www.mongodb.com/docs/manual/reference/operator/query/expr/) + * - `$near`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$center`](https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center) + * - `$nearSphere`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere) + * + * @param {object} filter + * @return {Query} + * @api public + */ + +Model.countDocuments = function countDocuments(conditions, options) { + _checkContext(this, 'countDocuments'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.countDocuments() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + if (options != null) { + mq.setOptions(options); + } + + return mq.countDocuments(conditions); +}; + + +/** + * Creates a Query for a `distinct` operation. + * + * #### Example: + * + * const query = Link.distinct('url'); + * query.exec(); + * + * @param {string} field + * @param {object} [conditions] optional + * @param {object} [options] optional + * @return {Query} + * @api public + */ + +Model.distinct = function distinct(field, conditions, options) { + _checkContext(this, 'distinct'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.distinct() no longer accepts a callback'); + } + + const mq = new this.Query({}, {}, this, this.$__collection); + if (options != null) { + mq.setOptions(options); + } + + return mq.distinct(field, conditions); +}; + +/** + * Creates a Query, applies the passed conditions, and returns the Query. + * + * For example, instead of writing: + * + * User.find({ age: { $gte: 21, $lte: 65 } }); + * + * we can instead write: + * + * User.where('age').gte(21).lte(65).exec(); + * + * Since the Query class also supports `where` you can continue chaining + * + * User + * .where('age').gte(21).lte(65) + * .where('name', /^b/i) + * ... etc + * + * @param {string} path + * @param {object} [val] optional value + * @return {Query} + * @api public + */ + +Model.where = function where(path, val) { + _checkContext(this, 'where'); + + void val; // eslint + const mq = new this.Query({}, {}, this, this.$__collection).find({}); + return mq.where.apply(mq, arguments); +}; + +/** + * Creates a `Query` and specifies a `$where` condition. + * + * Sometimes you need to query for things in mongodb using a JavaScript expression. You can do so via `find({ $where: javascript })`, or you can use the mongoose shortcut method $where via a Query chain or from your mongoose Model. + * + * Blog.$where('this.username.indexOf("val") !== -1').exec(function (err, docs) {}); + * + * @param {string|Function} argument is a javascript string or anonymous function + * @method $where + * @memberOf Model + * @return {Query} + * @see Query.$where https://mongoosejs.com/docs/api/query.html#Query.prototype.$where + * @api public + */ + +Model.$where = function $where() { + _checkContext(this, '$where'); + + const mq = new this.Query({}, {}, this, this.$__collection).find({}); + return mq.$where.apply(mq, arguments); +}; + +/** + * Issues a mongodb findOneAndUpdate command. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`. A Query object is returned. + * + * #### Example: + * + * A.findOneAndUpdate(filter, update, options); // returns Query + * A.findOneAndUpdate(filter, update); // returns Query + * A.findOneAndUpdate(filter); // returns Query + * A.findOneAndUpdate(); // returns Query + * + * // Other supported syntaxes + * // Note that calling `Query#findOneAndUpdate()` with 1 arg will treat the arg as `update`, NOT `filter` + * A.find(filter).findOneAndUpdate(update); + * + * #### Note: + * + * All top level update keys which are not `atomic` operation names are treated as set operations: + * + * #### Example: + * + * const query = { name: 'borne' }; + * Model.findOneAndUpdate(query, { name: 'jason bourne' }, options); + * + * // is sent as + * Model.findOneAndUpdate(query, { $set: { name: 'jason bourne' }}, options); + * + * #### Note: + * + * `findOneAndX` and `findByIdAndX` functions support limited validation that + * you can enable by setting the `runValidators` option. + * + * If you need full-fledged validation, use the traditional approach of first + * retrieving the document. + * + * const doc = await Model.findById(id); + * doc.name = 'jason bourne'; + * await doc.save(); + * + * @param {object} [conditions] + * @param {object} [update] + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {boolean} [options.new=false] if true, return the modified document rather than the original + * @param {object|string} [options.fields] Field selection. Equivalent to `.select(fields).findOneAndUpdate()` + * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 + * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. + * @param {boolean} [options.runValidators] if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema + * @param {boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created + * @param {boolean} [options.includeResultMetadata] if true, returns the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @return {Query} + * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @api public + */ + +Model.findOneAndUpdate = function(conditions, update, options) { + _checkContext(this, 'findOneAndUpdate'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { + throw new MongooseError('Model.findOneAndUpdate() no longer accepts a callback'); + } + + let fields; + if (options) { + fields = options.fields || options.projection; + } + + update = clone(update, { + depopulate: true, + _isNested: true + }); + + decorateUpdateWithVersionKey(update, options, this.schema.options.versionKey); + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.select(fields); + + return mq.findOneAndUpdate(conditions, update, options); +}; + +/** + * Issues a mongodb findOneAndUpdate command by a document's _id field. + * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`. + * + * Finds a matching document, updates it according to the `update` arg, + * passing any `options`, and returns the found document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndUpdate()` + * + * #### Example: + * + * A.findByIdAndUpdate(id, update, options) // returns Query + * A.findByIdAndUpdate(id, update) // returns Query + * A.findByIdAndUpdate() // returns Query + * + * #### Note: + * + * All top level update keys which are not `atomic` operation names are treated as set operations: + * + * #### Example: + * + * Model.findByIdAndUpdate(id, { name: 'jason bourne' }, options) + * + * // is sent as + * Model.findByIdAndUpdate(id, { $set: { name: 'jason bourne' }}, options) + * + * #### Note: + * + * `findOneAndX` and `findByIdAndX` functions support limited validation. You can + * enable validation by setting the `runValidators` option. + * + * If you need full-fledged validation, use the traditional approach of first + * retrieving the document. + * + * const doc = await Model.findById(id) + * doc.name = 'jason bourne'; + * await doc.save(); + * + * @param {object|number|string} id value of `_id` to query by + * @param {object} [update] + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. + * @param {boolean} [options.runValidators] if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema + * @param {boolean} [options.setDefaultsOnInsert=true] If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {boolean} [options.new=false] if true, return the modified document rather than the original + * @param {object|string} [options.select] sets the document fields to return. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @return {Query} + * @see Model.findOneAndUpdate https://mongoosejs.com/docs/api/model.html#Model.findOneAndUpdate() + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @api public + */ + +Model.findByIdAndUpdate = function(id, update, options) { + _checkContext(this, 'findByIdAndUpdate'); + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { + throw new MongooseError('Model.findByIdAndUpdate() no longer accepts a callback'); + } + + // if a model is passed in instead of an id + if (id instanceof Document) { + id = id._doc._id; + } + + return this.findOneAndUpdate.call(this, { _id: id }, update, options); +}; + +/** + * Issue a MongoDB `findOneAndDelete()` command. + * + * Finds a matching document, removes it, and returns the found document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndDelete()` + * + * #### Example: + * + * A.findOneAndDelete(conditions, options) // return Query + * A.findOneAndDelete(conditions) // returns Query + * A.findOneAndDelete() // returns Query + * + * `findOneAndX` and `findByIdAndX` functions support limited validation. You can + * enable validation by setting the `runValidators` option. + * + * If you need full-fledged validation, use the traditional approach of first + * retrieving the document. + * + * const doc = await Model.findById(id) + * doc.name = 'jason bourne'; + * await doc.save(); + * + * @param {object} conditions + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. + * @param {object|string} [options.select] sets the document fields to return. + * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @api public + */ + +Model.findOneAndDelete = function(conditions, options) { + _checkContext(this, 'findOneAndDelete'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.findOneAndDelete() no longer accepts a callback'); + } + + let fields; + if (options) { + fields = options.select; + options.select = undefined; + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.select(fields); + + return mq.findOneAndDelete(conditions, options); +}; + +/** + * Issue a MongoDB `findOneAndDelete()` command by a document's _id field. + * In other words, `findByIdAndDelete(id)` is a shorthand for + * `findOneAndDelete({ _id: id })`. + * + * This function triggers the following middleware. + * + * - `findOneAndDelete()` + * + * @param {object|number|string} id value of `_id` to query by + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @see Model.findOneAndDelete https://mongoosejs.com/docs/api/model.html#Model.findOneAndDelete() + * @see mongodb https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + */ + +Model.findByIdAndDelete = function(id, options) { + _checkContext(this, 'findByIdAndDelete'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.findByIdAndDelete() no longer accepts a callback'); + } + + return this.findOneAndDelete({ _id: id }, options); +}; + +/** + * Issue a MongoDB `findOneAndReplace()` command. + * + * Finds a matching document, replaces it with the provided doc, and returns the document. + * + * This function triggers the following query middleware. + * + * - `findOneAndReplace()` + * + * #### Example: + * + * A.findOneAndReplace(filter, replacement, options) // return Query + * A.findOneAndReplace(filter, replacement) // returns Query + * A.findOneAndReplace() // returns Query + * + * @param {object} filter Replace the first document that matches this filter + * @param {object} [replacement] Replace with this document + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {object|string|string[]} [options.projection=null] optional fields to return, see [`Query.prototype.select()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.select()) + * @param {object|string} [options.sort] if multiple docs are found by the conditions, sets the sort order to choose which doc to update. + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {object|string} [options.select] sets the document fields to return. + * @param {number} [options.maxTimeMS] puts a time limit on the query - requires mongodb >= 2.6.0 + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @api public + */ + +Model.findOneAndReplace = function(filter, replacement, options) { + _checkContext(this, 'findOneAndReplace'); + + if (typeof arguments[0] === 'function' || typeof arguments[1] === 'function' || typeof arguments[2] === 'function' || typeof arguments[3] === 'function') { + throw new MongooseError('Model.findOneAndReplace() no longer accepts a callback'); + } + + let fields; + if (options) { + fields = options.select; + options.select = undefined; + } + + const mq = new this.Query({}, {}, this, this.$__collection); + mq.select(fields); + + return mq.findOneAndReplace(filter, replacement, options); +}; + +/** + * Shortcut for saving one or more documents to the database. + * `MyModel.create(docs)` does `new MyModel(doc).save()` for every doc in + * docs. + * + * This function triggers the following middleware. + * + * - `save()` + * + * #### Example: + * + * // Insert one new `Character` document + * await Character.create({ name: 'Jean-Luc Picard' }); + * + * // Insert multiple new `Character` documents + * await Character.create([{ name: 'Will Riker' }, { name: 'Geordi LaForge' }]); + * + * // Create a new character within a transaction. Note that you **must** + * // pass an array as the first parameter to `create()` if you want to + * // specify options. + * await Character.create([{ name: 'Jean-Luc Picard' }], { session }); + * + * @param {Array|object} docs Documents to insert, as a spread or array + * @param {object} [options] Options passed down to `save()`. To specify `options`, `docs` **must** be an array, not a spread. See [Model.save](https://mongoosejs.com/docs/api/model.html#Model.prototype.save()) for available options. + * @param {boolean} [options.ordered] saves the docs in series rather than parallel. + * @param {boolean} [options.aggregateErrors] Aggregate Errors instead of throwing the first one that occurs. Default: false + * @return {Promise} + * @api public + */ + +Model.create = async function create(doc, options) { + if (typeof options === 'function' || + typeof arguments[2] === 'function') { + throw new MongooseError('Model.create() no longer accepts a callback'); + } + + _checkContext(this, 'create'); + + let args; + const discriminatorKey = this.schema.options.discriminatorKey; + + if (Array.isArray(doc)) { + args = doc; + options = options != null && typeof options === 'object' ? options : {}; + } else { + const last = arguments[arguments.length - 1]; + options = {}; + const hasCallback = typeof last === 'function' || + typeof options === 'function' || + typeof arguments[2] === 'function'; + if (hasCallback) { + throw new MongooseError('Model.create() no longer accepts a callback'); + } else { + args = [...arguments]; + // For backwards compatibility with 6.x, because of gh-5061 Mongoose 6.x and + // older would treat a falsy last arg as a callback. We don't want to throw + // an error here, because it would look strange if `Test.create({}, void 0)` + // threw a callback error. But we also don't want to create an unnecessary document. + if (args.length > 1 && !last) { + args.pop(); + } + } + + if (args.length === 2 && + args[0] != null && + args[1] != null && + args[0].session == null && + last && + getConstructorName(last.session) === 'ClientSession' && + !this.schema.path('session')) { + // Probably means the user is running into the common mistake of trying + // to use a spread to specify options, see gh-7535 + utils.warn('WARNING: to pass a `session` to `Model.create()` in ' + + 'Mongoose, you **must** pass an array as the first argument. See: ' + + 'https://mongoosejs.com/docs/api/model.html#Model.create()'); + } + } + + if (args.length === 0) { + return Array.isArray(doc) ? [] : null; + } + let res = []; + const immediateError = typeof options.aggregateErrors === 'boolean' ? !options.aggregateErrors : true; + + delete options.aggregateErrors; // dont pass on the option to "$save" + + if (options.session && !options.ordered && args.length > 1) { + throw new MongooseError('Cannot call `create()` with a session and multiple documents unless `ordered: true` is set'); + } + + if (!Array.isArray(doc) && args.length === 1) { + let toSave = doc; + + const Model = this.discriminators && doc[discriminatorKey] != null ? + this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) : + this; + if (Model == null) { + throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` + + `found for model "${this.modelName}"`); + } + + if (!(toSave instanceof Model)) { + toSave = new Model(toSave); + } + + await toSave.$save(options); + + return toSave; + } + + if (options.ordered) { + for (let i = 0; i < args.length; i++) { + try { + const doc = args[i]; + const Model = this.discriminators && doc[discriminatorKey] != null ? + this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) : + this; + if (Model == null) { + throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` + + `found for model "${this.modelName}"`); + } + let toSave = doc; + if (!(toSave instanceof Model)) { + toSave = new Model(toSave); + } + + await toSave.$save(options); + res.push(toSave); + } catch (err) { + if (!immediateError) { + res.push(err); + } else { + throw err; + } + } + } + return res; + } else if (!immediateError) { + res = await Promise.allSettled(args.map(async doc => { + const Model = this.discriminators && doc[discriminatorKey] != null ? + this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) : + this; + if (Model == null) { + throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` + + `found for model "${this.modelName}"`); + } + let toSave = doc; + + if (!(toSave instanceof Model)) { + toSave = new Model(toSave); + } + + await toSave.$save(options); + + return toSave; + })); + res = res.map(result => result.status === 'fulfilled' ? result.value : result.reason); + } else { + let firstError = null; + res = await Promise.all(args.map(async doc => { + const Model = this.discriminators && doc[discriminatorKey] != null ? + this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) : + this; + if (Model == null) { + throw new MongooseError(`Discriminator "${doc[discriminatorKey]}" not ` + + `found for model "${this.modelName}"`); + } + try { + let toSave = doc; + + if (!(toSave instanceof Model)) { + toSave = new Model(toSave); + } + + await toSave.$save(options); + + return toSave; + } catch (err) { + if (!firstError) { + firstError = err; + } + } + })); + if (firstError) { + throw firstError; + } + } + + return res; +}; + +/** + * Shortcut for saving one document to the database. + * `MyModel.insertOne(obj, options)` is almost equivalent to `new MyModel(obj).save(options)`. + * The difference is that `insertOne()` checks if `obj` is already a document, and checks for discriminators. + * + * This function triggers the following middleware. + * + * - `save()` + * + * #### Example: + * + * // Insert one new `Character` document + * const character = await Character.insertOne({ name: 'Jean-Luc Picard' }); + * character.name; // 'Jean-Luc Picard' + * + * // Create a new character within a transaction. + * await Character.insertOne({ name: 'Jean-Luc Picard' }, { session }); + * + * @param {object|Document} doc Document to insert, as a POJO or Mongoose document + * @param {object} [options] Options passed down to `save()`. + * @return {Promise} resolves to the saved document + * @api public + */ + +Model.insertOne = async function insertOne(doc, options) { + _checkContext(this, 'insertOne'); + + const discriminatorKey = this.schema.options.discriminatorKey; + const Model = this.discriminators && doc[discriminatorKey] != null ? + this.discriminators[doc[discriminatorKey]] || getDiscriminatorByValue(this.discriminators, doc[discriminatorKey]) : + this; + if (Model == null) { + throw new MongooseError( + `Discriminator "${doc[discriminatorKey]}" not found for model "${this.modelName}"` + ); + } + if (!(doc instanceof Model)) { + doc = new Model(doc); + } + + return await doc.$save(options); +}; + +/** + * _Requires a replica set running MongoDB >= 3.6.0._ Watches the + * underlying collection for changes using + * [MongoDB change streams](https://www.mongodb.com/docs/manual/changeStreams/). + * + * This function does **not** trigger any middleware. In particular, it + * does **not** trigger aggregate middleware. + * + * The ChangeStream object is an event emitter that emits the following events: + * + * - 'change': A change occurred, see below example + * - 'error': An unrecoverable error occurred. In particular, change streams currently error out if they lose connection to the replica set primary. Follow [this GitHub issue](https://github.com/Automattic/mongoose/issues/6799) for updates. + * - 'end': Emitted if the underlying stream is closed + * - 'close': Emitted if the underlying stream is closed + * + * #### Example: + * + * const doc = await Person.create({ name: 'Ned Stark' }); + * const changeStream = Person.watch().on('change', change => console.log(change)); + * // Will print from the above `console.log()`: + * // { _id: { _data: ... }, + * // operationType: 'delete', + * // ns: { db: 'mydb', coll: 'Person' }, + * // documentKey: { _id: 5a51b125c5500f5aa094c7bd } } + * await doc.deleteOne(); + * + * @param {Array} [pipeline] + * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#watch) + * @param {boolean} [options.hydrate=false] if true and `fullDocument: 'updateLookup'` is set, Mongoose will automatically hydrate `fullDocument` into a fully fledged Mongoose document + * @return {ChangeStream} mongoose-specific change stream wrapper, inherits from EventEmitter + * @api public + */ + +Model.watch = function(pipeline, options) { + _checkContext(this, 'watch'); + + options = options || {}; + const watchOptions = options?.hydrate !== undefined ? + utils.omit(options, ['hydrate']) : + { ...options }; + options.model = this; + + + pipeline = pipeline || []; + prepareDiscriminatorPipeline(pipeline, this.schema, 'fullDocument'); + + const changeStreamPromise = this.db._waitForConnect().then( + () => this.$__collection.watch(pipeline, watchOptions) + ); + + return new ChangeStream(changeStreamPromise, pipeline, options); +}; + +/** + * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](https://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + * + * Calling `MyModel.startSession()` is equivalent to calling `MyModel.db.startSession()`. + * + * This function does not trigger any middleware. + * + * #### Example: + * + * const session = await Person.startSession(); + * let doc = await Person.findOne({ name: 'Ned Stark' }, null, { session }); + * await doc.deleteOne(); + * // `doc` will always be null, even if reading from a replica set + * // secondary. Without causal consistency, it is possible to + * // get a doc back from the below query if the query reads from a + * // secondary that is experiencing replication lag. + * doc = await Person.findOne({ name: 'Ned Stark' }, null, { session, readPreference: 'secondary' }); + * + * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#startSession) + * @param {boolean} [options.causalConsistency=true] set to false to disable causal consistency + * @return {Promise} promise that resolves to a MongoDB driver `ClientSession` + * @api public + */ + +Model.startSession = function() { + _checkContext(this, 'startSession'); + + return this.db.startSession.apply(this.db, arguments); +}; + +/** + * Shortcut for validating an array of documents and inserting them into + * MongoDB if they're all valid. This function is faster than `.create()` + * because it only sends one operation to the server, rather than one for each + * document. + * + * Mongoose always validates each document **before** sending `insertMany` + * to MongoDB. So if one document has a validation error, no documents will + * be saved, unless you set + * [the `ordered` option to false](https://www.mongodb.com/docs/manual/reference/method/db.collection.insertMany/#error-handling). + * + * This function does **not** trigger save middleware. + * + * This function triggers the following middleware. + * + * - `insertMany()` + * + * #### Example: + * + * const docs = await Movies.insertMany([ + * { name: 'Star Wars' }, + * { name: 'The Empire Strikes Back' } + * ]); + * docs[0].name; // 'Star Wars' + * + * // Return raw result from MongoDB + * const result = await Movies.insertMany([ + * { name: 'Star Wars' }, + * { name: 'The Empire Strikes Back' } + * ], { rawResult: true }); + * + * @param {Array|object|any} doc(s) + * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#insertMany) + * @param {boolean} [options.ordered=true] if true, will fail fast on the first error encountered. If false, will insert all the documents it can and report errors later. An `insertMany()` with `ordered = false` is called an "unordered" `insertMany()`. + * @param {boolean} [options.rawResult=false] if false, the returned promise resolves to the documents that passed mongoose document validation. If `true`, will return the [raw result from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/InsertManyResult.html) with a `mongoose` property that contains `validationErrors` and `results` if this is an unordered `insertMany`. + * @param {boolean} [options.lean=false] if `true`, skips hydrating the documents. This means Mongoose will **not** cast, validate, or apply defaults to any of the documents passed to `insertMany()`. This option is useful if you need the extra performance, but comes with data integrity risk. Consider using with [`castObject()`](https://mongoosejs.com/docs/api/model.html#Model.castObject()) and [`applyDefaults()`](https://mongoosejs.com/docs/api/model.html#Model.applyDefaults()). + * @param {number} [options.limit=null] this limits the number of documents being processed (validation/casting) by mongoose in parallel, this does **NOT** send the documents in batches to MongoDB. Use this option if you're processing a large number of documents and your app is running out of memory. + * @param {string|object|Array} [options.populate=null] populates the result documents. This option is a no-op if `rawResult` is set. + * @param {boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {Promise} resolving to the raw result from the MongoDB driver if `options.rawResult` was `true`, or the documents that passed validation, otherwise + * @api public + */ + +Model.insertMany = async function insertMany(arr, options) { + _checkContext(this, 'insertMany'); + if (typeof options === 'function' || + typeof arguments[2] === 'function') { + throw new MongooseError('Model.insertMany() no longer accepts a callback'); + } + + options = options || {}; + const preFilter = buildMiddlewareFilter(options, 'pre'); + const postFilter = buildMiddlewareFilter(options, 'post'); + + try { + [arr] = await this._middleware.execPre('insertMany', this, [arr], { filter: preFilter }); + } catch (error) { + await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter }); + } + const ThisModel = this; + const limit = options.limit || 1000; + const rawResult = !!options.rawResult; + const ordered = typeof options.ordered === 'boolean' ? options.ordered : true; + const throwOnValidationError = typeof options.throwOnValidationError === 'boolean' ? options.throwOnValidationError : false; + const lean = !!options.lean; + + const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore(); + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { + options = { ...options, session: asyncLocalStorage.session }; + } + + if (!Array.isArray(arr)) { + arr = [arr]; + } + + const validationErrors = []; + const validationErrorsToOriginalOrder = new Map(); + const results = ordered ? null : new Array(arr.length); + async function validateDoc(doc, index) { + // If option `lean` is set to true bypass validation and hydration + if (lean) { + return doc; + } + let createdNewDoc = false; + if (!(doc instanceof ThisModel)) { + if (doc != null && typeof doc !== 'object') { + throw new ObjectParameterError(doc, 'arr.' + index, 'insertMany'); + } + doc = new ThisModel(doc); + createdNewDoc = true; + } + + if (options.session != null) { + doc.$session(options.session); + } + return doc.$validate(createdNewDoc ? { _skipParallelValidateCheck: true } : null) + .then(() => doc) + .catch(error => { + if (ordered === false) { + error.index = index; + validationErrors.push(error); + validationErrorsToOriginalOrder.set(error, index); + results[index] = error; + return; + } + throw error; + }); + } + + const docs = await parallelLimit(arr, validateDoc, limit); + + const originalDocIndex = new Map(); + const validDocIndexToOriginalIndex = new Map(); + for (let i = 0; i < docs.length; ++i) { + originalDocIndex.set(docs[i], i); + } + + // We filter all failed pre-validations by removing nulls + const docAttributes = docs.filter(function(doc) { + return doc != null; + }); + for (let i = 0; i < docAttributes.length; ++i) { + validDocIndexToOriginalIndex.set(i, originalDocIndex.get(docAttributes[i])); + } + + // Make sure validation errors are in the same order as the + // original documents, so if both doc1 and doc2 both fail validation, + // `Model.insertMany([doc1, doc2])` will always have doc1's validation + // error before doc2's. Re: gh-12791. + if (validationErrors.length > 0) { + validationErrors.sort((err1, err2) => { + return validationErrorsToOriginalOrder.get(err1) - validationErrorsToOriginalOrder.get(err2); + }); + } + + // Quickly escape while there aren't any valid docAttributes + if (docAttributes.length === 0) { + if (throwOnValidationError) { + throw new MongooseBulkWriteError( + validationErrors, + results, + null, + 'insertMany' + ); + } + if (rawResult) { + const res = { + acknowledged: true, + insertedCount: 0, + insertedIds: {} + }; + decorateBulkWriteResult(res, validationErrors, validationErrors); + return res; + } + return []; + } + const docObjects = lean ? docAttributes : docAttributes.map(function(doc) { + if (doc.$__schema.options.versionKey) { + doc[doc.$__schema.options.versionKey] = 0; + } + const shouldSetTimestamps = options?.timestamps !== false && doc.initializeTimestamps && (!doc.$__ || doc.$__.timestamps !== false); + if (shouldSetTimestamps) { + doc.initializeTimestamps(options?.timestamps); + } + if (doc.$__hasOnlyPrimitiveValues()) { + return doc.$__toObjectShallow(); + } + return doc.toObject(internalToObjectOptions); + }); + + let res; + try { + res = await this.$__collection.insertMany(docObjects, options); + } catch (error) { + // `writeErrors` is a property reported by the MongoDB driver, + // just not if there's only 1 error. + if (error.writeErrors == null && + error.result?.result?.writeErrors != null) { + error.writeErrors = error.result.result.writeErrors; + } + + // `insertedDocs` is a Mongoose-specific property + const hasWriteErrors = error?.writeErrors; + const erroredIndexes = new Set((error?.writeErrors || []).map(err => err.index)); + + if (error.writeErrors != null) { + for (let i = 0; i < error.writeErrors.length; ++i) { + const originalIndex = validDocIndexToOriginalIndex.get(error.writeErrors[i].index); + error.writeErrors[i] = { ...error.writeErrors[i], index: originalIndex }; + if (!ordered) { + results[originalIndex] = error.writeErrors[i]; + } + } + } + + if (!ordered) { + for (let i = 0; i < results.length; ++i) { + if (results[i] === void 0) { + results[i] = docs[i]; + } + } + + error.results = results; + } + + let firstErroredIndex = -1; + error.insertedDocs = docAttributes. + filter((doc, i) => { + const isErrored = !hasWriteErrors || erroredIndexes.has(i); + + if (ordered) { + if (firstErroredIndex > -1) { + return i < firstErroredIndex; + } + + if (isErrored) { + firstErroredIndex = i; + } + } + + return !isErrored; + }). + map(function setIsNewForInsertedDoc(doc) { + if (lean) { + return doc; + } + doc.$__reset(); + _setIsNew(doc, false); + return doc; + }); + + if (rawResult && ordered === false) { + decorateBulkWriteResult(error, validationErrors, results); + } + + await this._middleware.execPost('insertMany', this, [arr], { error, filter: postFilter }); + } + + if (!lean) { + for (const attribute of docAttributes) { + attribute.$__reset(); + _setIsNew(attribute, false); + } + } + + if (ordered === false && throwOnValidationError && validationErrors.length > 0) { + for (let i = 0; i < results.length; ++i) { + if (results[i] === void 0) { + results[i] = docs[i]; + } + } + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'insertMany' + ); + } + + if (rawResult) { + if (ordered === false) { + for (let i = 0; i < results.length; ++i) { + if (results[i] === void 0) { + results[i] = docs[i]; + } + } + + // Decorate with mongoose validation errors in case of unordered, + // because then still do `insertMany()` + decorateBulkWriteResult(res, validationErrors, results); + } + return res; + } + + if (options.populate != null) { + return this.populate(docAttributes, options.populate).catch(err => { + if (err != null) { + err.insertedDocs = docAttributes; + } + throw err; + }); + } + + const [result] = await this._middleware.execPost('insertMany', this, [docAttributes], { filter: postFilter }); + return result; +}; + +/*! + * ignore + */ + +function _setIsNew(doc, val) { + doc.$isNew = val; + doc.$emit('isNew', val); + doc.constructor.emit('isNew', val); + + const subdocs = doc.$getAllSubdocs({ useCache: true }); + for (const subdoc of subdocs) { + subdoc.$isNew = val; + subdoc.$emit('isNew', val); + } +} + +/** + * Sends multiple `insertOne`, `updateOne`, `updateMany`, `replaceOne`, + * `deleteOne`, and/or `deleteMany` operations to the MongoDB server in one + * command. This is faster than sending multiple independent operations (e.g. + * if you use `create()`) because with `bulkWrite()` there is only one round + * trip to MongoDB. + * + * Mongoose will perform casting on all operations you provide. + * The only exception is [setting the `update` operator for `updateOne` or `updateMany` to a pipeline](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#updateone-and-updatemany): Mongoose does **not** cast update pipelines. + * + * This function does **not** trigger any middleware, neither `save()`, nor `update()`. + * If you need to trigger + * `save()` middleware for every document use [`create()`](https://mongoosejs.com/docs/api/model.html#Model.create()) instead. + * + * #### Example: + * + * Character.bulkWrite([ + * { + * insertOne: { + * document: { + * name: 'Eddard Stark', + * title: 'Warden of the North' + * } + * } + * }, + * { + * updateOne: { + * filter: { name: 'Eddard Stark' }, + * // If you were using the MongoDB driver directly, you'd need to do + * // `update: { $set: { title: ... } }` but mongoose adds $set for + * // you. + * update: { title: 'Hand of the King' } + * } + * }, + * { + * deleteOne: { + * filter: { name: 'Eddard Stark' } + * } + * } + * ]).then(res => { + * // Prints "1 1 1" + * console.log(res.insertedCount, res.modifiedCount, res.deletedCount); + * }); + * + * // Mongoose does **not** cast update pipelines, so no casting for the `update` option below. + * // Mongoose does still cast `filter` + * await Character.bulkWrite([{ + * updateOne: { + * filter: { name: 'Annika Hansen' }, + * update: [{ $set: { name: 7 } }] // Array means update pipeline, so Mongoose skips casting + * } + * }]); + * + * The [supported operations](https://www.mongodb.com/docs/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) are: + * + * - `insertOne` + * - `updateOne` + * - `updateMany` + * - `deleteOne` + * - `deleteMany` + * - `replaceOne` + * + * @param {Array} ops + * @param {object} [ops.insertOne.document] The document to insert + * @param {object} [ops.insertOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation + * @param {object} [ops.updateOne.filter] Update the first document that matches this filter + * @param {object} [ops.updateOne.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + * @param {boolean} [ops.updateOne.upsert=false] If true, insert a doc if none match + * @param {boolean} [ops.updateOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation + * @param {boolean} [ops.updateOne.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @param {object} [ops.updateOne.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use + * @param {Array} [ops.updateOne.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update` + * @param {object} [ops.updateMany.filter] Update all the documents that match this filter + * @param {object} [ops.updateMany.update] An object containing [update operators](https://www.mongodb.com/docs/manual/reference/operator/update/) + * @param {boolean} [ops.updateMany.upsert=false] If true, insert a doc if no documents match `filter` + * @param {boolean} [ops.updateMany.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation + * @param {boolean} [ops.updateMany.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @param {object} [ops.updateMany.collation] The [MongoDB collation](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-collations) to use + * @param {Array} [ops.updateMany.arrayFilters] The [array filters](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-array-filters.html) used in `update` + * @param {object} [ops.deleteOne.filter] Delete the first document that matches this filter + * @param {object} [ops.deleteMany.filter] Delete all documents that match this filter + * @param {object} [ops.replaceOne.filter] Replace the first document that matches this filter + * @param {object} [ops.replaceOne.replacement] The replacement document + * @param {boolean} [ops.replaceOne.upsert=false] If true, insert a doc if no documents match `filter` + * @param {object} [ops.replaceOne.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to the operation + * @param {object} [options] + * @param {boolean} [options.ordered=true] If true, execute writes in order and stop at the first error. If false, execute writes in parallel and continue until all writes have either succeeded or errored. + * @param {boolean} [options.timestamps=true] If false, do not apply [timestamps](https://mongoosejs.com/docs/guide.html#timestamps) to any operations. Can be overridden at the operation-level. + * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {string|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information. + * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). + * @param {boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option) + * @param {boolean} [options.skipValidation=false] Set to true to skip Mongoose schema validation on bulk write operations. Mongoose currently runs validation on `insertOne` and `replaceOne` operations by default. + * @param {boolean} [options.bypassDocumentValidation=false] If true, disable [MongoDB server-side schema validation](https://www.mongodb.com/docs/manual/core/schema-validation/) for all writes in this bulk. + * @param {boolean} [options.throwOnValidationError=false] If true and `ordered: false`, throw an error if one of the operations failed validation, but all valid operations completed successfully. Note that Mongoose will still send all valid operations to the MongoDB server. + * @param {boolean|"throw"} [options.strict=null] Overwrites the [`strict` option](https://mongoosejs.com/docs/guide.html#strict) on schema. If false, allows filtering and writing fields not defined in the schema for all writes in this bulk. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {Promise} resolves to a [`BulkWriteOpResult`](https://mongodb.github.io/node-mongodb-native/7.0/classes/BulkWriteResult.html) if the operation succeeds + * @api public + */ + +Model.bulkWrite = async function bulkWrite(ops, options) { + _checkContext(this, 'bulkWrite'); + + if (typeof options === 'function' || + typeof arguments[2] === 'function') { + throw new MongooseError('Model.bulkWrite() no longer accepts a callback'); + } + options = options || {}; + const preFilter = buildMiddlewareFilter(options, 'pre'); + const postFilter = buildMiddlewareFilter(options, 'post'); + + try { + [ops, options] = await this.hooks.execPre('bulkWrite', this, [ops, options], { filter: preFilter }); + } catch (err) { + if (err instanceof Kareem.skipWrappedFunction) { + ops = err; + } else { + await this.hooks.execPost('bulkWrite', this, [null], { error: err, filter: postFilter }); + } + } + + if (ops instanceof Kareem.skipWrappedFunction) { + return ops.args[0]; + } + + const ordered = options.ordered == null ? true : options.ordered; + + if (ops.length === 0) { + const BulkWriteResult = this.base.driver.get().BulkWriteResult; + const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false); + bulkWriteResult.n = 0; + decorateBulkWriteResult(bulkWriteResult, [], []); + return bulkWriteResult; + } + + const validations = options?._skipCastBulkWrite ? [] : ops.map(op => castBulkWrite(this, op, options)); + const asyncLocalStorage = this.db.base.transactionAsyncLocalStorage?.getStore(); + if ((!options || !Object.hasOwn(options, 'session')) && asyncLocalStorage?.session != null) { + options = { ...options, session: asyncLocalStorage.session }; + } + + let res = null; + if (ordered) { + await new Promise((resolve, reject) => { + each(validations, (fn, cb) => fn(cb), error => { + if (error) { + return reject(error); + } + + resolve(); + }); + }); + + try { + res = await this.$__collection.bulkWrite(ops, options); + } catch (error) { + await this.hooks.execPost('bulkWrite', this, [null], { error, filter: postFilter }); + } + } else { + let validOpIndexes = []; + let validationErrors = []; + const results = []; + if (validations.length > 0) { + validOpIndexes = await Promise.all(ops.map((op, i) => { + if (i >= validations.length) { + return i; + } + return new Promise((resolve) => { + validations[i]((err) => { + if (err == null) { + resolve(i); + } else { + validationErrors.push({ index: i, error: err }); + results[i] = err; + } + resolve(); + }); + }); + })); + validOpIndexes = validOpIndexes.filter(index => index != null); + } else { + validOpIndexes = ops.map((op, i) => i); + } + + validationErrors = validationErrors. + sort((v1, v2) => v1.index - v2.index). + map(v => v.error); + + const validOps = validOpIndexes.sort((a, b) => a - b).map(index => ops[index]); + + if (validOps.length === 0) { + if (options.throwOnValidationError && validationErrors.length) { + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'bulkWrite' + ); + } + const BulkWriteResult = this.base.driver.get().BulkWriteResult; + const bulkWriteResult = new BulkWriteResult(getDefaultBulkwriteResult(), false); + bulkWriteResult.result = getDefaultBulkwriteResult(); + decorateBulkWriteResult(bulkWriteResult, validationErrors, results); + return bulkWriteResult; + } + + let error; + [res, error] = await this.$__collection.bulkWrite(validOps, options). + then(res => ([res, null])). + catch(error => ([null, error])); + + const writeErrorsByIndex = {}; + if (error?.writeErrors) { + for (const writeError of error.writeErrors) { + writeErrorsByIndex[writeError.err.index] = writeError; + } + } + for (let i = 0; i < validOpIndexes.length; ++i) { + results[validOpIndexes[i]] = writeErrorsByIndex[i] ?? null; + } + if (error) { + if (validationErrors.length > 0) { + decorateBulkWriteResult(error, validationErrors, results); + } + + await this.hooks.execPost('bulkWrite', this, [null], { error, filter: postFilter }); + } + + if (validationErrors.length > 0) { + if (options.throwOnValidationError) { + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'bulkWrite' + ); + } else { + decorateBulkWriteResult(res, validationErrors, results); + } + } + } + + await this.hooks.execPost('bulkWrite', this, [res], { filter: postFilter }); + + return res; +}; + +/** + * Takes an array of documents, gets the changes and inserts/updates documents in the database + * according to whether or not the document is new, or whether it has changes or not. + * + * `bulkSave` uses `bulkWrite` under the hood, so it's mostly useful when dealing with many documents (10K+) + * + * `bulkSave()` throws errors under the following conditions: + * + * - one of the provided documents fails validation. In this case, `bulkSave()` does not send a `bulkWrite()`, and throws the first validation error. + * - `bulkWrite()` fails (for example, due to being unable to connect to MongoDB or due to duplicate key error) + * - `bulkWrite()` did not insert or update **any** documents. In this case, `bulkSave()` will throw a DocumentNotFound error. + * + * Note that `bulkSave()` will **not** throw an error if only some of the `save()` calls succeeded. + * + * @param {Document[]} documents + * @param {object} [options] options passed to the underlying `bulkWrite()` + * @param {boolean} [options.timestamps] defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents. + * @param {ClientSession} [options.session=null] The session associated with this bulk write. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {string|number} [options.w=1] The [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/). See [`Query#w()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()) for more information. + * @param {number} [options.wtimeout=null] The [write concern timeout](https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout). + * @param {boolean} [options.j=true] If false, disable [journal acknowledgement](https://www.mongodb.com/docs/manual/reference/write-concern/#j-option) + * @param {boolean} [options.validateBeforeSave=true] set to `false` to skip Mongoose validation on all documents + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {BulkWriteResult} the return value from `bulkWrite()` + */ +Model.bulkSave = async function bulkSave(documents, options) { + options = options || {}; + + if (options.timestamps != null) { + for (const document of documents) { + document.$__.saveOptions = document.$__.saveOptions || {}; + document.$__.saveOptions.timestamps = options.timestamps; + } + } else { + for (const document of documents) { + if (document.$__.timestamps != null) { + document.$__.saveOptions = document.$__.saveOptions || {}; + document.$__.saveOptions.timestamps = document.$__.timestamps; + } + } + } + + await Promise.all(documents.map(doc => buildPreSavePromise(doc, options))); + + const writeOperations = this.buildBulkWriteOperations(documents, options); + const opts = { skipValidation: true, _skipCastBulkWrite: true, ...options }; + const { bulkWriteResult, bulkWriteError } = await this.bulkWrite(writeOperations, opts).then( + (res) => ({ bulkWriteResult: res, bulkWriteError: null }), + (err) => ({ bulkWriteResult: null, bulkWriteError: err }) + ); + // If not a MongoBulkWriteError, treat this as all documents failed to save. + if (bulkWriteError != null && bulkWriteError.name !== 'MongoBulkWriteError') { + throw bulkWriteError; + } + + const matchedCount = bulkWriteResult?.matchedCount ?? 0; + const insertedCount = bulkWriteResult?.insertedCount ?? 0; + if (writeOperations.length > 0 && matchedCount + insertedCount < writeOperations.length && !bulkWriteError) { + throw new MongooseBulkSaveIncompleteError( + this.modelName, + documents, + bulkWriteResult + ); + } + + const successfulDocuments = []; + for (let i = 0; i < documents.length; i++) { + const document = documents[i]; + const documentError = bulkWriteError?.writeErrors.find(writeError => { + const writeErrorDocumentId = writeError.err.op._id || writeError.err.op.q._id; + return writeErrorDocumentId.toString() === document._doc._id.toString(); + }); + + if (documentError == null) { + successfulDocuments.push(document); + } + } + await Promise.all(successfulDocuments.map(document => handleSuccessfulWrite(document, options))); + + if (bulkWriteError != null) { + throw bulkWriteError; + } + + return bulkWriteResult; +}; + +async function buildPreSavePromise(document, options) { + const preFilter = buildMiddlewareFilter(options, 'pre'); + const [newOptions] = await document.schema.s.hooks.execPre('save', document, [options], { filter: preFilter }); + if (newOptions !== options) { + throw new MongooseError('Cannot overwrite options in pre("save") hook on bulkSave()'); + } +} + +async function handleSuccessfulWrite(document, options) { + if (document.$isNew) { + _setIsNew(document, false); + } + + document.$__reset(); + document._applyVersionIncrement(); + const postFilter = buildMiddlewareFilter(options, 'post'); + return document.schema.s.hooks.execPost('save', document, [document], { filter: postFilter }); +} + +/** + * Apply defaults to the given document or POJO. + * + * @param {object|Document} obj object or document to apply defaults on + * @returns {object|Document} + * @api public + */ + +Model.applyDefaults = function applyDefaults(doc) { + if (doc == null) { + return doc; + } + if (doc.$__ != null) { + applyDefaultsHelper(doc, doc.$__.fields, doc.$__.exclude); + + for (const subdoc of doc.$getAllSubdocs()) { + applyDefaults(subdoc, subdoc.$__.fields, subdoc.$__.exclude); + } + + return doc; + } + + applyDefaultsToPOJO(doc, this.schema); + + return doc; +}; + +/** + * Apply this model's virtuals to a given POJO. Virtuals execute with the POJO as the context `this`. + * + * #### Example: + * + * const userSchema = new Schema({ name: String }); + * userSchema.virtual('upper').get(function() { return this.name.toUpperCase(); }); + * const User = mongoose.model('User', userSchema); + * + * const obj = { name: 'John' }; + * User.applyVirtuals(obj); + * obj.name; // 'John' + * obj.upper; // 'JOHN', Mongoose applied the return value of the virtual to the given object + * + * @param {object} obj object or document to apply virtuals on + * @param {string[]} [virtualsToApply] optional whitelist of virtuals to apply + * @returns {object} obj + * @api public + */ + +Model.applyVirtuals = function applyVirtuals(obj, virtualsToApply) { + if (obj == null) { + return obj; + } + // Nothing to do if this is already a hydrated document - it should already have virtuals + if (obj.$__ != null) { + return obj; + } + + applyVirtualsHelper(this.schema, obj, virtualsToApply); + + return obj; +}; + +/** + * Apply this model's timestamps to a given POJO, including subdocument timestamps + * + * #### Example: + * + * const userSchema = new Schema({ name: String }, { timestamps: true }); + * const User = mongoose.model('User', userSchema); + * + * const obj = { name: 'John' }; + * User.applyTimestamps(obj); + * obj.createdAt; // 2024-06-01T18:00:00.000Z + * obj.updatedAt; // 2024-06-01T18:00:00.000Z + * + * @param {object} obj object or document to apply virtuals on + * @param {object} [options] + * @param {boolean} [options.isUpdate=false] if true, treat this as an update: just set updatedAt, skip setting createdAt. If false, set both createdAt and updatedAt + * @param {Function} [options.currentTime] if set, Mongoose will call this function to get the current time. + * @returns {object} obj + * @api public + */ + +Model.applyTimestamps = function applyTimestamps(obj, options) { + if (obj == null) { + return obj; + } + // Nothing to do if this is already a hydrated document - it should already have timestamps + if (obj.$__ != null) { + return obj; + } + + applyTimestampsHelper(this.schema, obj, options); + + return obj; +}; + +/** + * Cast the given POJO to the model's schema + * + * #### Example: + * + * const Test = mongoose.model('Test', Schema({ num: Number })); + * + * const obj = Test.castObject({ num: '42' }); + * obj.num; // 42 as a number + * + * Test.castObject({ num: 'not a number' }); // Throws a ValidationError + * + * @param {object} obj object or document to cast + * @param {object} options options passed to castObject + * @param {boolean} options.ignoreCastErrors If set to `true` will not throw a ValidationError and only return values that were successfully cast. + * @returns {object} POJO casted to the model's schema + * @throws {ValidationError} if casting failed for at least one path + * @api public + */ + +Model.castObject = function castObject(obj, options) { + options = options || {}; + const ret = {}; + + let schema = this.schema; + const discriminatorKey = schema.options.discriminatorKey; + if (schema.discriminators != null && obj != null && obj[discriminatorKey] != null) { + schema = getSchemaDiscriminatorByValue(schema, obj[discriminatorKey]) || schema; + } + const paths = Object.keys(schema.paths); + + for (const path of paths) { + const schemaType = schema.path(path); + if (!schemaType?.$isMongooseArray) { + continue; + } + + const val = get(obj, path); + pushNestedArrayPaths(paths, val, path); + } + + let error = null; + + for (const path of paths) { + const schemaType = schema.path(path); + if (schemaType == null) { + continue; + } + + let val = get(obj, path, void 0); + + if (val == null) { + continue; + } + + const pieces = path.indexOf('.') === -1 ? [path] : path.split('.'); + let cur = ret; + for (let i = 0; i < pieces.length - 1; ++i) { + if (cur[pieces[i]] == null) { + cur[pieces[i]] = isNaN(pieces[i + 1]) ? {} : []; + } + cur = cur[pieces[i]]; + } + + if (schemaType.$isMongooseDocumentArray) { + const castNonArraysOption = schemaType.options?.castNonArrays ?? schemaType.constructor.options.castNonArrays; + if (!Array.isArray(val)) { + if (!castNonArraysOption) { + if (!options.ignoreCastErrors) { + error = error || new ValidationError(); + error.addError(path, new ObjectExpectedError(path, val)); + } + } else { + cur[pieces[pieces.length - 1]] = [ + Model.castObject.call(schemaType.Constructor, val) + ]; + } + + continue; + } + } + if (schemaType.$isSingleNested || schemaType.$isMongooseDocumentArrayElement) { + try { + val = Model.castObject.call(schemaType.Constructor, val); + } catch (err) { + if (!options.ignoreCastErrors) { + error = error || new ValidationError(); + error.addError(path, err); + } + continue; + } + + cur[pieces[pieces.length - 1]] = val; + continue; + } + + try { + val = schemaType.cast(val); + cur[pieces[pieces.length - 1]] = val; + } catch (err) { + if (!options.ignoreCastErrors) { + error = error || new ValidationError(); + error.addError(path, err); + } + + continue; + } + } + + if (error != null) { + throw error; + } + + return ret; +}; + +/** + * Build bulk write operations for `bulkSave()`. + * + * @param {Document[]} documents The array of documents to build write operations of + * @param {object} options + * @param {boolean} options.skipValidation defaults to `false`, when set to true, building the write operations will bypass validating the documents. + * @param {boolean} options.timestamps defaults to `null`, when set to false, mongoose will not add/update timestamps to the documents. + * @return {Promise[]} Returns a array of all Promises the function executes to be awaited. + * @api private + */ + +Model.buildBulkWriteOperations = function buildBulkWriteOperations(documents, options) { + if (!Array.isArray(documents)) { + throw new MongooseError(`bulkSave expects an array of documents to be passed, received \`${documents}\` instead`); + } + + setDefaultOptions(); + + const writeOperations = documents.map((document, i) => { + if (!options.skipValidation) { + if (!(document instanceof Document)) { + throw new MongooseError(`documents.${i} was not a mongoose document, documents must be an array of mongoose documents (instanceof mongoose.Document).`); + } + if (options.validateBeforeSave == null || options.validateBeforeSave) { + const err = document.validateSync(); + if (err != null) { + throw err; + } + } + } + + const isANewDocument = document.isNew; + if (isANewDocument) { + const writeOperation = { insertOne: { document } }; + utils.injectTimestampsOption(writeOperation.insertOne, options.timestamps); + return writeOperation; + } + + const delta = document.$__delta(); + const isDocumentWithChanges = delta != null && !utils.isEmptyObject(delta[0]); + + if (isDocumentWithChanges) { + const where = document.$__where(delta[0]); + const changes = delta[1]; + + _applyCustomWhere(document, where); + + // If shard key is set, add shard keys to _filter_ condition to right shard is targeted + const shardKey = this.schema.options.shardKey; + if (shardKey) { + const paths = Object.keys(shardKey); + const len = paths.length; + + for (let i = 0; i < len; ++i) { + where[paths[i]] = document[paths[i]]; + } + } + + document.$__version(where, delta); + const writeOperation = { updateOne: { filter: where, update: changes } }; + utils.injectTimestampsOption(writeOperation.updateOne, options.timestamps); + return writeOperation; + } + + return null; + }).filter(op => op !== null); + + return writeOperations; + + + function setDefaultOptions() { + options = options || {}; + if (options.skipValidation == null) { + options.skipValidation = false; + } + } +}; + + +/** + * Shortcut for creating a new Document from existing raw data, pre-saved in the DB. + * The document returned has no paths marked as modified initially. + * + * #### Example: + * + * // hydrate previous data into a Mongoose document + * const mongooseCandy = Candy.hydrate({ _id: '54108337212ffb6d459f854c', type: 'jelly bean' }); + * + * @param {object} obj + * @param {object|string|string[]} [projection] optional projection containing which fields should be selected for this document + * @param {object} [options] optional options + * @param {boolean} [options.setters=false] if true, apply schema setters when hydrating + * @param {boolean} [options.hydratedPopulatedDocs=false] if true, populates the docs if passing pre-populated data + * @param {boolean} [options.virtuals=false] if true, sets any virtuals present on `obj` + * @param {boolean|'throw'} [options.strict=false] configure strict mode for the hydrated document. In particular, if strict is false, fields not in the schema won't be stripped out; if strict is 'throw', `hydrate()` will throw an error if there are any fields that are not in the schema. Defaults to true (silently strip out fields not in the schema). + * @return {Document} document instance + * @api public + */ + +Model.hydrate = function hydrate(obj, projection, options) { + _checkContext(this, 'hydrate'); + + if (options?.virtuals && options?.hydratedPopulatedDocs === false) { + throw new MongooseError('Cannot set `hydratedPopulatedDocs` option to false if `virtuals` option is truthy because `virtuals: true` also sets populated virtuals'); + } + + if (projection != null) { + if (obj?.$__ != null) { + obj = obj.toObject(internalToObjectOptions); + } + projection = parseProjection(projection); + obj = applyProjection(obj, projection); + } + const document = require('./queryHelpers').createModel(this, obj, projection, projection, options); + document.$init(obj, options); + return document; +}; + +/** + * Same as `updateOne()`, except MongoDB will update _all_ documents that match + * `filter` (as opposed to just the first one) regardless of the value of + * the `multi` option. + * + * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')` + * and `post('updateMany')` instead. + * + * #### Example: + * + * const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true }); + * res.matchedCount; // Number of documents matched + * res.modifiedCount; // Number of documents modified + * res.acknowledged; // Boolean indicating the MongoDB server received the operation. This may be false if Mongoose did not send an update to the server because the update was empty. + * res.upsertedId; // null or an id containing a document that had to be upserted. + * res.upsertedCount; // Number indicating how many documents had to be upserted. Will either be 0 or 1. + * + * // Other supported syntaxes + * await Person.find({ name: /Stark$/ }).updateMany({ isDeleted: true }); // Using chaining syntax + * await Person.find().updateMany({ isDeleted: true }); // Set `isDeleted` on _all_ Person documents + * + * This function triggers the following middleware. + * + * - `updateMany()` + * + * @param {object} filter + * @param {object|Array} update. If array, this update will be treated as an update pipeline and not casted. + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @return {Query} + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @api public + */ + +Model.updateMany = function updateMany(conditions, update, options) { + _checkContext(this, 'updateMany'); + + if (update == null) { + throw new MongooseError('updateMany `update` parameter cannot be nullish'); + } + + return _update(this, 'updateMany', conditions, update, options); +}; + +/** + * Update _only_ the first document that matches `filter`. + * + * - Use `replaceOne()` if you want to overwrite an entire document rather than using atomic operators like `$set`. + * + * #### Example: + * + * const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' }); + * res.matchedCount; // Number of documents matched + * res.modifiedCount; // Number of documents modified + * res.acknowledged; // Boolean indicating the MongoDB server received the operation. This may be false if Mongoose did not send an update to the server because the update was empty. + * res.upsertedId; // null or an id containing a document that had to be upserted. + * res.upsertedCount; // Number indicating how many documents had to be upserted. Will either be 0 or 1. + * + * // Other supported syntaxes + * await Person.findOne({ name: 'Jean-Luc Picard' }).updateOne({ ship: 'USS Enterprise' }); // Using chaining syntax + * await Person.updateOne({ ship: 'USS Enterprise' }); // Updates first doc's `ship` property + * + * This function triggers the following middleware. + * + * - `updateOne()` + * + * @param {object} filter + * @param {object|Array} update. If array, this update will be treated as an update pipeline and not casted. + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @return {Query} + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @api public + */ + +Model.updateOne = function updateOne(conditions, doc, options) { + _checkContext(this, 'updateOne'); + + return _update(this, 'updateOne', conditions, doc, options); +}; + +/** + * Replace the existing document with the given document (no atomic operators like `$set`). + * + * #### Example: + * + * const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' }); + * res.matchedCount; // Number of documents matched + * res.modifiedCount; // Number of documents modified + * res.acknowledged; // Boolean indicating the MongoDB server received the operation. + * res.upsertedId; // null or an id containing a document that had to be upserted. + * res.upsertedCount; // Number indicating how many documents had to be upserted. Will either be 0 or 1. + * + * This function triggers the following middleware. + * + * - `replaceOne()` + * + * @param {object} filter + * @param {object} doc + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @return {Query} + * @api public + */ + +Model.replaceOne = function replaceOne(conditions, doc, options) { + _checkContext(this, 'replaceOne'); + + const versionKey = this?.schema?.options?.versionKey || null; + if (versionKey && !doc[versionKey]) { + doc[versionKey] = 0; + } + + return _update(this, 'replaceOne', conditions, doc, options); +}; + +/** + * Common code for `updateOne()`, `updateMany()`, `replaceOne()`, and `update()` + * because they need to do the same thing + * @api private + */ + +function _update(model, op, conditions, doc, options) { + const mq = new model.Query({}, {}, model, model.collection); + + // gh-2406 + // make local deep copy of conditions + if (conditions instanceof Document) { + conditions = conditions.toObject(); + } else { + conditions = clone(conditions); + } + options = typeof options === 'function' ? options : clone(options); + + const versionKey = model?.schema?.options?.versionKey ?? null; + decorateUpdateWithVersionKey(doc, options, versionKey); + + return mq[op](conditions, doc, options); +} + +/** + * Performs [aggregations](https://www.mongodb.com/docs/manual/aggregation/) on the models collection. + * + * The `aggregate` itself is returned. + * + * This function triggers the following middleware. + * + * - `aggregate()` + * + * #### Example: + * + * // Find the max balance of all accounts + * const res = await Users.aggregate([ + * { $group: { _id: null, maxBalance: { $max: '$balance' }}}, + * { $project: { _id: 0, maxBalance: 1 }} + * ]); + * + * console.log(res); // [ { maxBalance: 98000 } ] + * + * // Or use the aggregation pipeline builder. + * const res = await Users.aggregate(). + * group({ _id: null, maxBalance: { $max: '$balance' } }). + * project('-id maxBalance'). + * exec(); + * console.log(res); // [ { maxBalance: 98 } ] + * + * #### Note: + * + * - Mongoose does **not** cast aggregation pipelines to the model's schema because `$project` and `$group` operators allow redefining the "shape" of the documents at any stage of the pipeline, which may leave documents in an incompatible format. You can use the [mongoose-cast-aggregation plugin](https://github.com/AbdelrahmanHafez/mongoose-cast-aggregation) to enable minimal casting for aggregation pipelines. + * - The documents returned are plain javascript objects, not mongoose documents (since any shape of document can be returned). + * + * #### More About Aggregations: + * + * - [Mongoose `Aggregate`](https://mongoosejs.com/docs/api/aggregate.html) + * - [An Introduction to Mongoose Aggregate](https://masteringjs.io/tutorials/mongoose/aggregate) + * - [MongoDB Aggregation docs](https://www.mongodb.com/docs/manual/applications/aggregation/) + * + * @see Aggregate https://mongoosejs.com/docs/api/aggregate.html#Aggregate() + * @see MongoDB https://www.mongodb.com/docs/manual/applications/aggregation/ + * @param {Array} [pipeline] aggregation pipeline as an array of objects + * @param {object} [options] aggregation options + * @return {Aggregate} + * @api public + */ + +Model.aggregate = function aggregate(pipeline, options) { + _checkContext(this, 'aggregate'); + + if (typeof options === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.aggregate() no longer accepts a callback'); + } + + const aggregate = new Aggregate(pipeline || []); + aggregate.model(this); + if (options != null) { + aggregate.option(options); + } + + return aggregate; +}; + +/** + * Casts and validates the given object against this model's schema, passing the + * given `context` to custom validators. + * + * #### Example: + * + * const Model = mongoose.model('Test', Schema({ + * name: { type: String, required: true }, + * age: { type: Number, required: true } + * }); + * + * try { + * await Model.validate({ name: null }, ['name']) + * } catch (err) { + * err instanceof mongoose.Error.ValidationError; // true + * Object.keys(err.errors); // ['name'] + * } + * + * @param {object} obj + * @param {object|Array|string} pathsOrOptions + * @param {object} [context] + * @return {Promise} casted and validated copy of `obj` if validation succeeded + * @api public + */ + +Model.validate = async function validate(obj, pathsOrOptions, context) { + if ((arguments.length < 3) || (arguments.length === 3 && typeof arguments[2] === 'function')) { + // For convenience, if we're validating a document or an object, make `context` default to + // the model so users don't have to always pass `context`, re: gh-10132, gh-10346 + context = obj; + } + if (typeof context === 'function' || typeof arguments[3] === 'function') { + throw new MongooseError('Model.validate() no longer accepts a callback'); + } + + let schema = this.schema; + const discriminatorKey = schema.options.discriminatorKey; + if (schema.discriminators != null && obj != null && obj[discriminatorKey] != null) { + schema = getSchemaDiscriminatorByValue(schema, obj[discriminatorKey]) || schema; + } + let paths = Object.keys(schema.paths); + + if (pathsOrOptions != null) { + const _pathsToValidate = typeof pathsOrOptions === 'string' ? new Set(pathsOrOptions.split(' ')) : Array.isArray(pathsOrOptions) ? new Set(pathsOrOptions) : new Set(paths); + paths = paths.filter(p => { + if (pathsOrOptions.pathsToSkip) { + if (Array.isArray(pathsOrOptions.pathsToSkip)) { + if (pathsOrOptions.pathsToSkip.find(x => x == p)) { + return false; + } + } else if (typeof pathsOrOptions.pathsToSkip == 'string') { + if (pathsOrOptions.pathsToSkip.includes(p)) { + return false; + } + } + } + const pieces = p.split('.'); + let cur = pieces[0]; + + for (const piece of pieces) { + if (_pathsToValidate.has(cur)) { + return true; + } + cur += '.' + piece; + } + + return _pathsToValidate.has(p); + }); + } + + for (const path of paths) { + const schemaType = schema.path(path); + if (!schemaType?.$isMongooseArray || schemaType.$isMongooseDocumentArray) { + continue; + } + + const val = get(obj, path); + pushNestedArrayPaths(paths, val, path); + } + + let error = null; + paths = new Set(paths); + + try { + obj = this.castObject(obj); + } catch (err) { + error = err; + for (const key of Object.keys(error.errors || {})) { + paths.delete(key); + } + } + + const promises = []; + for (const path of paths) { + const schemaType = schema.path(path); + if (schemaType == null) { + continue; + } + + const pieces = path.indexOf('.') === -1 ? [path] : path.split('.'); + let cur = obj; + for (let i = 0; i < pieces.length - 1; ++i) { + cur = cur[pieces[i]]; + } + + const val = get(obj, path, void 0); + promises.push( + schemaType.doValidate(val, context, { path: path }).catch(err => { + error = error || new ValidationError(); + error.addError(path, err); + }) + ); + } + + await Promise.all(promises); + if (error != null) { + throw error; + } + + return obj; +}; + +/** + * Populates document references. + * + * Changed in Mongoose 6: the model you call `populate()` on should be the + * "local field" model, **not** the "foreign field" model. + * + * #### Available top-level options: + * + * - path: space delimited path(s) to populate + * - select: optional fields to select + * - match: optional query conditions to match + * - model: optional name of the model to use for population + * - options: optional query options like sort, limit, etc + * - justOne: optional boolean, if true Mongoose will always set `path` to a document, or `null` if no document was found. If false, Mongoose will always set `path` to an array, which will be empty if no documents are found. Inferred from schema by default. + * - strictPopulate: optional boolean, set to `false` to allow populating paths that aren't in the schema. + * - forceRepopulate: optional boolean, defaults to `true`. Set to `false` to prevent Mongoose from repopulating paths that are already populated + * + * #### Example: + * + * const Dog = mongoose.model('Dog', new Schema({ name: String, breed: String })); + * const Person = mongoose.model('Person', new Schema({ + * name: String, + * pet: { type: mongoose.ObjectId, ref: 'Dog' } + * })); + * + * const pets = await Pet.create([ + * { name: 'Daisy', breed: 'Beagle' }, + * { name: 'Einstein', breed: 'Catalan Sheepdog' } + * ]); + * + * // populate many plain objects + * const users = [ + * { name: 'John Wick', dog: pets[0]._id }, + * { name: 'Doc Brown', dog: pets[1]._id } + * ]; + * await User.populate(users, { path: 'dog', select: 'name' }); + * users[0].dog.name; // 'Daisy' + * users[0].dog.breed; // undefined because of `select` + * + * @param {Document|Array} docs Either a single document or array of documents to populate. + * @param {object|string} options Either the paths to populate or an object specifying all parameters + * @param {string} [options.path=null] The path to populate. + * @param {string|PopulateOptions} [options.populate=null] Recursively populate paths in the populated documents. See [deep populate docs](https://mongoosejs.com/docs/populate.html#deep-populate). + * @param {boolean} [options.retainNullValues=false] By default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries. + * @param {boolean} [options.getters=false] If true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](https://mongoosejs.com/docs/schematypes.html#schematype-options). + * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them. + * @param {object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object. + * @param {boolean} [options.skipInvalidIds=false] By default, Mongoose throws a cast error if `localField` and `foreignField` schemas don't line up. If you enable this option, Mongoose will instead filter out any `localField` properties that cannot be casted to `foreignField`'s schema type. + * @param {number} [options.perDocumentLimit=null] For legacy reasons, `limit` with `populate()` may give incorrect results because it only executes a single query for every document being populated. If you set `perDocumentLimit`, Mongoose will ensure correct `limit` per document by executing a separate query for each document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })` will execute 2 additional queries if `.find()` returns 2 documents. + * @param {boolean} [options.strictPopulate=true] Set to false to allow populating paths that aren't defined in the given model's schema. + * @param {object} [options.options=null] Additional options like `limit` and `lean`. + * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document. + * @param {boolean} [options.forceRepopulate=true] Set to `false` to prevent Mongoose from repopulating paths that are already populated + * @param {boolean} [options.ordered=false] Set to `true` to execute any populate queries one at a time, as opposed to in parallel. Set this option to `true` if populating multiple paths or paths with multiple models in transactions. + * @return {Promise} + * @api public + */ + +Model.populate = async function populate(docs, paths) { + _checkContext(this, 'populate'); + if (typeof paths === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Model.populate() no longer accepts a callback'); + } + // normalized paths + paths = utils.populate(paths); + + if (paths.length === 0) { + return docs; + } + + // each path has its own query options and must be executed separately + if (paths.find(p => p.ordered)) { + // Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on + // one transaction in parallel. + // Note that if _any_ path has `ordered`, we make the top-level populate `ordered` as well. + for (const path of paths) { + await _populatePath(this, docs, path); + } + } else { + // By default, populate in parallel + const promises = []; + for (const path of paths) { + promises.push(_populatePath(this, docs, path)); + } + await Promise.all(promises); + } + + return docs; +}; + +/*! + * Populates `docs` for a single `populateOptions` instance. + */ +const excludeIdReg = /\s?-_id\s?/; +const excludeIdRegGlobal = /\s?-_id\s?/g; + +async function _populatePath(model, docs, populateOptions) { + if (populateOptions.strictPopulate == null) { + if (populateOptions._localModel?.schema._userProvidedOptions.strictPopulate != null) { + populateOptions.strictPopulate = populateOptions._localModel.schema._userProvidedOptions.strictPopulate; + } else if (populateOptions._localModel != null && model.base.options.strictPopulate != null) { + populateOptions.strictPopulate = model.base.options.strictPopulate; + } else if (model.base.options.strictPopulate != null) { + populateOptions.strictPopulate = model.base.options.strictPopulate; + } + } + + // normalize single / multiple docs passed + if (!Array.isArray(docs)) { + docs = [docs]; + } + if (docs.length === 0 || docs.every(utils.isNullOrUndefined)) { + return; + } + + const modelsMap = getModelsMapForPopulate(model, docs, populateOptions); + if (modelsMap instanceof MongooseError) { + throw modelsMap; + } + const len = modelsMap.length; + let vals = []; + + function flatten(item) { + // no need to include undefined values in our query + return undefined !== item; + } + + let hasOne = false; + const params = []; + for (let i = 0; i < len; ++i) { + const mod = modelsMap[i]; + let select = mod.options.select; + let ids = utils.array.flatten(mod.ids, flatten); + ids = utils.array.unique(ids); + + const assignmentOpts = {}; + assignmentOpts.sort = mod && + mod.options && + mod.options.options && + mod.options.options.sort || void 0; + assignmentOpts.excludeId = excludeIdReg.test(select) || (select && select._id === 0); + + // Lean transform may delete `_id`, which would cause assignment + // to fail. So delay running lean transform until _after_ + // `_assign()` + if (mod.options && + mod.options.options && + mod.options.options.lean && + mod.options.options.lean.transform) { + mod.options.options._leanTransform = mod.options.options.lean.transform; + mod.options.options.lean = true; + } + + if (ids.length === 0 || ids.every(utils.isNullOrUndefined)) { + // Ensure that we set to 0 or empty array even + // if we don't actually execute a query to make sure there's a value + // and we know this path was populated for future sets. See gh-7731, gh-8230 + _assign(model, [], mod, assignmentOpts); + continue; + } + + hasOne = true; + if (typeof populateOptions.foreignField === 'string') { + mod.foreignField.clear(); + mod.foreignField.add(populateOptions.foreignField); + } + const match = createPopulateQueryFilter(ids, mod.match, mod.foreignField, mod.model, mod.options.skipInvalidIds); + if (assignmentOpts.excludeId) { + // override the exclusion from the query so we can use the _id + // for document matching during assignment. we'll delete the + // _id back off before returning the result. + if (typeof select === 'string') { + select = select.replace(excludeIdRegGlobal, ' '); + } else if (Array.isArray(select)) { + select = select.filter(field => field !== '-_id'); + } else { + // preserve original select conditions by copying + select = { ...select }; + delete select._id; + } + } + + if (mod.options.options?.limit != null) { + assignmentOpts.originalLimit = mod.options.options.limit; + } else if (mod.options.limit != null) { + assignmentOpts.originalLimit = mod.options.limit; + } + params.push([mod, match, select, assignmentOpts]); + } + if (!hasOne) { + // If models but no docs, skip further deep populate. + if (modelsMap.length !== 0) { + return; + } + // If no models and no docs to populate but we have a nested populate, + // probably a case of unnecessarily populating a non-ref path re: gh-8946 + if (populateOptions.populate != null) { + const opts = utils.populate(populateOptions.populate).map(pop => Object.assign({}, pop, { + path: populateOptions.path + '.' + pop.path + })); + return model.populate(docs, opts); + } + return; + } + + // Track deferred populates per-param (per model) to avoid mixing them + const deferredPopulatesPerParam = new Map(); + + if (populateOptions.ordered) { + // Populate in series, primarily for transactions because MongoDB doesn't support multiple operations on + // one transaction in parallel. + for (let i = 0; i < params.length; i++) { + const arr = params[i]; + const { docs, deferredPopulates } = await _execPopulateQuery.apply(null, arr); + vals = vals.concat(docs); + if (deferredPopulates.length > 0) { + deferredPopulatesPerParam.set(i, deferredPopulates); + } + } + } else { + // By default, populate in parallel + const promises = []; + for (const arr of params) { + promises.push(_execPopulateQuery.apply(null, arr)); + } + + const results = await Promise.all(promises); + for (let i = 0; i < results.length; i++) { + const { docs, deferredPopulates } = results[i]; + vals = vals.concat(docs); + if (deferredPopulates.length > 0) { + deferredPopulatesPerParam.set(i, deferredPopulates); + } + } + } + + + for (const arr of params) { + const mod = arr[0]; + const assignmentOpts = arr[3]; + for (const val of vals) { + mod.options._childDocs.push(val); + } + _assign(model, vals, mod, assignmentOpts); + } + + // Handle deferred populate for cases with per-document match functions. + // We defer populate so sift filtering can compare ObjectIds (not populated docs). + // This handles both explicit sub-populate and populate added by pre('find') hooks. + // Each param's deferred populates are tracked separately to avoid mixing them + // between different models (e.g., when using refPath with multiple model types). + if (deferredPopulatesPerParam.size > 0) { + for (let i = 0; i < params.length; i++) { + const arr = params[i]; + const mod = arr[0]; + if (!Array.isArray(mod.match)) { + continue; + } + + const paramDeferredPopulates = deferredPopulatesPerParam.get(i); + if (!paramDeferredPopulates || paramDeferredPopulates.length === 0) { + continue; + } + + // Get the assigned (filtered) children from parent docs + const childDocsToPopulate = []; + for (const doc of mod.docs) { + const childVal = doc.$__ != null ? doc.get(mod.options.path) : doc[mod.options.path]; + if (Array.isArray(childVal)) { + childDocsToPopulate.push(...childVal); + } else if (childVal != null) { + childDocsToPopulate.push(childVal); + } + } + + if (childDocsToPopulate.length > 0) { + for (const pop of paramDeferredPopulates) { + await mod.model.populate(childDocsToPopulate, pop); + } + } + } + } + + for (const arr of params) { + removeDeselectedForeignField(arr[0].foreignField, arr[0].options, vals); + } + for (const arr of params) { + const mod = arr[0]; + if (mod.options?.options?._leanTransform) { + for (const doc of vals) { + mod.options.options._leanTransform(doc); + } + } + } +} + +/*! + * ignore + */ + +function _execPopulateQuery(mod, match, select) { + let subPopulate = clone(mod.options.populate); + const queryOptions = {}; + if (mod.options.skip !== undefined) { + queryOptions.skip = mod.options.skip; + } + if (mod.options.limit !== undefined) { + queryOptions.limit = mod.options.limit; + } + if (mod.options.perDocumentLimit !== undefined) { + queryOptions.perDocumentLimit = mod.options.perDocumentLimit; + } + Object.assign(queryOptions, mod.options.options); + + if (mod.count) { + delete queryOptions.skip; + } + + if (queryOptions.perDocumentLimit != null) { + queryOptions.limit = queryOptions.perDocumentLimit; + delete queryOptions.perDocumentLimit; + } else if (queryOptions.limit != null) { + queryOptions.limit = queryOptions.limit * mod.ids.length; + } + + // When there's a per-document match function, defer any populate (including from hooks) + // until after sift filtering, otherwise sift compares ObjectIds against populated docs. + if (Array.isArray(mod.match)) { + queryOptions._deferPopulate = true; + } + + const query = mod.model.find(match, select, queryOptions); + // If we're doing virtual populate and projection is inclusive and foreign + // field is not selected, automatically select it because mongoose needs it. + // If projection is exclusive and client explicitly unselected the foreign + // field, that's the client's fault. + for (const foreignField of mod.foreignField) { + if (foreignField !== '_id' && + query.selectedInclusively() && + !isPathSelectedInclusive(query._fields, foreignField)) { + query.select(foreignField); + } + } + + // If using count, still need the `foreignField` so we can match counts + // to documents, otherwise we would need a separate `count()` for every doc. + if (mod.count) { + for (const foreignField of mod.foreignField) { + query.select(foreignField); + } + } + + // If we need to sub-populate, call populate recursively. + // However, if we have a per-document match function (Array.isArray(mod.match)), + // defer sub-populate until after sift filtering, otherwise sift compares + // ObjectIds against already-populated docs and fails to match. + const shouldDeferSubPopulate = subPopulate && Array.isArray(mod.match); + + // Prepare sub-populate options with necessary metadata (_fullPath, strictPopulate, _localModel). + // This must be done for both immediate and deferred sub-populate cases. + if (subPopulate) { + // If subpopulating on a discriminator, skip check for non-existent + // paths. Because the discriminator may not have the path defined. + if (mod.model.baseModelName != null) { + if (Array.isArray(subPopulate)) { + subPopulate.forEach(pop => { pop.strictPopulate = false; }); + } else if (typeof subPopulate === 'string') { + subPopulate = { path: subPopulate, strictPopulate: false }; + } else { + subPopulate.strictPopulate = false; + } + } + const basePath = mod.options._fullPath || mod.options.path; + + if (Array.isArray(subPopulate)) { + for (const pop of subPopulate) { + pop._fullPath = basePath + '.' + pop.path; + // Set _localModel for deferred populates so strictPopulate works correctly + if (shouldDeferSubPopulate && pop._localModel == null) { + pop._localModel = mod.model; + } + } + } else if (typeof subPopulate === 'object') { + subPopulate._fullPath = basePath + '.' + subPopulate.path; + // Set _localModel for deferred populates so strictPopulate works correctly + if (shouldDeferSubPopulate && subPopulate._localModel == null) { + subPopulate._localModel = mod.model; + } + } + } + + if (subPopulate && !shouldDeferSubPopulate) { + query.populate(subPopulate); + } + + return query.exec().then( + docs => { + for (const val of docs) { + leanPopulateMap.set(val, mod.model); + } + // Return both docs and any deferred populates (from hooks or explicit sub-populate) + const deferredPopulates = []; + if (query._deferredPopulate) { + deferredPopulates.push(...query._deferredPopulate); + delete query._deferredPopulate; + } + if (shouldDeferSubPopulate) { + deferredPopulates.push(subPopulate); + } + return { docs, deferredPopulates }; + } + ); +} + +/*! + * ignore + */ + +function _assign(model, vals, mod, assignmentOpts) { + const options = mod.options; + const isVirtual = mod.isVirtual; + const justOne = mod.justOne; + let _val; + const lean = options && + options.options && + options.options.lean || false; + const len = vals.length; + const rawOrder = {}; + const rawDocs = {}; + let key; + let val; + + // Clone because `assignRawDocsToIdStructure` will mutate the array + const allIds = clone(mod.allIds); + // optimization: + // record the document positions as returned by + // the query result. + for (let i = 0; i < len; i++) { + val = vals[i]; + if (val == null) { + continue; + } + for (const foreignField of mod.foreignField) { + _val = utils.getValue(foreignField, val); + if (Array.isArray(_val)) { + _val = utils.array.unique(utils.array.flatten(_val)); + + for (let __val of _val) { + if (__val instanceof Document) { + __val = __val._doc._id; + } + key = String(__val); + if (rawDocs[key]) { + if (Array.isArray(rawDocs[key])) { + rawDocs[key].push(val); + rawOrder[key].push(i); + } else { + rawDocs[key] = [rawDocs[key], val]; + rawOrder[key] = [rawOrder[key], i]; + } + } else { + if (isVirtual && !justOne) { + rawDocs[key] = [val]; + rawOrder[key] = [i]; + } else { + rawDocs[key] = val; + rawOrder[key] = i; + } + } + } + } else { + if (_val instanceof Document) { + _val = _val._doc._id; + } + key = String(_val); + if (rawDocs[key]) { + if (Array.isArray(rawDocs[key])) { + rawDocs[key].push(val); + rawOrder[key].push(i); + } else if (isVirtual || + rawDocs[key].constructor !== val.constructor || + (rawDocs[key] instanceof Document ? String(rawDocs[key]._doc._id) : String(rawDocs[key]._id)) !== (val instanceof Document ? String(val._doc._id) : String(val._id))) { + // May need to store multiple docs with the same id if there's multiple models + // if we have discriminators or a ref function. But avoid converting to an array + // if we have multiple queries on the same model because of `perDocumentLimit` re: gh-9906 + rawDocs[key] = [rawDocs[key], val]; + rawOrder[key] = [rawOrder[key], i]; + } + } else { + rawDocs[key] = val; + rawOrder[key] = i; + } + } + // flag each as result of population + if (!lean) { + val.$__.wasPopulated = val.$__.wasPopulated || { value: _val }; + } + } + } + + assignVals({ + originalModel: model, + // If virtual, make sure to not mutate original field + rawIds: mod.isVirtual ? allIds : mod.allIds, + allIds: allIds, + unpopulatedValues: mod.unpopulatedValues, + foreignField: mod.foreignField, + rawDocs: rawDocs, + rawOrder: rawOrder, + docs: mod.docs, + path: options.path, + options: assignmentOpts, + justOne: mod.justOne, + isVirtual: mod.isVirtual, + allOptions: mod, + populatedModel: mod.model, + lean: lean, + virtual: mod.virtual, + count: mod.count, + match: mod.match + }); +} + +/** + * Compiler utility. + * + * @param {string|Function} name model name or class extending Model + * @param {Schema} schema + * @param {string} collectionName + * @param {Connection} connection + * @param {Mongoose} base mongoose instance + * @api private + */ + +Model.compile = function compile(name, schema, collectionName, connection, base) { + const versioningEnabled = schema.options.versionKey !== false; + + if (versioningEnabled && !schema.paths[schema.options.versionKey]) { + // add versioning to top level documents only + const o = {}; + o[schema.options.versionKey] = Number; + schema.add(o); + } + let model; + if (typeof name === 'function' && name.prototype instanceof Model) { + model = name; + name = model.name; + schema.loadClass(model, false); + model.prototype.$isMongooseModelPrototype = true; + } else { + // generate new class + model = function model(doc, fields, skipId) { + model.hooks.execPreSync('createModel', doc); + if (!(this instanceof model)) { + return new model(doc, fields, skipId); + } + const discriminatorKey = model.schema.options.discriminatorKey; + + if (model.discriminators == null || doc == null || doc[discriminatorKey] == null) { + Model.call(this, doc, fields, skipId); + return; + } + + // If discriminator key is set, use the discriminator instead (gh-7586) + const Discriminator = model.discriminators[doc[discriminatorKey]] || + getDiscriminatorByValue(model.discriminators, doc[discriminatorKey]); + if (Discriminator != null) { + return new Discriminator(doc, fields, skipId); + } + + // Otherwise, just use the top-level model + Model.call(this, doc, fields, skipId); + }; + } + + model.hooks = schema.s.hooks.clone(); + model.base = base; + model.modelName = name; + + if (!(model.prototype instanceof Model)) { + Object.setPrototypeOf(model, Model); + Object.setPrototypeOf(model.prototype, Model.prototype); + } + model.model = function model(name) { + return this.db.model(name); + }; + + model.db = connection; + model.prototype.db = connection; + model.prototype[modelDbSymbol] = connection; + model.discriminators = model.prototype.discriminators = undefined; + model[modelSymbol] = true; + model.events = new EventEmitter(); + + schema._preCompile(); + const _userProvidedOptions = schema._userProvidedOptions || {}; + + const collectionOptions = { + schemaUserProvidedOptions: _userProvidedOptions, + capped: schema.options.capped, + Promise: model.base.Promise, + modelName: name + }; + if (schema.options.autoCreate !== void 0) { + collectionOptions.autoCreate = schema.options.autoCreate; + } + + const collection = connection.collection( + collectionName, + collectionOptions + ); + + model.prototype.collection = collection; + model.prototype.$collection = collection; + model.prototype[modelCollectionSymbol] = collection; + + model.prototype.$__setSchema(schema); + + // apply methods and statics + applyMethods(model, schema); + applyStatics(model, schema); + applyHooks(model, schema); + applyStaticHooks(model, schema.s.hooks, schema.statics); + + model.schema = model.prototype.$__schema; + model.collection = collection; + model.$__collection = collection; + + // Create custom query constructor + model.Query = function() { + Query.apply(this, arguments); + }; + Object.setPrototypeOf(model.Query.prototype, Query.prototype); + model.Query.base = Query.base; + model.Query.prototype.constructor = Query; + model._applyQueryMiddleware(); + applyQueryMethods(model, schema.query); + + return model; +}; + +/** + * If auto encryption is enabled, returns a ClientEncryption instance that is configured with the same settings that + * Mongoose's underlying MongoClient is using. If the client has not yet been configured, returns null. + * + * @returns {ClientEncryption | null} + */ +Model.clientEncryption = function clientEncryption() { + const ClientEncryption = this.base.driver.get().ClientEncryption; + if (!ClientEncryption) { + throw new MongooseError('The mongodb driver must be used to obtain a ClientEncryption object.'); + } + + const client = this.collection?.conn?.client; + + if (!client) return null; + + const autoEncryptionOptions = client.options.autoEncryption; + + if (!autoEncryptionOptions) return null; + + const { + keyVaultNamespace, + keyVaultClient, + kmsProviders, + credentialProviders, + proxyOptions, + tlsOptions + } = autoEncryptionOptions; + return new ClientEncryption(keyVaultClient ?? client, + { keyVaultNamespace, kmsProviders, credentialProviders, proxyOptions, tlsOptions } + ); +}; + +/** + * Update this model to use the new connection, including updating all internal + * references and creating a new `Collection` instance using the new connection. + * Not for external use, only used by `setDriver()` to ensure that you can still + * call `setDriver()` after creating a model using `mongoose.model()`. + * + * @param {Connection} newConnection the new connection to use + * @api private + */ + +Model.$__updateConnection = function $__updateConnection(newConnection) { + this.db = newConnection; + this.prototype.db = newConnection; + this.prototype[modelDbSymbol] = newConnection; + + const collection = newConnection.collection( + this.collection.collectionName, + this.collection.opts + ); + + this.prototype.collection = collection; + this.prototype.$collection = collection; + this.prototype[modelCollectionSymbol] = collection; + + this.collection = collection; + this.$__collection = collection; +}; + +/** + * Register custom query methods for this model + * + * @param {Model} model + * @param {Schema} schema + * @api private + */ + +function applyQueryMethods(model, methods) { + for (const i in methods) { + model.Query.prototype[i] = methods[i]; + } +} + +/** + * Subclass this model with `conn`, `schema`, and `collection` settings. + * + * @param {Connection} conn + * @param {Schema} [schema] + * @param {string} [collection] + * @return {Model} + * @api private + * @memberOf Model + * @static + * @method __subclass + */ + +Model.__subclass = function subclass(conn, schema, collection) { + // subclass model using this connection and collection name + const _this = this; + + const Model = function Model(doc, fields, skipId) { + if (!(this instanceof Model)) { + return new Model(doc, fields, skipId); + } + _this.call(this, doc, fields, skipId); + }; + + Object.setPrototypeOf(Model, _this); + Object.setPrototypeOf(Model.prototype, _this.prototype); + Model.db = conn; + Model.prototype.db = conn; + Model.prototype[modelDbSymbol] = conn; + + _this[subclassedSymbol] = _this[subclassedSymbol] || []; + _this[subclassedSymbol].push(Model); + if (_this.discriminators != null) { + Model.discriminators = {}; + for (const key of Object.keys(_this.discriminators)) { + Model.discriminators[key] = _this.discriminators[key]. + __subclass(_this.db, _this.discriminators[key].schema, collection); + } + } + + const s = schema && typeof schema !== 'string' + ? schema + : _this.prototype.$__schema; + + const options = s.options || {}; + const _userProvidedOptions = s._userProvidedOptions || {}; + + if (!collection) { + collection = _this.prototype.$__schema.get('collection') || + utils.toCollectionName(_this.modelName, this.base.pluralize()); + } + + const collectionOptions = { + schemaUserProvidedOptions: _userProvidedOptions, + capped: s && options.capped + }; + + Model.prototype.collection = conn.collection(collection, collectionOptions); + Model.prototype.$collection = Model.prototype.collection; + Model.prototype[modelCollectionSymbol] = Model.prototype.collection; + Model.collection = Model.prototype.collection; + Model.$__collection = Model.collection; + // Errors handled internally, so ignore + Model.init().catch(() => {}); + return Model; +}; + +/** + * Apply changes made to this model's schema after this model was compiled. + * By default, adding virtuals and other properties to a schema after the model is compiled does nothing. + * Call this function to apply virtuals and properties that were added later. + * + * #### Example: + * + * const schema = new mongoose.Schema({ field: String }); + * const TestModel = mongoose.model('Test', schema); + * TestModel.schema.virtual('myVirtual').get(function() { + * return this.field + ' from myVirtual'; + * }); + * const doc = new TestModel({ field: 'Hello' }); + * doc.myVirtual; // undefined + * + * TestModel.recompileSchema(); + * doc.myVirtual; // 'Hello from myVirtual' + * + * @return {undefined} + * @api public + * @memberOf Model + * @static + * @method recompileSchema + */ + +Model.recompileSchema = function recompileSchema() { + this.prototype.$__setSchema(this.schema); + + if (this.schema._applyDiscriminators != null) { + for (const disc of this.schema._applyDiscriminators.keys()) { + this.discriminator(disc, this.schema._applyDiscriminators.get(disc)); + } + } + + delete this.schema._defaultToObjectOptionsMap; + + applyEmbeddedDiscriminators(this.schema, new WeakSet(), true); +}; + +/** + * Helper for console.log. Given a model named 'MyModel', returns the string + * `'Model { MyModel }'`. + * + * #### Example: + * + * const MyModel = mongoose.model('Test', Schema({ name: String })); + * MyModel.inspect(); // 'Model { Test }' + * console.log(MyModel); // Prints 'Model { Test }' + * + * @api public + */ + +Model.inspect = function() { + return `Model { ${this.modelName} }`; +}; + +/** + * Return the MongoDB namespace for this model as a string. The namespace is the database name, followed by '.', followed by the collection name. + * + * #### Example: + * + * const conn = mongoose.createConnection('mongodb://127.0.0.1:27017/mydb'); + * const TestModel = conn.model('Test', mongoose.Schema({ name: String })); + * + * TestModel.namespace(); // 'mydb.tests' + * + * @api public + */ + +Model.namespace = function namespace() { + return this.db.name + '.' + this.collection.collectionName; +}; + +if (util.inspect.custom) { + // Avoid Node deprecation warning DEP0079 + Model[util.inspect.custom] = Model.inspect; +} + +/*! + * Applies query middleware from this model's schema to this model's + * Query constructor. + */ + +Model._applyQueryMiddleware = function _applyQueryMiddleware() { + const Query = this.Query; + const queryMiddleware = this.schema.s.hooks.filter(hook => { + const contexts = _getContexts(hook); + if (hook.name === 'validate') { + return !!contexts.query; + } + if (hook.name === 'deleteOne' || hook.name === 'updateOne') { + return !!contexts.query || utils.hasOwnKeys(contexts) === false; + } + if (hook.query != null || hook.document != null) { + return !!hook.query; + } + return true; + }); + + Query.prototype._queryMiddleware = queryMiddleware; +}; + +function _getContexts(hook) { + const ret = {}; + if (Object.hasOwn(hook, 'query')) { + ret.query = hook.query; + } + if (Object.hasOwn(hook, 'document')) { + ret.document = hook.document; + } + return ret; +} + +/*! + * Module exports. + */ + +module.exports = exports = Model; diff --git a/backend/node_modules/mongoose/lib/modifiedPathsSnapshot.js b/backend/node_modules/mongoose/lib/modifiedPathsSnapshot.js new file mode 100644 index 0000000..54d6b30 --- /dev/null +++ b/backend/node_modules/mongoose/lib/modifiedPathsSnapshot.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = class ModifiedPathsSnapshot { + constructor(subdocSnapshot, activePaths, version) { + this.subdocSnapshot = subdocSnapshot; + this.activePaths = activePaths; + this.version = version; + } +}; diff --git a/backend/node_modules/mongoose/lib/mongoose.js b/backend/node_modules/mongoose/lib/mongoose.js new file mode 100644 index 0000000..1f5d1a3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/mongoose.js @@ -0,0 +1,1435 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const Document = require('./document'); +const EventEmitter = require('events').EventEmitter; +const Kareem = require('kareem'); +const Schema = require('./schema'); +const SchemaType = require('./schemaType'); +const SchemaTypes = require('./schema/index'); +const VirtualType = require('./virtualType'); +const STATES = require('./connectionState'); +const VALID_OPTIONS = require('./validOptions'); +const Types = require('./types'); +const Query = require('./query'); +const Model = require('./model'); +const applyPlugins = require('./helpers/schema/applyPlugins'); +const builtinPlugins = require('./plugins'); +const driver = require('./driver'); +const legacyPluralize = require('./helpers/pluralize'); +const utils = require('./utils'); +const pkg = require('../package.json'); +const cast = require('./cast'); + +const Aggregate = require('./aggregate'); +const trusted = require('./helpers/query/trusted').trusted; +const sanitizeFilter = require('./helpers/query/sanitizeFilter'); +const isBsonType = require('./helpers/isBsonType'); +const MongooseError = require('./error/mongooseError'); +const SetOptionError = require('./error/setOptionError'); +const applyEmbeddedDiscriminators = require('./helpers/discriminator/applyEmbeddedDiscriminators'); + +const defaultMongooseSymbol = Symbol.for('mongoose:default'); +const defaultConnectionSymbol = Symbol('mongoose:defaultConnection'); + +require('./helpers/printJestWarning'); + +const objectIdHexRegexp = /^[0-9A-Fa-f]{24}$/; + +const { AsyncLocalStorage } = require('async_hooks'); + +/** + * Mongoose constructor. + * + * The exports object of the `mongoose` module is an instance of this class. + * Most apps will only use this one instance. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * mongoose instanceof mongoose.Mongoose; // true + * + * // Create a new Mongoose instance with its own `connect()`, `set()`, `model()`, etc. + * const m = new mongoose.Mongoose(); + * + * @api public + * @param {object} options see [`Mongoose#set()` docs](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.set()) + */ +function Mongoose(options) { + this.connections = []; + this.nextConnectionId = 0; + this.models = {}; + this.events = new EventEmitter(); + this.__driver = driver.get(); + // default global options + this.options = Object.assign({ + pluralization: true, + autoIndex: true, + autoCreate: true, + autoSearchIndex: false + }, options); + const createInitialConnection = utils.getOption('createInitialConnection', this.options) ?? true; + if (createInitialConnection && this.__driver != null) { + _createDefaultConnection(this); + } + + if (this.options.pluralization) { + this._pluralize = legacyPluralize; + } + + // If a user creates their own Mongoose instance, give them a separate copy + // of the `Schema` constructor so they get separate custom types. (gh-6933) + if (!options?.[defaultMongooseSymbol]) { + const _this = this; + this.Schema = function() { + this.base = _this; + return Schema.apply(this, arguments); + }; + this.Schema.prototype = Object.create(Schema.prototype); + + Object.assign(this.Schema, Schema); + this.Schema.base = this; + this.Schema.Types = Object.assign({}, Schema.Types); + } else { + // Hack to work around babel's strange behavior with + // `import mongoose, { Schema } from 'mongoose'`. Because `Schema` is not + // an own property of a Mongoose global, Schema will be undefined. See gh-5648 + for (const key of ['Schema', 'model']) { + this[key] = Mongoose.prototype[key]; + } + } + this.Schema.prototype.base = this; + + if (options?.transactionAsyncLocalStorage) { + this.transactionAsyncLocalStorage = new AsyncLocalStorage(); + } + + Object.defineProperty(this, 'plugins', { + configurable: false, + enumerable: true, + writable: false, + value: Object.values(builtinPlugins).map(plugin => ([plugin, { deduplicate: true }])) + }); +} + +Mongoose.prototype.cast = cast; +/** + * Expose connection states for user-land + * + * @memberOf Mongoose + * @property STATES + * @api public + */ +Mongoose.prototype.STATES = STATES; + +/** + * Expose connection states for user-land + * + * @memberOf Mongoose + * @property ConnectionStates + * @api public + */ +Mongoose.prototype.ConnectionStates = STATES; + +/** + * Object with `get()` and `set()` containing the underlying driver this Mongoose instance + * uses to communicate with the database. A driver is a Mongoose-specific interface that defines functions + * like `find()`. + * + * @deprecated + * @memberOf Mongoose + * @property driver + * @api public + */ + +Mongoose.prototype.driver = driver; + +/** + * Overwrites the current driver used by this Mongoose instance. A driver is a + * Mongoose-specific interface that defines functions like `find()`. + * + * @memberOf Mongoose + * @method setDriver + * @api public + */ + +Mongoose.prototype.setDriver = function setDriver(driver) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + if (_mongoose.__driver === driver) { + return _mongoose; + } + + const openConnection = _mongoose.connections && _mongoose.connections.find(conn => conn.readyState !== STATES.disconnected); + if (openConnection) { + const msg = 'Cannot modify Mongoose driver if a connection is already open. ' + + 'Call `mongoose.disconnect()` before modifying the driver'; + throw new MongooseError(msg); + } + _mongoose.__driver = driver; + + if (Array.isArray(driver.plugins)) { + for (const plugin of driver.plugins) { + if (typeof plugin === 'function') { + _mongoose.plugin(plugin); + } + } + } + + if (driver.SchemaTypes != null) { + Object.assign(mongoose.Schema.Types, driver.SchemaTypes); + } + + const Connection = driver.Connection; + const oldDefaultConnection = _mongoose.connections[0]; + _mongoose.connections = [new Connection(_mongoose)]; + _mongoose.connections[0].models = _mongoose.models; + if (oldDefaultConnection == null) { + return _mongoose; + } + + // Update all models that pointed to the old default connection to + // the new default connection, including collections + for (const model of Object.values(_mongoose.models)) { + if (model.db !== oldDefaultConnection) { + continue; + } + model.$__updateConnection(_mongoose.connections[0]); + } + + return _mongoose; +}; + +/** + * Sets mongoose options + * + * `key` can be used as an object to set multiple options at once. + * If an error gets thrown for one option, other options will still be evaluated. + * + * #### Example: + * + * mongoose.set('test', value) // sets the 'test' option to `value` + * + * mongoose.set('debug', true) // enable logging collection methods + arguments to the console/file + * + * mongoose.set('debug', function(collectionName, methodName, ...methodArgs) {}); // use custom function to log collection methods + arguments + * + * mongoose.set({ debug: true, autoIndex: false }); // set multiple options at once + * + * Currently supported options are: + * - `allowDiskUse`: Set to `true` to set `allowDiskUse` to true to all aggregation operations by default. + * - `applyPluginsToChildSchemas`: `true` by default. Set to false to skip applying global plugins to child schemas + * - `applyPluginsToDiscriminators`: `false` by default. Set to true to apply global plugins to discriminator schemas. This typically isn't necessary because plugins are applied to the base schema and discriminators copy all middleware, methods, statics, and properties from the base schema. + * - `autoCreate`: Set to `true` to make Mongoose call [`Model.createCollection()`](https://mongoosejs.com/docs/api/model.html#Model.createCollection()) automatically when you create a model with `mongoose.model()` or `conn.model()`. This is useful for testing transactions, change streams, and other features that require the collection to exist. + * - `autoIndex`: `true` by default. Set to false to disable automatic index creation for all models associated with this Mongoose instance. + * - `bufferCommands`: enable/disable mongoose's buffering mechanism for all connections and models + * - `bufferTimeoutMS`: If bufferCommands is on, this option sets the maximum amount of time Mongoose buffering will wait before throwing an error. If not specified, Mongoose will use 10000 (10 seconds). + * - `cloneSchemas`: `false` by default. Set to `true` to `clone()` all schemas before compiling into a model. + * - `debug`: If `true`, prints the operations mongoose sends to MongoDB to the console. If a writable stream is passed, it will log to that stream, without colorization. If a callback function is passed, it will receive the collection name, the method name, then all arguments passed to the method. For example, if you wanted to replicate the default logging, you could output from the callback `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`. + * - `id`: If `true`, adds a `id` virtual to all schemas unless overwritten on a per-schema basis. + * - `maxTimeMS`: If set, attaches [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) to every query + * - `objectIdGetter`: `true` by default. Mongoose adds a getter to MongoDB ObjectId's called `_id` that returns `this` for convenience with populate. Set this to false to remove the getter. + * - `overwriteModels`: Set to `true` to default to overwriting models with the same name when calling `mongoose.model()`, as opposed to throwing an `OverwriteModelError`. + * - `returnDocument`: Set to `'before'` or `'after'` to set the default value for the `returnDocument` option to `findOneAndUpdate()`, `findByIdAndUpdate()`, and `findOneAndReplace()`. Defaults to `'before'`, which returns the document before the update was applied. Read our [`findOneAndUpdate()` tutorial](https://mongoosejs.com/docs/tutorials/findoneandupdate.html) for more information. + * - `returnOriginal`: **Deprecated.** Use `returnDocument` instead. If `false`, changes the default `returnOriginal` option to `findOneAndUpdate()`, `findByIdAndUpdate`, and `findOneAndReplace()` to false. This is equivalent to setting the `new` option to `true` for `findOneAndX()` calls by default. Read our [`findOneAndUpdate()` tutorial](https://mongoosejs.com/docs/tutorials/findoneandupdate.html) for more information. + * - `runValidators`: `false` by default. Set to true to enable [update validators](https://mongoosejs.com/docs/validation.html#update-validators) for all validators by default. + * - `sanitizeFilter`: `false` by default. Set to true to enable the [sanitization of the query filters](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.sanitizeFilter()) against query selector injection attacks by wrapping any nested objects that have a property whose name starts with `$` in a `$eq`. + * - `selectPopulatedPaths`: `true` by default. Set to false to opt out of Mongoose adding all fields that you `populate()` to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one. + * - `strictQuery`: `false` by default. May be `false`, `true`, or `'throw'`. Sets the default [strictQuery](https://mongoosejs.com/docs/guide.html#strictQuery) mode for schemas. + * - `strict`: `true` by default, may be `false`, `true`, or `'throw'`. Sets the default strict mode for schemas. + * - `timestamps.createdAt.immutable`: `true` by default. If `false`, it will change the `createdAt` field to be [`immutable: false`](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.immutable) which means you can update the `createdAt` + * - `toJSON`: `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toJSON()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.toJSON()), for determining how Mongoose documents get serialized by `JSON.stringify()` + * - `toObject`: `{ transform: true, flattenDecimals: true }` by default. Overwrites default objects to [`toObject()`](https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()) + * - `transactionAsyncLocalStorage`: `false` by default. If `true`, Mongoose will automatically pass the `session` to any operation executing within a transaction executor function. See [transaction AsyncLocalStorage documentation](https://mongoosejs.com/docs/transactions.html#asynclocalstorage) + * - `translateAliases`: `false` by default. If `true`, Mongoose will automatically translate aliases to their original paths before sending the query to MongoDB. + * - `updatePipeline`: `false` by default. If `true`, allows passing update pipelines (arrays) to update operations by default without explicitly setting `updatePipeline: true` in each query. + * + * @param {string|object} key The name of the option or an object of multiple key-value pairs + * @param {string|Function|boolean} value The value of the option, unused if "key" is an object + * @returns {Mongoose} The used Mongoose instance + * @api public + */ + +Mongoose.prototype.set = function getsetOptions(key, value) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + if (key == null) { + const error = new SetOptionError(); + error.addError(String(key), new SetOptionError.SetOptionInnerError(String(key))); + throw error; + } + + if (arguments.length === 1 && typeof key !== 'object') { + if (VALID_OPTIONS.indexOf(key) === -1) { + const error = new SetOptionError(); + error.addError(key, new SetOptionError.SetOptionInnerError(key)); + throw error; + } + + return _mongoose.options[key]; + } + + let options = {}; + + if (arguments.length === 2) { + options = { [key]: value }; + } + + if (arguments.length === 1 && typeof key === 'object') { + options = key; + } + + // array for errors to collect all errors for all key-value pairs, like ".validate" + let error = undefined; + + for (const [optionKey, optionValue] of Object.entries(options)) { + if (VALID_OPTIONS.indexOf(optionKey) === -1) { + if (!error) { + error = new SetOptionError(); + } + error.addError(optionKey, new SetOptionError.SetOptionInnerError(optionKey)); + continue; + } + + _mongoose.options[optionKey] = optionValue; + + if (optionKey === 'objectIdGetter') { + if (optionValue) { + Object.defineProperty(_mongoose.Types.ObjectId.prototype, '_id', { + enumerable: false, + configurable: true, + get: function() { + return this; + } + }); + } else { + delete _mongoose.Types.ObjectId.prototype._id; + } + } else if (optionKey === 'transactionAsyncLocalStorage') { + if (optionValue && !_mongoose.transactionAsyncLocalStorage) { + _mongoose.transactionAsyncLocalStorage = new AsyncLocalStorage(); + } else if (!optionValue && _mongoose.transactionAsyncLocalStorage) { + delete _mongoose.transactionAsyncLocalStorage; + } + } else if (optionKey === 'createInitialConnection') { + if (optionValue && !_mongoose.connection) { + _createDefaultConnection(_mongoose); + } else if (optionValue === false && _mongoose.connection?.[defaultConnectionSymbol]) { + if (_mongoose.connection.readyState === STATES.disconnected && utils.hasOwnKeys(_mongoose.connection.models) === false) { + _mongoose.connections.shift(); + } + } + } else if (optionKey === 'returnOriginal') { + if (_mongoose.options.returnDocument != null) { + throw new MongooseError( + 'Cannot set `returnOriginal` when `returnDocument` is already set. Use `returnDocument` instead.' + ); + } + const replacement = optionValue === true ? '\'before\'' : '\'after\''; + utils.warn( + 'Mongoose: the `returnOriginal` option for `mongoose.set()` is deprecated. ' + + 'Use `mongoose.set(\'returnDocument\', ' + replacement + ')` instead.' + ); + } else if (optionKey === 'returnDocument') { + if (_mongoose.options.returnOriginal != null) { + throw new MongooseError( + 'Cannot set `returnDocument` when `returnOriginal` is already set. Use `returnDocument` instead of `returnOriginal`.' + ); + } + } + } + + if (error) { + throw error; + } + + return _mongoose; +}; + +/** + * Gets mongoose options + * + * #### Example: + * + * mongoose.get('test') // returns the 'test' value + * + * @param {string} key + * @method get + * @api public + */ + +Mongoose.prototype.get = Mongoose.prototype.set; + +/** + * Creates a Connection instance. + * + * Each `connection` instance maps to a single database. This method is helpful when managing multiple db connections. + * + * + * _Options passed take precedence over options included in connection strings._ + * + * #### Example: + * + * // with mongodb:// URI + * db = mongoose.createConnection('mongodb://user:pass@127.0.0.1:port/database'); + * + * // and options + * const opts = { db: { native_parser: true }} + * db = mongoose.createConnection('mongodb://user:pass@127.0.0.1:port/database', opts); + * + * // replica sets + * db = mongoose.createConnection('mongodb://user:pass@127.0.0.1:port,anotherhost:port,yetanother:port/database'); + * + * // and options + * const opts = { replset: { strategy: 'ping', rs_name: 'testSet' }} + * db = mongoose.createConnection('mongodb://user:pass@127.0.0.1:port,anotherhost:port,yetanother:port/database', opts); + * + * // initialize now, connect later + * db = mongoose.createConnection(); + * await db.openUri('mongodb://127.0.0.1:27017/database'); + * + * @param {string} uri mongodb URI to connect to + * @param {object} [options] passed down to the [MongoDB driver's `connect()` function](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/MongoClientOptions.html), except for 4 mongoose-specific options explained below. + * @param {boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](https://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection. + * @param {string} [options.dbName] The name of the database you want to use. If not provided, Mongoose uses the database name from connection string. + * @param {string} [options.user] username for authentication, equivalent to `options.auth.username`. Maintained for backwards compatibility. + * @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. + * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. + * @param {number} [options.maxPoolSize=5] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {number} [options.minPoolSize=1] The minimum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. Defaults to 0, which means Node.js will not time out the socket due to inactivity. A socket may be inactive because of either no activity or a long-running operation. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. + * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. + * @return {Connection} the created Connection object. Connections are not thenable, so you can't do `await mongoose.createConnection()`. To await use `mongoose.createConnection(uri).asPromise()` instead. + * @api public + */ + +Mongoose.prototype.createConnection = function createConnection(uri, options) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + const Connection = _mongoose.__driver.Connection; + const conn = new Connection(_mongoose); + _mongoose.connections.push(conn); + _mongoose.nextConnectionId++; + _mongoose.events.emit('createConnection', conn); + + if (arguments.length > 0) { + conn.openUri(uri, { ...options, _fireAndForget: true }); + } + + return conn; +}; + +/** + * Opens the default mongoose connection. + * + * #### Example: + * + * mongoose.connect('mongodb://user:pass@127.0.0.1:port/database'); + * + * // replica sets + * const uri = 'mongodb://user:pass@127.0.0.1:port,anotherhost:port,yetanother:port/mydatabase'; + * mongoose.connect(uri); + * + * // with options + * mongoose.connect(uri, options); + * + * // Using `await` throws "MongooseServerSelectionError: Server selection timed out after 30000 ms" + * // if Mongoose can't connect. + * const uri = 'mongodb://nonexistent.domain:27000'; + * await mongoose.connect(uri); + * + * @param {string} uri mongodb URI to connect to + * @param {object} [options] passed down to the [MongoDB driver's `connect()` function](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/MongoClientOptions.html), except for 4 mongoose-specific options explained below. + * @param {boolean} [options.bufferCommands=true] Mongoose specific option. Set to false to [disable buffering](https://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection. + * @param {number} [options.bufferTimeoutMS=10000] Mongoose specific option. If `bufferCommands` is true, Mongoose will throw an error after `bufferTimeoutMS` if the operation is still buffered. + * @param {string} [options.dbName] The name of the database we want to use. If not provided, use database name from connection string. + * @param {string} [options.user] username for authentication, equivalent to `options.auth.username`. Maintained for backwards compatibility. + * @param {string} [options.pass] password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. + * @param {number} [options.maxPoolSize=100] The maximum number of sockets the MongoDB driver will keep open for this connection. Keep in mind that MongoDB only allows one operation per socket at a time, so you may want to increase this if you find you have a few slow queries that are blocking faster queries from proceeding. See [Slow Trains in MongoDB and Node.js](https://thecodebarbarian.com/slow-trains-in-mongodb-and-nodejs). + * @param {number} [options.minPoolSize=0] The minimum number of sockets the MongoDB driver will keep open for this connection. + * @param {number} [options.serverSelectionTimeoutMS] If `useUnifiedTopology = true`, the MongoDB driver will try to find a server to send any given operation to, and keep retrying for `serverSelectionTimeoutMS` milliseconds before erroring out. If not set, the MongoDB driver defaults to using `30000` (30 seconds). + * @param {number} [options.heartbeatFrequencyMS] If `useUnifiedTopology = true`, the MongoDB driver sends a heartbeat every `heartbeatFrequencyMS` to check on the status of the connection. A heartbeat is subject to `serverSelectionTimeoutMS`, so the MongoDB driver will retry failed heartbeats for up to 30 seconds by default. Mongoose only emits a `'disconnected'` event after a heartbeat has failed, so you may want to decrease this setting to reduce the time between when your server goes down and when Mongoose emits `'disconnected'`. We recommend you do **not** set this setting below 1000, too many heartbeats can lead to performance degradation. + * @param {boolean} [options.autoIndex=true] Mongoose-specific option. Set to false to disable automatic index creation for all models associated with this connection. + * @param {number} [options.socketTimeoutMS=0] How long the MongoDB driver will wait before killing a socket due to inactivity _after initial connection_. A socket may be inactive because of either no activity or a long-running operation. `socketTimeoutMS` defaults to 0, which means Node.js will not time out the socket due to inactivity. This option is passed to [Node.js `socket#setTimeout()` function](https://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback) after the MongoDB driver successfully completes. + * @param {number} [options.family=0] Passed transparently to [Node.js' `dns.lookup()`](https://nodejs.org/api/dns.html#dns_dns_lookup_hostname_options_callback) function. May be either `0`, `4`, or `6`. `4` means use IPv4 only, `6` means use IPv6 only, `0` means try both. + * @param {boolean} [options.autoCreate=false] Set to `true` to make Mongoose automatically call `createCollection()` on every model created on this connection. + * @see Mongoose#createConnection https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.createConnection() + * @api public + * @return {Promise} resolves to `this` if connection succeeded + */ + +Mongoose.prototype.connect = async function connect(uri, options) { + if (typeof options === 'function' || (arguments.length >= 3 && typeof arguments[2] === 'function')) { + throw new MongooseError('Mongoose.prototype.connect() no longer accepts a callback'); + } + + const _mongoose = this instanceof Mongoose ? this : mongoose; + if (_mongoose.connection == null) { + _createDefaultConnection(_mongoose); + } + const conn = _mongoose.connection; + + return conn.openUri(uri, options).then(() => _mongoose); +}; + +/** + * Runs `.close()` on all connections in parallel. + * + * @return {Promise} resolves when all connections are closed, or rejects with the first error that occurred. + * @api public + */ + +Mongoose.prototype.disconnect = async function disconnect() { + if (arguments.length >= 1 && typeof arguments[0] === 'function') { + throw new MongooseError('Mongoose.prototype.disconnect() no longer accepts a callback'); + } + + const _mongoose = this instanceof Mongoose ? this : mongoose; + + const remaining = _mongoose.connections.length; + if (remaining <= 0) { + return; + } + await Promise.all(_mongoose.connections.map(conn => conn.close())); +}; + +/** + * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](https://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + * + * Calling `mongoose.startSession()` is equivalent to calling `mongoose.connection.startSession()`. + * Sessions are scoped to a connection, so calling `mongoose.startSession()` + * starts a session on the [default mongoose connection](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.connection). + * + * @param {object} [options] see the [mongodb driver options](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html#startSession) + * @param {boolean} [options.causalConsistency=true] set to false to disable causal consistency + * @return {Promise} promise that resolves to a MongoDB driver `ClientSession` + * @api public + */ + +Mongoose.prototype.startSession = function startSession() { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + return _mongoose.connection.startSession.apply(_mongoose.connection, arguments); +}; + +/** + * Getter/setter around function for pluralizing collection names. + * + * @param {Function|null} [fn] overwrites the function used to pluralize collection names + * @return {Function|null} the current function used to pluralize collection names, defaults to the legacy function from `mongoose-legacy-pluralize`. + * @api public + */ + +Mongoose.prototype.pluralize = function pluralize(fn) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + if (arguments.length > 0) { + _mongoose._pluralize = fn; + } + return _mongoose._pluralize; +}; + +/** + * Defines a model or retrieves it. + * + * Models defined on the `mongoose` instance are available to all connection + * created by the same `mongoose` instance. + * + * If you call `mongoose.model()` with twice the same name but a different schema, + * you will get an `OverwriteModelError`. If you call `mongoose.model()` with + * the same name and same schema, you'll get the same schema back. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * + * // define an Actor model with this mongoose instance + * const schema = new Schema({ name: String }); + * mongoose.model('Actor', schema); + * + * // create a new connection + * const conn = mongoose.createConnection(..); + * + * // create Actor model + * const Actor = conn.model('Actor', schema); + * conn.model('Actor') === Actor; // true + * conn.model('Actor', schema) === Actor; // true, same schema + * conn.model('Actor', schema, 'actors') === Actor; // true, same schema and collection name + * + * // This throws an `OverwriteModelError` because the schema is different. + * conn.model('Actor', new Schema({ name: String })); + * + * _When no `collection` argument is passed, Mongoose uses the model name. If you don't like this behavior, either pass a collection name, use `mongoose.pluralize()`, or set your schemas collection name option._ + * + * #### Example: + * + * const schema = new Schema({ name: String }, { collection: 'actor' }); + * + * // or + * + * schema.set('collection', 'actor'); + * + * // or + * + * const collectionName = 'actor'; + * const M = mongoose.model('Actor', schema, collectionName); + * + * @param {string|Function} name model name or class extending Model + * @param {Schema} [schema] the schema to use. + * @param {string} [collection] name (optional, inferred from model name) + * @param {object} [options] + * @param {boolean} [options.overwriteModels=false] If true, overwrite existing models with the same name to avoid `OverwriteModelError` + * @return {Model} The model associated with `name`. Mongoose will create the model if it doesn't already exist. + * @api public + */ + +Mongoose.prototype.model = function model(name, schema, collection, options) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + if (typeof schema === 'string') { + collection = schema; + schema = false; + } + + if (arguments.length === 1) { + const model = _mongoose.models[name]; + if (!model) { + throw new _mongoose.Error.MissingSchemaError(name); + } + return model; + } + + if (utils.isObject(schema) && !(schema instanceof Schema)) { + schema = new Schema(schema); + } + if (schema && !(schema instanceof Schema)) { + throw new _mongoose.Error('The 2nd parameter to `mongoose.model()` should be a ' + + 'schema or a POJO'); + } + + // handle internal options from connection.model() + options = options || {}; + + const originalSchema = schema; + if (schema) { + if (_mongoose.get('cloneSchemas')) { + schema = schema.clone(); + } + _mongoose._applyPlugins(schema); + } + + // connection.model() may be passing a different schema for + // an existing model name. in this case don't read from cache. + const overwriteModels = Object.hasOwn(_mongoose.options, 'overwriteModels') ? + _mongoose.options.overwriteModels : + options.overwriteModels; + if (Object.hasOwn(_mongoose.models, name) && options.cache !== false && overwriteModels !== true) { + if (originalSchema?.instanceOfSchema && + originalSchema !== _mongoose.models[name].schema) { + throw new _mongoose.Error.OverwriteModelError(name); + } + if (collection && collection !== _mongoose.models[name].collection.name) { + // subclass current model with alternate collection + const model = _mongoose.models[name]; + schema = model.prototype.schema; + const sub = model.__subclass(_mongoose.connection, schema, collection); + // do not cache the sub model + return sub; + } + return _mongoose.models[name]; + } + if (schema == null) { + throw new _mongoose.Error.MissingSchemaError(name); + } + + const model = _mongoose._model(name, schema, collection, options); + _mongoose.connection.models[name] = model; + _mongoose.models[name] = model; + + return model; +}; + +/*! + * ignore + */ + +Mongoose.prototype._model = function _model(name, schema, collection, options) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + let model; + if (typeof name === 'function') { + model = name; + name = model.name; + if (!(model.prototype instanceof Model)) { + throw new _mongoose.Error('The provided class ' + name + ' must extend Model'); + } + } + + if (schema) { + if (_mongoose.get('cloneSchemas')) { + schema = schema.clone(); + } + _mongoose._applyPlugins(schema); + } + + // Apply relevant "global" options to the schema + if (schema == null || !('pluralization' in schema.options)) { + schema.options.pluralization = _mongoose.options.pluralization; + } + + if (!collection) { + collection = schema.get('collection') || + utils.toCollectionName(name, _mongoose.pluralize()); + } + + applyEmbeddedDiscriminators(schema); + + const connection = options.connection || _mongoose.connection; + model = _mongoose.Model.compile(model || name, schema, collection, connection, _mongoose); + // Errors handled internally, so safe to ignore error + model.init().catch(function $modelInitNoop() {}); + + connection.emit('model', model); + + if (schema._applyDiscriminators != null) { + for (const disc of schema._applyDiscriminators.keys()) { + const { + schema: discriminatorSchema, + options + } = schema._applyDiscriminators.get(disc); + model.discriminator(disc, discriminatorSchema, options); + } + } + + return model; +}; + +/** + * Removes the model named `name` from the default connection, if it exists. + * You can use this function to clean up any models you created in your tests to + * prevent OverwriteModelErrors. + * + * Equivalent to `mongoose.connection.deleteModel(name)`. + * + * #### Example: + * + * mongoose.model('User', new Schema({ name: String })); + * console.log(mongoose.model('User')); // Model object + * mongoose.deleteModel('User'); + * console.log(mongoose.model('User')); // undefined + * + * // Usually useful in a Mocha `afterEach()` hook + * afterEach(function() { + * mongoose.deleteModel(/.+/); // Delete every model + * }); + * + * @api public + * @param {string|RegExp} name if string, the name of the model to remove. If regexp, removes all models whose name matches the regexp. + * @return {Mongoose} this + */ + +Mongoose.prototype.deleteModel = function deleteModel(name) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + _mongoose.connection.deleteModel(name); + delete _mongoose.models[name]; + return _mongoose; +}; + +/** + * Returns an array of model names created on this instance of Mongoose. + * + * #### Note: + * + * _Does not include names of models created using `connection.model()`._ + * + * @api public + * @return {Array} + */ + +Mongoose.prototype.modelNames = function modelNames() { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + const names = Object.keys(_mongoose.models); + return names; +}; + +/** + * Applies global plugins to `schema`. + * + * @param {Schema} schema + * @api private + */ + +Mongoose.prototype._applyPlugins = function _applyPlugins(schema, options) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + options = options || {}; + options.applyPluginsToDiscriminators = _mongoose.options?.applyPluginsToDiscriminators || false; + options.applyPluginsToChildSchemas = typeof _mongoose.options?.applyPluginsToChildSchemas === 'boolean' ? + _mongoose.options.applyPluginsToChildSchemas : + true; + applyPlugins(schema, _mongoose.plugins, options, '$globalPluginsApplied'); +}; + +/** + * Declares a global plugin executed on all Schemas. + * + * Equivalent to calling `.plugin(fn)` on each Schema you create. + * + * @param {Function} fn plugin callback + * @param {object} [opts] optional options + * @return {Mongoose} this + * @see plugins https://mongoosejs.com/docs/plugins.html + * @api public + */ + +Mongoose.prototype.plugin = function plugin(fn, opts) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + + _mongoose.plugins.push([fn, opts]); + return _mongoose; +}; + +/** + * The Mongoose module's default connection. Equivalent to `mongoose.connections[0]`, see [`connections`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.connections). + * + * #### Example: + * + * const mongoose = require('mongoose'); + * mongoose.connect(...); + * mongoose.connection.on('error', cb); + * + * This is the connection used by default for every model created using [mongoose.model](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.model()). + * + * To create a new connection, use [`createConnection()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.createConnection()). + * + * @memberOf Mongoose + * @instance + * @property {Connection} connection + * @api public + */ + +Mongoose.prototype.__defineGetter__('connection', function() { + return this.connections[0]; +}); + +Mongoose.prototype.__defineSetter__('connection', function(v) { + if (v instanceof this.__driver.Connection) { + this.connections[0] = v; + this.models = v.models; + } +}); + +/** + * An array containing all [connections](connection.html) associated with this + * Mongoose instance. By default, there is 1 connection. Calling + * [`createConnection()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.createConnection()) adds a connection + * to this array. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * mongoose.connections.length; // 1, just the default connection + * mongoose.connections[0] === mongoose.connection; // true + * + * mongoose.createConnection('mongodb://127.0.0.1:27017/test'); + * mongoose.connections.length; // 2 + * + * @memberOf Mongoose + * @instance + * @property {Array} connections + * @api public + */ + +Mongoose.prototype.connections; + +/** + * An integer containing the value of the next connection id. Calling + * [`createConnection()`](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.createConnection()) increments + * this value. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * mongoose.createConnection(); // id `0`, `nextConnectionId` becomes `1` + * mongoose.createConnection(); // id `1`, `nextConnectionId` becomes `2` + * mongoose.connections[0].destroy() // Removes connection with id `0` + * mongoose.createConnection(); // id `2`, `nextConnectionId` becomes `3` + * + * @memberOf Mongoose + * @instance + * @property {number} nextConnectionId + * @api private + */ + +Mongoose.prototype.nextConnectionId; + +/** + * The Mongoose Aggregate constructor + * + * @method Aggregate + * @api public + */ + +Mongoose.prototype.Aggregate = Aggregate; + +/** + * The Base Mongoose Collection class. `mongoose.Collection` extends from this class. + * + * @memberOf Mongoose + * @instance + * @method Collection + * @api public + */ + +Mongoose.prototype.BaseCollection = require('./collection'); + +/** + * The Mongoose Collection constructor + * + * @memberOf Mongoose + * @instance + * @method Collection + * @api public + */ + +Object.defineProperty(Mongoose.prototype, 'Collection', { + get: function() { + return this.__driver.Collection; + }, + set: function(Collection) { + this.__driver.Collection = Collection; + } +}); + +/** + * The Mongoose [Connection](https://mongoosejs.com/docs/api/connection.html#Connection()) constructor + * + * @memberOf Mongoose + * @instance + * @method Connection + * @api public + */ + +Object.defineProperty(Mongoose.prototype, 'Connection', { + get: function() { + return this.__driver.Connection; + }, + set: function(Connection) { + if (Connection === this.__driver.Connection) { + return; + } + + this.__driver.Connection = Connection; + } +}); + +/** + * The Base Mongoose Connection class. `mongoose.Connection` extends from this class. + * + * @memberOf Mongoose + * @instance + * @method Connection + * @api public + */ + +Mongoose.prototype.BaseConnection = require('./connection'); + +/** + * The Mongoose version + * + * #### Example: + * + * console.log(mongoose.version); // '5.x.x' + * + * @property version + * @api public + */ + +Mongoose.prototype.version = pkg.version; + +/** + * The Mongoose constructor + * + * The exports of the mongoose module is an instance of this class. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * const mongoose2 = new mongoose.Mongoose(); + * + * @method Mongoose + * @api public + */ + +Mongoose.prototype.Mongoose = Mongoose; + +/** + * The Mongoose [Schema](https://mongoosejs.com/docs/api/schema.html#Schema()) constructor + * + * #### Example: + * + * const mongoose = require('mongoose'); + * const Schema = mongoose.Schema; + * const CatSchema = new Schema(..); + * + * @method Schema + * @api public + */ + +Mongoose.prototype.Schema = Schema; + +/** + * The Mongoose [SchemaType](https://mongoosejs.com/docs/api/schematype.html#SchemaType()) constructor + * + * @method SchemaType + * @api public + */ + +Mongoose.prototype.SchemaType = SchemaType; + +/** + * The various Mongoose SchemaTypes. + * + * #### Note: + * + * _Alias of mongoose.Schema.Types for backwards compatibility._ + * + * @property SchemaTypes + * @see Schema.SchemaTypes https://mongoosejs.com/docs/schematypes.html + * @api public + */ + +Mongoose.prototype.SchemaTypes = Schema.Types; + +/** + * The Mongoose [VirtualType](https://mongoosejs.com/docs/api/virtualtype.html#VirtualType()) constructor + * + * @method VirtualType + * @api public + */ + +Mongoose.prototype.VirtualType = VirtualType; + +/** + * The various Mongoose Types. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * const array = mongoose.Types.Array; + * + * #### Types: + * + * - [Array](https://mongoosejs.com/docs/schematypes.html#arrays) + * - [Buffer](https://mongoosejs.com/docs/schematypes.html#buffers) + * - [Embedded](https://mongoosejs.com/docs/schematypes.html#schemas) + * - [DocumentArray](https://mongoosejs.com/docs/api/documentarray.html) + * - [Decimal128](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.Decimal128) + * - [ObjectId](https://mongoosejs.com/docs/schematypes.html#objectids) + * - [Map](https://mongoosejs.com/docs/schematypes.html#maps) + * - [Subdocument](https://mongoosejs.com/docs/schematypes.html#schemas) + * - [Int32](https://mongoosejs.com/docs/schematypes.html#int32) + * + * Using this exposed access to the `ObjectId` type, we can construct ids on demand. + * + * const ObjectId = mongoose.Types.ObjectId; + * const id1 = new ObjectId; + * + * @property Types + * @api public + */ + +Mongoose.prototype.Types = Types; + +/** + * The Mongoose [Query](https://mongoosejs.com/docs/api/query.html#Query()) constructor. + * + * @method Query + * @api public + */ + +Mongoose.prototype.Query = Query; + +/** + * The Mongoose [Model](https://mongoosejs.com/docs/api/model.html#Model()) constructor. + * + * @method Model + * @api public + */ + +Mongoose.prototype.Model = Model; + +/** + * The Mongoose [Document](https://mongoosejs.com/docs/api/document.html#Document()) constructor. + * + * @method Document + * @api public + */ + +Mongoose.prototype.Document = Document; + +/** + * The Mongoose ObjectId [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for + * declaring paths in your schema that should be + * [MongoDB ObjectIds](https://www.mongodb.com/docs/manual/reference/method/ObjectId/). + * Do not use this to create a new ObjectId instance, use `mongoose.Types.ObjectId` + * instead. + * + * #### Example: + * + * const childSchema = new Schema({ parentId: mongoose.ObjectId }); + * + * @property ObjectId + * @api public + */ + +Mongoose.prototype.ObjectId = SchemaTypes.ObjectId; + +/** + * Returns true if Mongoose can cast the given value to an ObjectId, or + * false otherwise. + * + * #### Example: + * + * mongoose.isValidObjectId(new mongoose.Types.ObjectId()); // true + * mongoose.isValidObjectId('0123456789ab'); // true + * mongoose.isValidObjectId(6); // true + * mongoose.isValidObjectId(new User({ name: 'test' })); // true + * + * mongoose.isValidObjectId({ test: 42 }); // false + * + * @method isValidObjectId + * @param {any} v + * @returns {boolean} true if `v` is something Mongoose can coerce to an ObjectId + * @api public + */ + +Mongoose.prototype.isValidObjectId = function isValidObjectId(v) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + return _mongoose.Types.ObjectId.isValid(v); +}; + +/** + * Returns true if the given value is a Mongoose ObjectId (using `instanceof`) or if the + * given value is a 24 character hex string, which is the most commonly used string representation + * of an ObjectId. + * + * This function is similar to `isValidObjectId()`, but considerably more strict, because + * `isValidObjectId()` will return `true` for _any_ value that Mongoose can convert to an + * ObjectId. That includes Mongoose documents, any string of length 12, and any number. + * `isObjectIdOrHexString()` returns true only for `ObjectId` instances or 24 character hex + * strings, and will return false for numbers, documents, and strings of length 12. + * + * #### Example: + * + * mongoose.isObjectIdOrHexString(new mongoose.Types.ObjectId()); // true + * mongoose.isObjectIdOrHexString('62261a65d66c6be0a63c051f'); // true + * + * mongoose.isObjectIdOrHexString('0123456789ab'); // false + * mongoose.isObjectIdOrHexString(6); // false + * mongoose.isObjectIdOrHexString(new User({ name: 'test' })); // false + * mongoose.isObjectIdOrHexString({ test: 42 }); // false + * + * @method isObjectIdOrHexString + * @param {any} v + * @returns {boolean} true if `v` is an ObjectId instance _or_ a 24 char hex string + * @api public + */ + +Mongoose.prototype.isObjectIdOrHexString = function isObjectIdOrHexString(v) { + return isBsonType(v, 'ObjectId') || (typeof v === 'string' && objectIdHexRegexp.test(v)); +}; + +/** + * + * Syncs all the indexes for the models registered with this connection. + * + * @param {object} options + * @param {boolean} options.continueOnError `false` by default. If set to `true`, mongoose will not throw an error if one model syncing failed, and will return an object where the keys are the names of the models, and the values are the results/errors for each model. + * @return {Promise} Returns a Promise, when the Promise resolves the value is a list of the dropped indexes. + */ +Mongoose.prototype.syncIndexes = function syncIndexes(options) { + const _mongoose = this instanceof Mongoose ? this : mongoose; + return _mongoose.connection.syncIndexes(options); +}; + +/** + * The Mongoose Decimal128 [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for + * declaring paths in your schema that should be + * [128-bit decimal floating points](https://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-decimal.html). + * Do not use this to create a new Decimal128 instance, use `mongoose.Types.Decimal128` + * instead. + * + * #### Example: + * + * const vehicleSchema = new Schema({ fuelLevel: mongoose.Decimal128 }); + * + * @property Decimal128 + * @api public + */ + +Mongoose.prototype.Decimal128 = SchemaTypes.Decimal128; + + +/** + * The Mongoose Mixed [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for + * declaring paths in your schema that Mongoose's change tracking, casting, + * and validation should ignore. + * + * #### Example: + * + * const schema = new Schema({ arbitrary: mongoose.Mixed }); + * + * @property Mixed + * @api public + */ + +Mongoose.prototype.Mixed = SchemaTypes.Mixed; + +/** + * The Mongoose Date [SchemaType](https://mongoosejs.com/docs/schematypes.html). + * + * #### Example: + * + * const schema = new Schema({ test: Date }); + * schema.path('test') instanceof mongoose.Date; // true + * + * @property Date + * @api public + */ + +Mongoose.prototype.Date = SchemaTypes.Date; + +/** + * The Mongoose Number [SchemaType](https://mongoosejs.com/docs/schematypes.html). Used for + * declaring paths in your schema that Mongoose should cast to numbers. + * + * #### Example: + * + * const schema = new Schema({ num: mongoose.Number }); + * // Equivalent to: + * const schema = new Schema({ num: 'number' }); + * + * @property Number + * @api public + */ + +Mongoose.prototype.Number = SchemaTypes.Number; + +/** + * The [MongooseError](https://mongoosejs.com/docs/api/error.html#Error()) constructor. + * + * @method Error + * @api public + */ + +Mongoose.prototype.Error = MongooseError; +Mongoose.prototype.MongooseError = MongooseError; + +/** + * Mongoose uses this function to get the current time when setting + * [timestamps](https://mongoosejs.com/docs/guide.html#timestamps). You may stub out this function + * using a tool like [Sinon](https://www.npmjs.com/package/sinon) for testing. + * + * @method now + * @returns {Date} the current time + * @api public + */ + +Mongoose.prototype.now = function now() { return new Date(); }; + +/** + * The Mongoose CastError constructor + * + * @method CastError + * @param {string} type The name of the type + * @param {any} value The value that failed to cast + * @param {string} path The path `a.b.c` in the doc where this cast error occurred + * @param {Error} [reason] The original error that was thrown + * @api public + */ + +Mongoose.prototype.CastError = MongooseError.CastError; + +/** + * The constructor used for schematype options + * + * @method SchemaTypeOptions + * @api public + */ + +Mongoose.prototype.SchemaTypeOptions = require('./options/schemaTypeOptions'); + +/** + * The [mquery](https://github.com/aheckmann/mquery) query builder Mongoose uses. + * + * @property mquery + * @api public + */ + +Mongoose.prototype.mquery = require('mquery'); + +/** + * Sanitizes query filters against [query selector injection attacks](https://thecodebarbarian.com/2014/09/04/defending-against-query-selector-injection-attacks.html) + * by wrapping any nested objects that have a property whose name starts with `$` in a `$eq`. + * + * ```javascript + * const obj = { username: 'val', pwd: { $ne: null } }; + * sanitizeFilter(obj); + * obj; // { username: 'val', pwd: { $eq: { $ne: null } } }); + * ``` + * + * @method sanitizeFilter + * @param {object} filter + * @returns {object} the sanitized object + * @api public + */ + +Mongoose.prototype.sanitizeFilter = sanitizeFilter; + +/** + * Tells `sanitizeFilter()` to skip the given object when filtering out potential [query selector injection attacks](https://thecodebarbarian.com/2014/09/04/defending-against-query-selector-injection-attacks.html). + * Use this method when you have a known query selector that you want to use. + * + * ```javascript + * const obj = { username: 'val', pwd: trusted({ $type: 'string', $eq: 'my secret' }) }; + * sanitizeFilter(obj); + * + * // Note that `sanitizeFilter()` did not add `$eq` around `$type`. + * obj; // { username: 'val', pwd: { $type: 'string', $eq: 'my secret' } }); + * ``` + * + * @method trusted + * @param {object} obj + * @returns {object} the passed in object + * @api public + */ + +Mongoose.prototype.trusted = trusted; + +/** + * Use this function in `pre()` middleware to skip calling the wrapped function. + * + * #### Example: + * + * schema.pre('save', function() { + * // Will skip executing `save()`, but will execute post hooks as if + * // `save()` had executed with the result `{ matchedCount: 0 }` + * return mongoose.skipMiddlewareFunction({ matchedCount: 0 }); + * }); + * + * @method skipMiddlewareFunction + * @param {any} result + * @api public + */ + +Mongoose.prototype.skipMiddlewareFunction = Kareem.skipWrappedFunction; + +/** + * Use this function in `post()` middleware to replace the result + * + * #### Example: + * + * schema.post('find', function(res) { + * // Normally you have to modify `res` in place. But with + * // `overwriteMiddlewarResult()`, you can make `find()` return a + * // completely different value. + * return mongoose.overwriteMiddlewareResult(res.filter(doc => !doc.isDeleted)); + * }); + * + * @method overwriteMiddlewareResult + * @param {any} result + * @api public + */ + +Mongoose.prototype.overwriteMiddlewareResult = Kareem.overwriteResult; + +/** + * Use this function in `pre()` middleware to replace the arguments passed to the next middleware or hook. + * + * #### Example: + * + * // Suppose you have a schema for time in "HH:MM" string format, but you want to store it as an object { hours, minutes } + * const timeStringToObject = (time) => { + * if (typeof time !== 'string') return time; + * const [hours, minutes] = time.split(':'); + * return { hours: parseInt(hours), minutes: parseInt(minutes) }; + * }; + * + * const timeSchema = new Schema({ + * hours: { type: Number, required: true }, + * minutes: { type: Number, required: true }, + * }); + * + * // In a pre('init') hook, replace raw string doc with custom object form + * timeSchema.pre('init', function(doc) { + * if (typeof doc === 'string') { + * return mongoose.overwriteMiddlewareArguments(timeStringToObject(doc)); + * } + * }); + * + * // Now, initializing with a time string gets auto-converted by the hook + * const userSchema = new Schema({ time: timeSchema }); + * const User = mongoose.model('User', userSchema); + * const doc = new User({}); + * doc.$init({ time: '12:30' }); + * + * @method overwriteMiddlewareArguments + * @param {...any} args The new arguments to be passed to the next middleware. Pass multiple arguments as a spread, **not** as an array. + * @api public + */ + +Mongoose.prototype.overwriteMiddlewareArguments = Kareem.overwriteArguments; + +/** + * Takes in an object and deletes any keys from the object whose values + * are strictly equal to `undefined`. + * This function is useful for query filters because Mongoose treats + * `TestModel.find({ name: undefined })` as `TestModel.find({ name: null })`. + * + * #### Example: + * + * const filter = { name: 'John', age: undefined, status: 'active' }; + * mongoose.omitUndefined(filter); // { name: 'John', status: 'active' } + * filter; // { name: 'John', status: 'active' } + * + * await UserModel.findOne(mongoose.omitUndefined(filter)); + * + * @method omitUndefined + * @param {object} [val] the object to remove undefined keys from + * @returns {object} the object passed in + * @api public + */ + +Mongoose.prototype.omitUndefined = require('./helpers/omitUndefined'); + +/*! + * Create a new default connection (`mongoose.connection`) for a Mongoose instance. + * No-op if there is already a default connection. + */ + +function _createDefaultConnection(mongoose) { + if (mongoose.connection) { + return; + } + const conn = mongoose.createConnection(); // default connection + conn[defaultConnectionSymbol] = true; + conn.models = mongoose.models; +} + +/** + * The exports object is an instance of Mongoose. + * + * @api private + */ + +const mongoose = module.exports = exports = new Mongoose({ + [defaultMongooseSymbol]: true +}); diff --git a/backend/node_modules/mongoose/lib/options.js b/backend/node_modules/mongoose/lib/options.js new file mode 100644 index 0000000..3651dd6 --- /dev/null +++ b/backend/node_modules/mongoose/lib/options.js @@ -0,0 +1,18 @@ +'use strict'; + +/*! + * ignore + */ + +exports.internalToObjectOptions = { + transform: false, + virtuals: false, + getters: false, + _skipDepopulateTopLevel: true, + depopulate: true, + flattenDecimals: false, + useProjection: false, + versionKey: true, + flattenObjectIds: false, + flattenUUIDs: false +}; diff --git a/backend/node_modules/mongoose/lib/options/populateOptions.js b/backend/node_modules/mongoose/lib/options/populateOptions.js new file mode 100644 index 0000000..ad743fe --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/populateOptions.js @@ -0,0 +1,36 @@ +'use strict'; + +const clone = require('../helpers/clone'); + +class PopulateOptions { + constructor(obj) { + this._docs = {}; + this._childDocs = []; + + if (obj == null) { + return; + } + obj = clone(obj); + Object.assign(this, obj); + if (typeof obj.subPopulate === 'object') { + this.populate = obj.subPopulate; + } + + + if (obj.perDocumentLimit != null && obj.limit != null) { + throw new Error('Can not use `limit` and `perDocumentLimit` at the same time. Path: `' + obj.path + '`.'); + } + } +} + +/** + * The connection used to look up models by name. If not specified, Mongoose + * will default to using the connection associated with the model in + * `PopulateOptions#model`. + * + * @memberOf PopulateOptions + * @property {Connection} connection + * @api public + */ + +module.exports = PopulateOptions; diff --git a/backend/node_modules/mongoose/lib/options/propertyOptions.js b/backend/node_modules/mongoose/lib/options/propertyOptions.js new file mode 100644 index 0000000..cb95f35 --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/propertyOptions.js @@ -0,0 +1,8 @@ +'use strict'; + +module.exports = Object.freeze({ + enumerable: true, + configurable: true, + writable: true, + value: void 0 +}); diff --git a/backend/node_modules/mongoose/lib/options/saveOptions.js b/backend/node_modules/mongoose/lib/options/saveOptions.js new file mode 100644 index 0000000..286987e --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/saveOptions.js @@ -0,0 +1,16 @@ +'use strict'; + +const clone = require('../helpers/clone'); + +class SaveOptions { + constructor(obj) { + if (obj == null) { + return; + } + Object.assign(this, clone(obj)); + } +} + +SaveOptions.prototype.__subdocs = null; + +module.exports = SaveOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaArrayOptions.js b/backend/node_modules/mongoose/lib/options/schemaArrayOptions.js new file mode 100644 index 0000000..b10d38c --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaArrayOptions.js @@ -0,0 +1,78 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on an Array schematype. + * + * #### Example: + * + * const schema = new Schema({ tags: [String] }); + * schema.path('tags').options; // SchemaArrayOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaArrayOptions + */ + +class SchemaArrayOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If this is an array of strings, an array of allowed values for this path. + * Throws an error if this array isn't an array of strings. + * + * @api public + * @property enum + * @memberOf SchemaArrayOptions + * @type {Array} + * @instance + */ + +Object.defineProperty(SchemaArrayOptions.prototype, 'enum', opts); + +/** + * If set, specifies the type of this array's values. Equivalent to setting + * `type` to an array whose first element is `of`. + * + * #### Example: + * + * // `arr` is an array of numbers. + * new Schema({ arr: [Number] }); + * // Equivalent way to define `arr` as an array of numbers + * new Schema({ arr: { type: Array, of: Number } }); + * + * @api public + * @property of + * @memberOf SchemaArrayOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaArrayOptions.prototype, 'of', opts); + +/** + * If set to `false`, will always deactivate casting non-array values to arrays. + * If set to `true`, will cast non-array values to arrays if `init` and `SchemaArray.options.castNonArrays` are also `true` + * + * #### Example: + * + * const Model = db.model('Test', new Schema({ x1: { castNonArrays: false, type: [String] } })); + * const doc = new Model({ x1: "some non-array value" }); + * await doc.validate(); // Errors with "CastError" + * + * @api public + * @property castNonArrays + * @memberOf SchemaArrayOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(SchemaArrayOptions.prototype, 'castNonArrays', opts); + +/*! + * ignore + */ + +module.exports = SchemaArrayOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaBufferOptions.js b/backend/node_modules/mongoose/lib/options/schemaBufferOptions.js new file mode 100644 index 0000000..dcdc1ef --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaBufferOptions.js @@ -0,0 +1,38 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a Buffer schematype. + * + * #### Example: + * + * const schema = new Schema({ bitmap: Buffer }); + * schema.path('bitmap').options; // SchemaBufferOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaBufferOptions + */ + +class SchemaBufferOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * Set the default subtype for this buffer. + * + * @api public + * @property subtype + * @memberOf SchemaBufferOptions + * @type {number} + * @instance + */ + +Object.defineProperty(SchemaBufferOptions.prototype, 'subtype', opts); + +/*! + * ignore + */ + +module.exports = SchemaBufferOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaDateOptions.js b/backend/node_modules/mongoose/lib/options/schemaDateOptions.js new file mode 100644 index 0000000..a4ef62b --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaDateOptions.js @@ -0,0 +1,71 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a Date schematype. + * + * #### Example: + * + * const schema = new Schema({ startedAt: Date }); + * schema.path('startedAt').options; // SchemaDateOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaDateOptions + */ + +class SchemaDateOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If set, Mongoose adds a validator that checks that this path is after the + * given `min`. + * + * @api public + * @property min + * @memberOf SchemaDateOptions + * @type {Date} + * @instance + */ + +Object.defineProperty(SchemaDateOptions.prototype, 'min', opts); + +/** + * If set, Mongoose adds a validator that checks that this path is before the + * given `max`. + * + * @api public + * @property max + * @memberOf SchemaDateOptions + * @type {Date} + * @instance + */ + +Object.defineProperty(SchemaDateOptions.prototype, 'max', opts); + +/** + * If set, Mongoose creates a TTL index on this path. + * + * mongo TTL index `expireAfterSeconds` value will take 'expires' value expressed in seconds. + * + * #### Example: + * + * const schema = new Schema({ "expireAt": { type: Date, expires: 11 } }); + * // if 'expireAt' is set, then document expires at expireAt + 11 seconds + * + * @api public + * @property expires + * @memberOf SchemaDateOptions + * @type {Date} + * @instance + */ + +Object.defineProperty(SchemaDateOptions.prototype, 'expires', opts); + +/*! + * ignore + */ + +module.exports = SchemaDateOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaDocumentArrayOptions.js b/backend/node_modules/mongoose/lib/options/schemaDocumentArrayOptions.js new file mode 100644 index 0000000..9cf409f --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaDocumentArrayOptions.js @@ -0,0 +1,91 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on an Document Array schematype. + * + * #### Example: + * + * const schema = new Schema({ users: [{ name: string }] }); + * schema.path('users').options; // SchemaDocumentArrayOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaDocumentOptions + */ + +class SchemaDocumentArrayOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If `true`, Mongoose will skip building any indexes defined in this array's schema. + * If not set, Mongoose will build all indexes defined in this array's schema. + * + * #### Example: + * + * const childSchema = Schema({ name: { type: String, index: true } }); + * // If `excludeIndexes` is `true`, Mongoose will skip building an index + * // on `arr.name`. Otherwise, Mongoose will build an index on `arr.name`. + * const parentSchema = Schema({ + * arr: { type: [childSchema], excludeIndexes: true } + * }); + * + * @api public + * @property excludeIndexes + * @memberOf SchemaDocumentArrayOptions + * @type {Array} + * @instance + */ + +Object.defineProperty(SchemaDocumentArrayOptions.prototype, 'excludeIndexes', opts); + +/** + * If set, overwrites the child schema's `_id` option. + * + * #### Example: + * + * const childSchema = Schema({ name: String }); + * const parentSchema = Schema({ + * child: { type: childSchema, _id: false } + * }); + * parentSchema.path('child').schema.options._id; // false + * + * @api public + * @property _id + * @memberOf SchemaDocumentArrayOptions + * @type {Array} + * @instance + */ + +Object.defineProperty(SchemaDocumentArrayOptions.prototype, '_id', opts); + +/** + * Defines embedded discriminators for this document array path. + * + * #### Example: + * + * const eventSchema = Schema({ message: String }, { discriminatorKey: 'kind' }); + * const clickedSchema = Schema({ element: String }); + * const parentSchema = Schema({ + * events: [{ + * type: eventSchema, + * discriminators: { Clicked: clickedSchema } + * }] + * }); + * + * @api public + * @property discriminators + * @memberOf SchemaDocumentArrayOptions + * @type {Object} + * @instance + */ + +Object.defineProperty(SchemaDocumentArrayOptions.prototype, 'discriminators', opts); + +/*! + * ignore + */ + +module.exports = SchemaDocumentArrayOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaMapOptions.js b/backend/node_modules/mongoose/lib/options/schemaMapOptions.js new file mode 100644 index 0000000..d6de6a4 --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaMapOptions.js @@ -0,0 +1,43 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a Map schematype. + * + * #### Example: + * + * const schema = new Schema({ socialMediaHandles: { type: Map, of: String } }); + * schema.path('socialMediaHandles').options; // SchemaMapOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaMapOptions + */ + +class SchemaMapOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If set, specifies the type of this map's values. Mongoose will cast + * this map's values to the given type. + * + * If not set, Mongoose will not cast the map's values. + * + * #### Example: + * + * // Mongoose will cast `socialMediaHandles` values to strings + * const schema = new Schema({ socialMediaHandles: { type: Map, of: String } }); + * schema.path('socialMediaHandles').options.of; // String + * + * @api public + * @property of + * @memberOf SchemaMapOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaMapOptions.prototype, 'of', opts); + +module.exports = SchemaMapOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaNumberOptions.js b/backend/node_modules/mongoose/lib/options/schemaNumberOptions.js new file mode 100644 index 0000000..f30f669 --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaNumberOptions.js @@ -0,0 +1,101 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a Number schematype. + * + * #### Example: + * + * const schema = new Schema({ count: Number }); + * schema.path('count').options; // SchemaNumberOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaNumberOptions + */ + +class SchemaNumberOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If set, Mongoose adds a validator that checks that this path is at least the + * given `min`. + * + * @api public + * @property min + * @memberOf SchemaNumberOptions + * @type {number} + * @instance + */ + +Object.defineProperty(SchemaNumberOptions.prototype, 'min', opts); + +/** + * If set, Mongoose adds a validator that checks that this path is less than the + * given `max`. + * + * @api public + * @property max + * @memberOf SchemaNumberOptions + * @type {number} + * @instance + */ + +Object.defineProperty(SchemaNumberOptions.prototype, 'max', opts); + +/** + * If set, Mongoose adds a validator that checks that this path is strictly + * equal to one of the given values. + * + * #### Example: + * + * const schema = new Schema({ + * favoritePrime: { + * type: Number, + * enum: [3, 5, 7] + * } + * }); + * schema.path('favoritePrime').options.enum; // [3, 5, 7] + * + * @api public + * @property enum + * @memberOf SchemaNumberOptions + * @type {Array} + * @instance + */ + +Object.defineProperty(SchemaNumberOptions.prototype, 'enum', opts); + +/** + * Sets default [populate options](https://mongoosejs.com/docs/populate.html#query-conditions). + * + * #### Example: + * + * const schema = new Schema({ + * child: { + * type: Number, + * ref: 'Child', + * populate: { select: 'name' } + * } + * }); + * const Parent = mongoose.model('Parent', schema); + * + * // Automatically adds `.select('name')` + * Parent.findOne().populate('child'); + * + * @api public + * @property populate + * @memberOf SchemaNumberOptions + * @type {object} + * @instance + */ + +Object.defineProperty(SchemaNumberOptions.prototype, 'populate', opts); + +/*! + * ignore + */ + +module.exports = SchemaNumberOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaObjectIdOptions.js b/backend/node_modules/mongoose/lib/options/schemaObjectIdOptions.js new file mode 100644 index 0000000..776beef --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaObjectIdOptions.js @@ -0,0 +1,64 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on an ObjectId schematype. + * + * #### Example: + * + * const schema = new Schema({ testId: mongoose.ObjectId }); + * schema.path('testId').options; // SchemaObjectIdOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaObjectIdOptions + */ + +class SchemaObjectIdOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If truthy, uses Mongoose's default built-in ObjectId path. + * + * @api public + * @property auto + * @memberOf SchemaObjectIdOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(SchemaObjectIdOptions.prototype, 'auto', opts); + +/** + * Sets default [populate options](https://mongoosejs.com/docs/populate.html#query-conditions). + * + * #### Example: + * + * const schema = new Schema({ + * child: { + * type: 'ObjectId', + * ref: 'Child', + * populate: { select: 'name' } + * } + * }); + * const Parent = mongoose.model('Parent', schema); + * + * // Automatically adds `.select('name')` + * Parent.findOne().populate('child'); + * + * @api public + * @property populate + * @memberOf SchemaObjectIdOptions + * @type {object} + * @instance + */ + +Object.defineProperty(SchemaObjectIdOptions.prototype, 'populate', opts); + +/*! + * ignore + */ + +module.exports = SchemaObjectIdOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaStringOptions.js b/backend/node_modules/mongoose/lib/options/schemaStringOptions.js new file mode 100644 index 0000000..23c790f --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaStringOptions.js @@ -0,0 +1,138 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a string schematype. + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * schema.path('name').options; // SchemaStringOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaStringOptions + */ + +class SchemaStringOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * Array of allowed values for this path + * + * @api public + * @property enum + * @memberOf SchemaStringOptions + * @type {Array} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'enum', opts); + +/** + * Attach a validator that succeeds if the data string matches the given regular + * expression, and fails otherwise. + * + * @api public + * @property match + * @memberOf SchemaStringOptions + * @type {RegExp} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'match', opts); + +/** + * If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that lowercases this string + * using JavaScript's built-in `String#toLowerCase()`. + * + * @api public + * @property lowercase + * @memberOf SchemaStringOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'lowercase', opts); + +/** + * If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that removes leading and trailing + * whitespace using [JavaScript's built-in `String#trim()`](https://masteringjs.io/tutorials/fundamentals/trim-string). + * + * @api public + * @property trim + * @memberOf SchemaStringOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'trim', opts); + +/** + * If truthy, Mongoose will add a custom setter that uppercases this string + * using JavaScript's built-in [`String#toUpperCase()`](https://masteringjs.io/tutorials/fundamentals/uppercase). + * + * @api public + * @property uppercase + * @memberOf SchemaStringOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'uppercase', opts); + +/** + * If set, Mongoose will add a custom validator that ensures the given + * string's `length` is at least the given number. + * + * Mongoose supports two different spellings for this option: `minLength` and `minlength`. + * `minLength` is the recommended way to specify this option, but Mongoose also supports + * `minlength` (lowercase "l"). + * + * @api public + * @property minLength + * @memberOf SchemaStringOptions + * @type {number} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'minLength', opts); +Object.defineProperty(SchemaStringOptions.prototype, 'minlength', opts); + +/** + * If set, Mongoose will add a custom validator that ensures the given + * string's `length` is at most the given number. + * + * Mongoose supports two different spellings for this option: `maxLength` and `maxlength`. + * `maxLength` is the recommended way to specify this option, but Mongoose also supports + * `maxlength` (lowercase "l"). + * + * @api public + * @property maxLength + * @memberOf SchemaStringOptions + * @type {number} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'maxLength', opts); +Object.defineProperty(SchemaStringOptions.prototype, 'maxlength', opts); + +/** + * Sets default [populate options](https://mongoosejs.com/docs/populate.html#query-conditions). + * + * @api public + * @property populate + * @memberOf SchemaStringOptions + * @type {object} + * @instance + */ + +Object.defineProperty(SchemaStringOptions.prototype, 'populate', opts); + +/*! + * ignore + */ + +module.exports = SchemaStringOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaSubdocumentOptions.js b/backend/node_modules/mongoose/lib/options/schemaSubdocumentOptions.js new file mode 100644 index 0000000..e5d581b --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaSubdocumentOptions.js @@ -0,0 +1,89 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a single nested schematype. + * + * #### Example: + * + * const schema = Schema({ child: Schema({ name: String }) }); + * schema.path('child').options; // SchemaSubdocumentOptions instance + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaSubdocumentOptions + */ + +class SchemaSubdocumentOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If set, overwrites the child schema's `_id` option. + * + * #### Example: + * + * const childSchema = Schema({ name: String }); + * const parentSchema = Schema({ + * child: { type: childSchema, _id: false } + * }); + * parentSchema.path('child').schema.options._id; // false + * + * @api public + * @property _id + * @memberOf SchemaSubdocumentOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaSubdocumentOptions.prototype, '_id', opts); + +/** + * If set, overwrites the child schema's `minimize` option. In addition, configures whether the entire + * subdocument can be minimized out. + * + * #### Example: + * + * const childSchema = Schema({ name: String }); + * const parentSchema = Schema({ + * child: { type: childSchema, minimize: false } + * }); + * const ParentModel = mongoose.model('Parent', parentSchema); + * // Saves `{ child: {} }` to the db. Without `minimize: false`, Mongoose would remove the empty + * // object and save `{}` to the db. + * await ParentModel.create({ child: {} }); + * + * @api public + * @property minimize + * @memberOf SchemaSubdocumentOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaSubdocumentOptions.prototype, 'minimize', opts); + +/** + * Defines embedded discriminators for this single nested path. + * + * #### Example: + * + * const eventSchema = Schema({ message: String }, { discriminatorKey: 'kind' }); + * const clickedSchema = Schema({ element: String }); + * const parentSchema = Schema({ + * event: { + * type: eventSchema, + * discriminators: { Clicked: clickedSchema } + * } + * }); + * + * @api public + * @property discriminators + * @memberOf SchemaSubdocumentOptions + * @type {Object} + * @instance + */ + +Object.defineProperty(SchemaSubdocumentOptions.prototype, 'discriminators', opts); + +module.exports = SchemaSubdocumentOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaTypeOptions.js b/backend/node_modules/mongoose/lib/options/schemaTypeOptions.js new file mode 100644 index 0000000..3977d9d --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaTypeOptions.js @@ -0,0 +1,244 @@ +'use strict'; + +const clone = require('../helpers/clone'); + +/** + * The options defined on a schematype. + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * schema.path('name').options instanceof mongoose.SchemaTypeOptions; // true + * + * @api public + * @constructor SchemaTypeOptions + */ + +class SchemaTypeOptions { + constructor(obj) { + if (obj == null) { + return this; + } + Object.assign(this, clone(obj)); + } +} + +const opts = require('./propertyOptions'); + +/** + * The type to cast this path to. + * + * @api public + * @property type + * @memberOf SchemaTypeOptions + * @type {Function|string|object} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'type', opts); + +/** + * Function or object describing how to validate this schematype. + * + * @api public + * @property validate + * @memberOf SchemaTypeOptions + * @type {Function|object} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'validate', opts); + +/** + * Allows overriding casting logic for this individual path. If a string, the + * given string overwrites Mongoose's default cast error message. + * + * #### Example: + * + * const schema = new Schema({ + * num: { + * type: Number, + * cast: '{VALUE} is not a valid number' + * } + * }); + * + * // Throws 'CastError: "bad" is not a valid number' + * schema.path('num').cast('bad'); + * + * const Model = mongoose.model('Test', schema); + * const doc = new Model({ num: 'fail' }); + * const err = doc.validateSync(); + * + * err.errors['num']; // 'CastError: "fail" is not a valid number' + * + * @api public + * @property cast + * @memberOf SchemaTypeOptions + * @type {string} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'cast', opts); + +/** + * If true, attach a required validator to this path, which ensures this path + * cannot be set to a nullish value. If a function, Mongoose calls the + * function and only checks for nullish values if the function returns a truthy value. + * + * @api public + * @property required + * @memberOf SchemaTypeOptions + * @type {Function|boolean} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'required', opts); + +/** + * The default value for this path. If a function, Mongoose executes the function + * and uses the return value as the default. + * + * @api public + * @property default + * @memberOf SchemaTypeOptions + * @type {Function|any} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'default', opts); + +/** + * The model that `populate()` should use if populating this path. + * + * @api public + * @property ref + * @memberOf SchemaTypeOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'ref', opts); + +/** + * The path in the document that `populate()` should use to find the model + * to use. + * + * @api public + * @property ref + * @memberOf SchemaTypeOptions + * @type {Function|string} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'refPath', opts); + +/** + * Whether to include or exclude this path by default when loading documents + * using `find()`, `findOne()`, etc. + * + * @api public + * @property select + * @memberOf SchemaTypeOptions + * @type {boolean|number} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'select', opts); + +/** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * build an index on this path when the model is compiled. + * + * @api public + * @property index + * @memberOf SchemaTypeOptions + * @type {boolean|number|object} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'index', opts); + +/** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose + * will build a unique index on this path when the + * model is compiled. [The `unique` option is **not** a validator](https://mongoosejs.com/docs/validation.html#the-unique-option-is-not-a-validator). + * + * @api public + * @property unique + * @memberOf SchemaTypeOptions + * @type {boolean|number} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'unique', opts); + +/** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * disallow changes to this path once the document + * is saved to the database for the first time. Read more about [immutability in Mongoose here](https://thecodebarbarian.com/whats-new-in-mongoose-5-6-immutable-properties.html). + * + * @api public + * @property immutable + * @memberOf SchemaTypeOptions + * @type {Function|boolean} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'immutable', opts); + +/** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * build a sparse index on this path. + * + * @api public + * @property sparse + * @memberOf SchemaTypeOptions + * @type {boolean|number} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'sparse', opts); + +/** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose + * will build a text index on this path. + * + * @api public + * @property text + * @memberOf SchemaTypeOptions + * @type {boolean|number|object} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'text', opts); + +/** + * Define a transform function for this individual schema type. + * Only called when calling `toJSON()` or `toObject()`. + * + * #### Example: + * + * const schema = Schema({ + * myDate: { + * type: Date, + * transform: v => v.getFullYear() + * } + * }); + * const Model = mongoose.model('Test', schema); + * + * const doc = new Model({ myDate: new Date('2019/06/01') }); + * doc.myDate instanceof Date; // true + * + * const res = doc.toObject({ transform: true }); + * res.myDate; // 2019 + * + * @api public + * @property transform + * @memberOf SchemaTypeOptions + * @type {Function} + * @instance + */ + +Object.defineProperty(SchemaTypeOptions.prototype, 'transform', opts); + +module.exports = SchemaTypeOptions; diff --git a/backend/node_modules/mongoose/lib/options/schemaUnionOptions.js b/backend/node_modules/mongoose/lib/options/schemaUnionOptions.js new file mode 100644 index 0000000..1fcbc65 --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/schemaUnionOptions.js @@ -0,0 +1,32 @@ +'use strict'; + +const SchemaTypeOptions = require('./schemaTypeOptions'); + +/** + * The options defined on a Union schematype. + * + * @api public + * @inherits SchemaTypeOptions + * @constructor SchemaUnionOptions + */ + +class SchemaUnionOptions extends SchemaTypeOptions {} + +const opts = require('./propertyOptions'); + +/** + * If set, specifies the types that this union can take. Mongoose will cast + * the value to one of the given types. + * + * If not set, Mongoose will not cast the value to any specific type. + * + * @api public + * @property of + * @memberOf SchemaUnionOptions + * @type {Function|Function[]|string|string[]} + * @instance + */ + +Object.defineProperty(SchemaUnionOptions.prototype, 'of', opts); + +module.exports = SchemaUnionOptions; diff --git a/backend/node_modules/mongoose/lib/options/virtualOptions.js b/backend/node_modules/mongoose/lib/options/virtualOptions.js new file mode 100644 index 0000000..3c4080e --- /dev/null +++ b/backend/node_modules/mongoose/lib/options/virtualOptions.js @@ -0,0 +1,164 @@ +'use strict'; + +const opts = require('./propertyOptions'); + +class VirtualOptions { + constructor(obj) { + Object.assign(this, obj); + + if (obj?.options != null) { + this.options = Object.assign({}, obj.options); + } + } +} + +/** + * Marks this virtual as a populate virtual, and specifies the model to + * use for populate. + * + * @api public + * @property ref + * @memberOf VirtualOptions + * @type {string|Model|Function} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'ref', opts); + +/** + * Marks this virtual as a populate virtual, and specifies the path that + * contains the name of the model to populate + * + * @api public + * @property refPath + * @memberOf VirtualOptions + * @type {string|Function} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'refPath', opts); + +/** + * The name of the property in the local model to match to `foreignField` + * in the foreign model. + * + * @api public + * @property localField + * @memberOf VirtualOptions + * @type {string|Function} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'localField', opts); + +/** + * The name of the property in the foreign model to match to `localField` + * in the local model. + * + * @api public + * @property foreignField + * @memberOf VirtualOptions + * @type {string|Function} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'foreignField', opts); + +/** + * Whether to populate this virtual as a single document (true) or an + * array of documents (false). + * + * @api public + * @property justOne + * @memberOf VirtualOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'justOne', opts); + +/** + * If true, populate just the number of documents where `localField` + * matches `foreignField`, as opposed to the documents themselves. + * + * If `count` is set, it overrides `justOne`. + * + * @api public + * @property count + * @memberOf VirtualOptions + * @type {boolean} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'count', opts); + +/** + * Add an additional filter to populate, in addition to `localField` + * matches `foreignField`. + * + * @api public + * @property match + * @memberOf VirtualOptions + * @type {object|Function} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'match', opts); + +/** + * Additional options to pass to the query used to `populate()`: + * + * - `sort` + * - `skip` + * - `limit` + * + * @api public + * @property options + * @memberOf VirtualOptions + * @type {object} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'options', opts); + +/** + * If true, add a `skip` to the query used to `populate()`. + * + * @api public + * @property skip + * @memberOf VirtualOptions + * @type {number} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'skip', opts); + +/** + * If true, add a `limit` to the query used to `populate()`. + * + * @api public + * @property limit + * @memberOf VirtualOptions + * @type {number} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'limit', opts); + +/** + * The `limit` option for `populate()` has [some unfortunate edge cases](https://mongoosejs.com/docs/populate.html#query-conditions) + * when working with multiple documents, like `.find().populate()`. The + * `perDocumentLimit` option makes `populate()` execute a separate query + * for each document returned from `find()` to ensure each document + * gets up to `perDocumentLimit` populated docs if possible. + * + * @api public + * @property perDocumentLimit + * @memberOf VirtualOptions + * @type {number} + * @instance + */ + +Object.defineProperty(VirtualOptions.prototype, 'perDocumentLimit', opts); + +module.exports = VirtualOptions; diff --git a/backend/node_modules/mongoose/lib/plugins/index.js b/backend/node_modules/mongoose/lib/plugins/index.js new file mode 100644 index 0000000..c0714f8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/plugins/index.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.saveSubdocs = require('./saveSubdocs'); +exports.sharding = require('./sharding'); +exports.trackTransaction = require('./trackTransaction'); diff --git a/backend/node_modules/mongoose/lib/plugins/saveSubdocs.js b/backend/node_modules/mongoose/lib/plugins/saveSubdocs.js new file mode 100644 index 0000000..fb45587 --- /dev/null +++ b/backend/node_modules/mongoose/lib/plugins/saveSubdocs.js @@ -0,0 +1,94 @@ +'use strict'; + +const symbols = require('../schema/symbols'); + +/*! + * ignore + */ + +module.exports = function saveSubdocs(schema) { + const unshift = true; + + schema.s.hooks.pre('save', false, saveSubdocsPreSave, null, unshift); + schema.s.hooks.post('save', saveSubdocsPostSave, null, unshift); + schema.s.hooks.pre('save', saveSubdocsPreDeleteOne); + schema.s.hooks.post('save', saveSubdocsPostDeleteOne); +}; + + +async function saveSubdocsPreSave() { + if (this.$isSubdocument) { + return; + } + + const subdocs = this.$getAllSubdocs({ useCache: true }); + + if (!subdocs.length) { + return; + } + + const options = this.$__.saveOptions; + await Promise.all(subdocs.map(subdoc => subdoc._execDocumentPreHooks('save', options, [options]))); + + // Invalidate subdocs cache because subdoc pre hooks can add new subdocuments + if (this.$__.saveOptions) { + this.$__.saveOptions.__subdocs = null; + } +} + +async function saveSubdocsPostSave() { + if (this.$isSubdocument) { + return; + } + + const subdocs = this.$getAllSubdocs({ useCache: true }); + + if (!subdocs.length) { + return; + } + + const options = this.$__.saveOptions; + const promises = []; + for (const subdoc of subdocs) { + promises.push(subdoc._execDocumentPostHooks('save', options)); + } + + await Promise.all(promises); +} + +async function saveSubdocsPreDeleteOne() { + const removedSubdocs = this.$__.removedSubdocs; + if (!removedSubdocs?.length) { + return; + } + + const options = this.$__.saveOptions; + const promises = []; + for (const subdoc of removedSubdocs) { + promises.push(subdoc._execDocumentPreHooks('deleteOne', options)); + } + + await Promise.all(promises); +} + +async function saveSubdocsPostDeleteOne() { + const removedSubdocs = this.$__.removedSubdocs; + if (!removedSubdocs?.length) { + return; + } + + const options = this.$__.saveOptions; + const promises = []; + for (const subdoc of removedSubdocs) { + promises.push(subdoc._execDocumentPostHooks('deleteOne', options)); + } + + this.$__.removedSubdocs = null; + await Promise.all(promises); +} + + +saveSubdocsPreSave[symbols.builtInMiddleware] = true; +saveSubdocsPostSave[symbols.builtInMiddleware] = true; +saveSubdocsPreDeleteOne[symbols.builtInMiddleware] = true; +saveSubdocsPostDeleteOne[symbols.builtInMiddleware] = true; diff --git a/backend/node_modules/mongoose/lib/plugins/sharding.js b/backend/node_modules/mongoose/lib/plugins/sharding.js new file mode 100644 index 0000000..21beb22 --- /dev/null +++ b/backend/node_modules/mongoose/lib/plugins/sharding.js @@ -0,0 +1,101 @@ +'use strict'; + +const objectIdSymbol = require('../helpers/symbols').objectIdSymbol; +const symbols = require('../schema/symbols'); +const utils = require('../utils'); + +/*! + * ignore + */ + +module.exports = function shardingPlugin(schema) { + schema.post('init', shardingPluginPostInit); + schema.pre('save', shardingPluginPreSave); + schema.post('save', shardingPluginPostSave); + schema.pre('deleteOne', { document: true, query: false }, shardingPluginPreDeleteOne); + schema.pre('updateOne', { document: true, query: false }, shardingPluginPreUpdateOne); +}; + +function shardingPluginPostInit() { + storeShard.call(this); + return this; +} + +function shardingPluginPreSave() { + applyWhere.call(this); +} + +function shardingPluginPostSave() { + storeShard.call(this); +} + +function shardingPluginPreDeleteOne() { + applyWhere.call(this); +} + +function shardingPluginPreUpdateOne() { + applyWhere.call(this); +} + +/*! + * ignore + */ + +function applyWhere() { + let paths; + let len; + + if (this.$__.shardval) { + paths = Object.keys(this.$__.shardval); + len = paths.length; + + this.$where = this.$where || {}; + for (let i = 0; i < len; ++i) { + this.$where[paths[i]] = this.$__.shardval[paths[i]]; + } + } +} + +/*! + * ignore + */ + +module.exports.storeShard = storeShard; + +/*! + * ignore + */ + +function storeShard() { + // backwards compat + const key = this.$__schema.options.shardKey || this.$__schema.options.shardkey; + if (!utils.isPOJO(key)) { + return; + } + + const orig = this.$__.shardval = {}; + const paths = Object.keys(key); + const len = paths.length; + let val; + + for (let i = 0; i < len; ++i) { + val = this.$__getValue(paths[i]); + if (val == null) { + orig[paths[i]] = val; + } else if (utils.isMongooseObject(val)) { + orig[paths[i]] = val.toObject({ depopulate: true, _isNested: true }); + } else if (val instanceof Date || val[objectIdSymbol]) { + orig[paths[i]] = val; + } else if (typeof val.valueOf === 'function') { + orig[paths[i]] = val.valueOf(); + } else { + orig[paths[i]] = val; + } + } +} + +shardingPluginPostInit[symbols.builtInMiddleware] = true; +shardingPluginPreSave[symbols.builtInMiddleware] = true; +shardingPluginPostSave[symbols.builtInMiddleware] = true; +shardingPluginPreDeleteOne[symbols.builtInMiddleware] = true; +shardingPluginPreUpdateOne[symbols.builtInMiddleware] = true; diff --git a/backend/node_modules/mongoose/lib/plugins/trackTransaction.js b/backend/node_modules/mongoose/lib/plugins/trackTransaction.js new file mode 100644 index 0000000..7dec70d --- /dev/null +++ b/backend/node_modules/mongoose/lib/plugins/trackTransaction.js @@ -0,0 +1,89 @@ +'use strict'; + +const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol; +const sessionNewDocuments = require('../helpers/symbols').sessionNewDocuments; +const symbols = require('../schema/symbols'); +const utils = require('../utils'); + +module.exports = function trackTransaction(schema) { + schema.pre('save', trackTransactionPreSave); +}; + +function trackTransactionPreSave() { + const session = this.$session(); + if (session == null) { + return; + } + if (session.transaction == null || session[sessionNewDocuments] == null) { + return; + } + + if (!session[sessionNewDocuments].has(this)) { + const initialState = {}; + if (this.isNew) { + initialState.isNew = true; + } + if (this.$__schema.options.versionKey) { + initialState.versionKey = this.get(this.$__schema.options.versionKey); + } + + initialState.modifiedPaths = new Set(Object.keys(this.$__.activePaths.getStatePaths('modify'))); + initialState.atomics = _getAtomics(this); + + session[sessionNewDocuments].set(this, initialState); + } +} + +function _getAtomics(doc, previous) { + const pathToAtomics = new Map(); + previous = previous || new Map(); + + const pathsToCheck = Object.keys(doc.$__.activePaths.init).concat(Object.keys(doc.$__.activePaths.modify)); + + for (const path of pathsToCheck) { + const val = doc.$__getValue(path); + if (Array.isArray(val) && + utils.isMongooseDocumentArray(val) && + val.length && + val[arrayAtomicsSymbol] != null && + utils.hasOwnKeys(val[arrayAtomicsSymbol])) { + const existing = previous.get(path) || {}; + pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); + } + } + + const dirty = doc.$__dirty(); + for (const dirt of dirty) { + const path = dirt.path; + + const val = dirt.value; + if (val?.[arrayAtomicsSymbol] != null && utils.hasOwnKeys(val[arrayAtomicsSymbol])) { + const existing = previous.get(path) || {}; + pathToAtomics.set(path, mergeAtomics(existing, val[arrayAtomicsSymbol])); + } + } + + return pathToAtomics; +} + +function mergeAtomics(destination, source) { + destination = destination || {}; + + if (source.$pullAll != null) { + destination.$pullAll = (destination.$pullAll || []).concat(source.$pullAll); + } + if (source.$push != null) { + destination.$push = destination.$push || {}; + destination.$push.$each = (destination.$push.$each || []).concat(source.$push.$each); + } + if (source.$addToSet != null) { + destination.$addToSet = (destination.$addToSet || []).concat(source.$addToSet); + } + if (source.$set != null) { + destination.$set = Array.isArray(source.$set) ? [...source.$set] : Object.assign({}, source.$set); + } + + return destination; +} + +trackTransactionPreSave[symbols.builtInMiddleware] = true; diff --git a/backend/node_modules/mongoose/lib/query.js b/backend/node_modules/mongoose/lib/query.js new file mode 100644 index 0000000..572312b --- /dev/null +++ b/backend/node_modules/mongoose/lib/query.js @@ -0,0 +1,5760 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('./error/cast'); +const DocumentNotFoundError = require('./error/notFound'); +const Kareem = require('kareem'); +const MongooseError = require('./error/mongooseError'); +const ObjectParameterError = require('./error/objectParameter'); +const QueryCursor = require('./cursor/queryCursor'); +const ValidationError = require('./error/validation'); +const { applyGlobalMaxTimeMS, applyGlobalDiskUse } = require('./helpers/query/applyGlobalOption'); +const handleReadPreferenceAliases = require('./helpers/query/handleReadPreferenceAliases'); +const applyReadConcern = require('./helpers/schema/applyReadConcern'); +const applyWriteConcern = require('./helpers/schema/applyWriteConcern'); +const cast = require('./cast'); +const castArrayFilters = require('./helpers/update/castArrayFilters'); +const castNumber = require('./cast/number'); +const castUpdate = require('./helpers/query/castUpdate'); +const clone = require('./helpers/clone'); +const getDiscriminatorByValue = require('./helpers/discriminator/getDiscriminatorByValue'); +const helpers = require('./queryHelpers'); +const internalToObjectOptions = require('./options').internalToObjectOptions; +const isExclusive = require('./helpers/projection/isExclusive'); +const isInclusive = require('./helpers/projection/isInclusive'); +const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive'); +const isSubpath = require('./helpers/projection/isSubpath'); +const mpath = require('mpath'); +const mquery = require('mquery'); +const parseProjection = require('./helpers/projection/parseProjection'); +const removeUnusedArrayFilters = require('./helpers/update/removeUnusedArrayFilters'); +const sanitizeFilter = require('./helpers/query/sanitizeFilter'); +const sanitizeProjection = require('./helpers/query/sanitizeProjection'); +const { buildMiddlewareFilter } = require('./helpers/buildMiddlewareFilter'); +const selectPopulatedFields = require('./helpers/query/selectPopulatedFields'); +const setDefaultsOnInsert = require('./helpers/setDefaultsOnInsert'); +const specialProperties = require('./helpers/specialProperties'); +const updateValidators = require('./helpers/updateValidators'); +const util = require('util'); +const utils = require('./utils'); +const queryMiddlewareFunctions = require('./constants').queryMiddlewareFunctions; + +const queryOptionMethods = new Set([ + 'allowDiskUse', + 'batchSize', + 'collation', + 'comment', + 'explain', + 'hint', + 'j', + 'lean', + 'limit', + 'maxTimeMS', + 'populate', + 'projection', + 'read', + 'select', + 'skip', + 'slice', + 'sort', + 'tailable', + 'w', + 'writeConcern', + 'wtimeout' +]); + +// Map from operation name to the name of the function that executes the actual operation against MongoDB. +// Called a thunk for legacy reasons, "thunk" means function that takes exactly 1 param, a callback. +// Currently `_countDocuments()`, etc. are async functions that take no params. +const opToThunk = new Map([ + ['countDocuments', '_countDocuments'], + ['distinct', '__distinct'], + ['estimatedDocumentCount', '_estimatedDocumentCount'], + ['find', '_find'], + ['findOne', '_findOne'], + ['findOneAndReplace', '_findOneAndReplace'], + ['findOneAndUpdate', '_findOneAndUpdate'], + ['replaceOne', '_replaceOne'], + ['updateMany', '_updateMany'], + ['updateOne', '_updateOne'], + ['deleteMany', '_deleteMany'], + ['deleteOne', '_deleteOne'], + ['findOneAndDelete', '_findOneAndDelete'] +]); + +/** + * Query constructor used for building queries. You do not need + * to instantiate a `Query` directly. Instead use Model functions like + * [`Model.find()`](https://mongoosejs.com/docs/api/model.html#Model.find()). + * + * #### Example: + * + * const query = MyModel.find(); // `query` is an instance of `Query` + * query.setOptions({ lean : true }); + * query.collection(MyModel.collection); + * query.where('age').gte(21).exec(callback); + * + * // You can instantiate a query directly. There is no need to do + * // this unless you're an advanced user with a very good reason to. + * const query = new mongoose.Query(); + * + * @param {object} [options] + * @param {object} [model] + * @param {object} [conditions] + * @param {object} [collection] Mongoose collection + * @api public + */ + +function Query(conditions, options, model, collection) { + // this stuff is for dealing with custom queries created by #toConstructor + if (!this._mongooseOptions) { + this._mongooseOptions = {}; + } + options = options || {}; + + this._transforms = []; + this._hooks = new Kareem(); + this._execCount = 0; + + // this is the case where we have a CustomQuery, we need to check if we got + // options passed in, and if we did, merge them in + const keys = Object.keys(options); + for (const key of keys) { + this._mongooseOptions[key] = options[key]; + } + + if (collection) { + this.mongooseCollection = collection; + } + + if (model) { + this.model = model; + this.schema = model.schema; + } + + // this is needed because map reduce returns a model that can be queried, but + // all of the queries on said model should be lean + if (this.model?._mapreduce) { + this.lean(); + } + + // inherit mquery + mquery.call(this, null, options); + if (collection) { + this.collection(collection); + } + + if (conditions) { + this.find(conditions); + } + + this.options = this.options || {}; + + // For gh-6880. mquery still needs to support `fields` by default for old + // versions of MongoDB + this.$useProjection = true; + + const collation = this?.schema?.options?.collation || null; + if (collation != null) { + this.options.collation = collation; + } +} + +// Helper function to check if an object is empty or contains only empty objects/arrays +function isEmptyFilter(obj) { + if (obj == null) return true; + if (typeof obj !== 'object') return true; + if (utils.hasOwnKeys(obj) === false) return true; + + // Check $and, $or, $nor arrays + for (const key of ['$and', '$or', '$nor']) { + if (Array.isArray(obj[key])) { + // If array is empty or all elements are empty objects, consider it empty + if (obj[key].length === 0 || obj[key].every(item => isEmptyFilter(item))) { + return true; + } + } + } + + return false; +} + +// Helper function to check for empty/invalid filter +function checkRequireFilter(filter, options) { + if (options?.requireFilter && isEmptyFilter(filter)) { + throw new MongooseError('Empty or invalid filter not allowed with requireFilter enabled'); + } +} + +/*! + * inherit mquery + */ + +Query.prototype = new mquery(); +Query.prototype.constructor = Query; + +// Remove some legacy methods that we removed in Mongoose 8, but +// are still in mquery 5. +Query.prototype.count = undefined; +Query.prototype.findOneAndRemove = undefined; + +Query.base = mquery.prototype; + +/*! + * Overwrite mquery's `_distinct`, because Mongoose uses that name + * to store the field to apply distinct on. + */ + +Object.defineProperty(Query.prototype, '_distinct', { + configurable: true, + writable: true, + enumerable: true, + value: undefined +}); + +/** + * Flag to opt out of using `$geoWithin`. + * + * ```javascript + * mongoose.Query.use$geoWithin = false; + * ``` + * + * MongoDB 2.4 deprecated the use of `$within`, replacing it with `$geoWithin`. Mongoose uses `$geoWithin` by default (which is 100% backward compatible with `$within`). If you are running an older version of MongoDB, set this flag to `false` so your `within()` queries continue to work. + * + * @see geoWithin https://www.mongodb.com/docs/manual/reference/operator/geoWithin/ + * @default true + * @property use$geoWithin + * @memberOf Query + * @static + * @api public + */ + +Query.use$geoWithin = mquery.use$geoWithin; + +/** + * Converts this query to a customized, reusable query constructor with all arguments and options retained. + * + * #### Example: + * + * // Create a query for adventure movies and read from the primary + * // node in the replica-set unless it is down, in which case we'll + * // read from a secondary node. + * const query = Movie.find({ tags: 'adventure' }).read('primaryPreferred'); + * + * // create a custom Query constructor based off these settings + * const Adventure = query.toConstructor(); + * + * // further narrow down our query results while still using the previous settings + * await Adventure().where({ name: /^Life/ }).exec(); + * + * // since Adventure is a stand-alone constructor we can also add our own + * // helper methods and getters without impacting global queries + * Adventure.prototype.startsWith = function (prefix) { + * this.where({ name: new RegExp('^' + prefix) }) + * return this; + * } + * Object.defineProperty(Adventure.prototype, 'highlyRated', { + * get: function () { + * this.where({ rating: { $gt: 4.5 }}); + * return this; + * } + * }) + * await Adventure().highlyRated.startsWith('Life').exec(); + * + * @return {Query} subclass-of-Query + * @api public + */ + +Query.prototype.toConstructor = function toConstructor() { + const model = this.model; + const coll = this.mongooseCollection; + + const CustomQuery = function(criteria, options) { + if (!(this instanceof CustomQuery)) { + return new CustomQuery(criteria, options); + } + this._mongooseOptions = clone(p._mongooseOptions); + Query.call(this, criteria, options || null, model, coll); + }; + + util.inherits(CustomQuery, model.Query); + + // set inherited defaults + const p = CustomQuery.prototype; + + p.options = {}; + + // Need to handle `sort()` separately because entries-style `sort()` syntax + // `sort([['prop1', 1]])` confuses mquery into losing the outer nested array. + // See gh-8159 + const options = Object.assign({}, this.options); + if (options.sort != null) { + p.sort(options.sort); + delete options.sort; + } + p.setOptions(options); + + p.op = this.op; + p._conditions = clone(this._conditions); + p._fields = clone(this._fields); + p._update = clone(this._update, { + flattenDecimals: false + }); + p._path = this._path; + p._distinct = this._distinct; + p._collection = this._collection; + p._mongooseOptions = this._mongooseOptions; + + return CustomQuery; +}; + +/** + * Make a copy of this query so you can re-execute it. + * + * #### Example: + * + * const q = Book.findOne({ title: 'Casino Royale' }); + * await q.exec(); + * await q.exec(); // Throws an error because you can't execute a query twice + * + * await q.clone().exec(); // Works + * + * @method clone + * @return {Query} copy + * @memberOf Query + * @instance + * @api public + */ + +Query.prototype.clone = function() { + const model = this.model; + const collection = this.mongooseCollection; + + const q = new this.model.Query({}, {}, model, collection); + + // Need to handle `sort()` separately because entries-style `sort()` syntax + // `sort([['prop1', 1]])` confuses mquery into losing the outer nested array. + // See gh-8159 + const options = Object.assign({}, this.options); + if (options.sort != null) { + q.sort(options.sort); + delete options.sort; + } + q.setOptions(options); + + q.op = this.op; + q._conditions = clone(this._conditions); + q._fields = clone(this._fields); + q._update = clone(this._update, { + flattenDecimals: false + }); + q._path = this._path; + q._distinct = this._distinct; + q._collection = this._collection; + q._mongooseOptions = this._mongooseOptions; + + return q; +}; + +/** + * Specifies a javascript function or expression to pass to MongoDBs query system. + * + * #### Example: + * + * query.$where('this.comments.length === 10 || this.name.length === 5') + * + * // or + * + * query.$where(function () { + * return this.comments.length === 10 || this.name.length === 5; + * }) + * + * #### Note: + * + * Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`. + * **Be sure to read about all of [its caveats](https://www.mongodb.com/docs/manual/reference/operator/where/) before using.** + * + * @see $where https://www.mongodb.com/docs/manual/reference/operator/where/ + * @method $where + * @param {string|Function} js javascript string or function + * @return {Query} this + * @memberOf Query + * @instance + * @method $where + * @api public + */ + +/** + * Specifies a `path` for use with chaining. + * + * #### Example: + * + * // instead of writing: + * User.find({age: {$gte: 21, $lte: 65}}); + * + * // we can instead write: + * User.where('age').gte(21).lte(65); + * + * // passing query conditions is permitted + * User.find().where({ name: 'vonderful' }) + * + * // chaining + * User + * .where('age').gte(21).lte(65) + * .where('name', /^vonderful/i) + * .where('friends').slice(10) + * .exec() + * + * @method where + * @memberOf Query + * @instance + * @param {string|object} [path] + * @param {any} [val] + * @return {Query} this + * @api public + */ + +/** + * Specifies a `$slice` projection for an array. + * + * #### Example: + * + * query.slice('comments', 5); // Returns the first 5 comments + * query.slice('comments', -5); // Returns the last 5 comments + * query.slice('comments', [10, 5]); // Returns the first 5 comments after the 10-th + * query.where('comments').slice(5); // Returns the first 5 comments + * query.where('comments').slice([-10, 5]); // Returns the first 5 comments after the 10-th to last + * + * **Note:** If the absolute value of the number of elements to be sliced is greater than the number of elements in the array, all array elements will be returned. + * + * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * query.slice('arr', 20); // Returns [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * query.slice('arr', -20); // Returns [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * **Note:** If the number of elements to skip is positive and greater than the number of elements in the array, an empty array will be returned. + * + * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * query.slice('arr', [20, 5]); // Returns [] + * + * **Note:** If the number of elements to skip is negative and its absolute value is greater than the number of elements in the array, the starting position is the start of the array. + * + * // Given `arr`: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * query.slice('arr', [-20, 5]); // Returns [1, 2, 3, 4, 5] + * + * @method slice + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number|Array} val number of elements to slice or array with number of elements to skip and number of elements to slice + * @return {Query} this + * @see mongodb https://www.mongodb.com/docs/manual/tutorial/query-documents/#projection + * @see $slice https://www.mongodb.com/docs/manual/reference/projection/slice/#prj._S_slice + * @api public + */ + +Query.prototype.slice = function(...args) { + if (args.length === 0) { + return this; + } + + this._validate('slice'); + + let path; + let val; + + if (args.length === 1) { + const arg = args[0]; + if (typeof arg === 'object' && !Array.isArray(arg)) { + const keys = Object.keys(arg); + const numKeys = keys.length; + for (let i = 0; i < numKeys; ++i) { + this.slice(keys[i], arg[keys[i]]); + } + return this; + } + this._ensurePath('slice'); + path = this._path; + val = args[0]; + } else if (args.length === 2) { + if ('number' === typeof args[0]) { + this._ensurePath('slice'); + path = this._path; + val = [args[0], args[1]]; + } else { + path = args[0]; + val = args[1]; + } + } else if (args.length === 3) { + path = args[0]; + val = [args[1], args[2]]; + } + + const p = {}; + p[path] = { $slice: val }; + this.select(p); + + return this; +}; + +/*! + * ignore + */ + +const validOpsSet = new Set(queryMiddlewareFunctions); + +Query.prototype._validateOp = function() { + if (this.op != null && !validOpsSet.has(this.op)) { + this.error(new Error('Query has invalid `op`: "' + this.op + '"')); + } + + if (this.op !== 'estimatedDocumentCount' && this._conditions == null) { + throw new ObjectParameterError(this._conditions, 'filter', this.op); + } +}; + +/** + * Specifies the complementary comparison value for paths specified with `where()` + * + * #### Example: + * + * User.where('age').equals(49); + * + * // is the same as + * + * User.where('age', 49); + * + * @method equals + * @memberOf Query + * @instance + * @param {object} val + * @return {Query} this + * @api public + */ + +/** + * Specifies arguments for an `$or` condition. + * + * #### Example: + * + * query.or([{ color: 'red' }, { status: 'emergency' }]); + * + * @see $or https://www.mongodb.com/docs/manual/reference/operator/or/ + * @method or + * @memberOf Query + * @instance + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +/** + * Specifies arguments for a `$nor` condition. + * + * #### Example: + * + * query.nor([{ color: 'green' }, { status: 'ok' }]); + * + * @see $nor https://www.mongodb.com/docs/manual/reference/operator/nor/ + * @method nor + * @memberOf Query + * @instance + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +/** + * Specifies arguments for a `$and` condition. + * + * #### Example: + * + * query.and([{ color: 'green' }, { status: 'ok' }]) + * + * @method and + * @memberOf Query + * @instance + * @see $and https://www.mongodb.com/docs/manual/reference/operator/and/ + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +/** + * Specifies a `$gt` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * #### Example: + * + * Thing.find().where('age').gt(21); + * + * // or + * Thing.find().gt('age', 21); + * + * @method gt + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @see $gt https://www.mongodb.com/docs/manual/reference/operator/gt/ + * @api public + */ + +/** + * Specifies a `$gte` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method gte + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @see $gte https://www.mongodb.com/docs/manual/reference/operator/gte/ + * @api public + */ + +/** + * Specifies a `$lt` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lt + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @see $lt https://www.mongodb.com/docs/manual/reference/operator/lt/ + * @api public + */ + +/** + * Specifies a `$lte` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lte + * @see $lte https://www.mongodb.com/docs/manual/reference/operator/lte/ + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @api public + */ + +/** + * Specifies a `$ne` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @see $ne https://www.mongodb.com/docs/manual/reference/operator/ne/ + * @method ne + * @memberOf Query + * @instance + * @param {string} [path] + * @param {any} val + * @api public + */ + +/** + * Specifies an `$in` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @see $in https://www.mongodb.com/docs/manual/reference/operator/in/ + * @method in + * @memberOf Query + * @instance + * @param {string} [path] + * @param {Array} val + * @api public + */ + +/** + * Specifies an `$nin` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @see $nin https://www.mongodb.com/docs/manual/reference/operator/nin/ + * @method nin + * @memberOf Query + * @instance + * @param {string} [path] + * @param {Array} val + * @api public + */ + +/** + * Specifies an `$all` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * #### Example: + * + * MyModel.find().where('pets').all(['dog', 'cat', 'ferret']); + * // Equivalent: + * MyModel.find().all('pets', ['dog', 'cat', 'ferret']); + * + * @see $all https://www.mongodb.com/docs/manual/reference/operator/all/ + * @method all + * @memberOf Query + * @instance + * @param {string} [path] + * @param {Array} val + * @api public + */ + +/** + * Specifies a `$size` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * #### Example: + * + * const docs = await MyModel.where('tags').size(0).exec(); + * assert(Array.isArray(docs)); + * console.log('documents with 0 tags', docs); + * + * @see $size https://www.mongodb.com/docs/manual/reference/operator/size/ + * @method size + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @api public + */ + +/** + * Specifies a `$regex` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @see $regex https://www.mongodb.com/docs/manual/reference/operator/regex/ + * @method regex + * @memberOf Query + * @instance + * @param {string} [path] + * @param {string|RegExp} val + * @api public + */ + +/** + * Specifies a `maxDistance` query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/ + * @method maxDistance + * @memberOf Query + * @instance + * @param {string} [path] + * @param {number} val + * @api public + */ + +/** + * Specifies a `$mod` condition, filters documents for documents whose + * `path` property is a number that is equal to `remainder` modulo `divisor`. + * + * #### Example: + * + * // All find products whose inventory is odd + * Product.find().mod('inventory', [2, 1]); + * Product.find().where('inventory').mod([2, 1]); + * // This syntax is a little strange, but supported. + * Product.find().where('inventory').mod(2, 1); + * + * @method mod + * @memberOf Query + * @instance + * @param {string} [path] + * @param {Array} val must be of length 2, first element is `divisor`, 2nd element is `remainder`. + * @return {Query} this + * @see $mod https://www.mongodb.com/docs/manual/reference/operator/mod/ + * @api public + */ + +Query.prototype.mod = function() { + let val; + let path; + + if (arguments.length === 1) { + this._ensurePath('mod'); + val = arguments[0]; + path = this._path; + } else if (arguments.length === 2 && !Array.isArray(arguments[1])) { + this._ensurePath('mod'); + val = [arguments[0], arguments[1]]; + path = this._path; + } else if (arguments.length === 3) { + val = [arguments[1], arguments[2]]; + path = arguments[0]; + } else { + val = arguments[1]; + path = arguments[0]; + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$mod = val; + return this; +}; + +/** + * Specifies an `$exists` condition + * + * #### Example: + * + * // { name: { $exists: true }} + * Thing.where('name').exists() + * Thing.where('name').exists(true) + * Thing.find().exists('name') + * + * // { name: { $exists: false }} + * Thing.where('name').exists(false); + * Thing.find().exists('name', false); + * + * @method exists + * @memberOf Query + * @instance + * @param {string} [path] + * @param {boolean} val + * @return {Query} this + * @see $exists https://www.mongodb.com/docs/manual/reference/operator/exists/ + * @api public + */ + +/** + * Specifies an `$elemMatch` condition + * + * #### Example: + * + * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}}) + * + * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}}) + * + * query.elemMatch('comment', function (elem) { + * elem.where('author').equals('autobot'); + * elem.where('votes').gte(5); + * }) + * + * query.where('comment').elemMatch(function (elem) { + * elem.where({ author: 'autobot' }); + * elem.where('votes').gte(5); + * }) + * + * @method elemMatch + * @memberOf Query + * @instance + * @param {string|object|Function} path + * @param {object|Function} filter + * @return {Query} this + * @see $elemMatch https://www.mongodb.com/docs/manual/reference/operator/elemMatch/ + * @api public + */ + +/** + * Defines a `$within` or `$geoWithin` argument for geo-spatial queries. + * + * #### Example: + * + * query.where(path).within().box() + * query.where(path).within().circle() + * query.where(path).within().geometry() + * + * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true }); + * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] }); + * query.where('loc').within({ polygon: [[],[],[],[]] }); + * + * query.where('loc').within([], [], []) // polygon + * query.where('loc').within([], []) // box + * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry + * + * **MUST** be used after `where()`. + * + * #### Note: + * + * As of Mongoose 3.7, `$geoWithin` is always used for queries. To change this behavior, see [Query.use$geoWithin](https://mongoosejs.com/docs/api/query.html#Query.prototype.use$geoWithin). + * + * #### Note: + * + * In Mongoose 3.7, `within` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within). + * + * @method within + * @see $polygon https://www.mongodb.com/docs/manual/reference/operator/polygon/ + * @see $box https://www.mongodb.com/docs/manual/reference/operator/box/ + * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/ + * @see $center https://www.mongodb.com/docs/manual/reference/operator/center/ + * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/ + * @memberOf Query + * @instance + * @return {Query} this + * @api public + */ + +/** + * Specifies the maximum number of documents the query will return. + * + * #### Example: + * + * query.limit(20); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method limit + * @memberOf Query + * @instance + * @param {number} val + * @api public + */ + +Query.prototype.limit = function limit(v) { + this._validate('limit'); + + if (typeof v === 'string') { + try { + v = castNumber(v); + } catch { + throw new CastError('Number', v, 'limit'); + } + } + + this.options.limit = v; + return this; +}; + +/** + * Specifies the number of documents to skip. + * + * #### Example: + * + * query.skip(100).limit(20); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method skip + * @memberOf Query + * @instance + * @param {number} val + * @see cursor.skip https://www.mongodb.com/docs/manual/reference/method/cursor.skip/ + * @api public + */ + +Query.prototype.skip = function skip(v) { + this._validate('skip'); + + if (typeof v === 'string') { + try { + v = castNumber(v); + } catch { + throw new CastError('Number', v, 'skip'); + } + } + + this.options.skip = v; + return this; +}; + +/** + * Specifies the batchSize option. + * + * #### Example: + * + * query.batchSize(100) + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method batchSize + * @memberOf Query + * @instance + * @param {number} val + * @see batchSize https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/ + * @api public + */ + +/** + * Specifies the `comment` option. + * + * #### Example: + * + * query.comment('login query') + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method comment + * @memberOf Query + * @instance + * @param {string} val + * @see comment https://www.mongodb.com/docs/manual/reference/operator/comment/ + * @api public + */ + +/** + * Sets query hints. + * + * #### Example: + * + * query.hint({ indexA: 1, indexB: -1 }); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method hint + * @memberOf Query + * @instance + * @param {object} val a hint object + * @return {Query} this + * @see $hint https://www.mongodb.com/docs/manual/reference/operator/hint/ + * @api public + */ + +/** + * Get/set the current projection (AKA fields). Pass `null` to remove the + * current projection. + * + * Unlike `projection()`, the `select()` function modifies the current + * projection in place. This function overwrites the existing projection. + * + * #### Example: + * + * const q = Model.find(); + * q.projection(); // null + * + * q.select('a b'); + * q.projection(); // { a: 1, b: 1 } + * + * q.projection({ c: 1 }); + * q.projection(); // { c: 1 } + * + * q.projection(null); + * q.projection(); // null + * + * + * @method projection + * @memberOf Query + * @instance + * @param {object|null} arg + * @return {object} the current projection + * @api public + */ + +Query.prototype.projection = function(arg) { + if (arguments.length === 0) { + return this._fields; + } + + this._fields = {}; + this._userProvidedFields = {}; + this.select(arg); + return this._fields; +}; + +/** + * Specifies which document fields to include or exclude (also known as the query "projection") + * + * When using string syntax, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. Lastly, if a path is prefixed with `+`, it forces inclusion of the path, which is useful for paths excluded at the [schema level](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.select()). + * + * A projection _must_ be either inclusive or exclusive. In other words, you must + * either list the fields to include (which excludes all others), or list the fields + * to exclude (which implies all other fields are included). The [`_id` field is the only exception because MongoDB includes it by default](https://www.mongodb.com/docs/manual/tutorial/project-fields-from-query-results/#suppress-id-field). + * + * #### Example: + * + * // include a and b, exclude other fields + * query.select('a b'); + * // Equivalent syntaxes: + * query.select(['a', 'b']); + * query.select({ a: 1, b: 1 }); + * + * // exclude c and d, include other fields + * query.select('-c -d'); + * + * // Use `+` to override schema-level `select: false` without making the + * // projection inclusive. + * const schema = new Schema({ + * foo: { type: String, select: false }, + * bar: String + * }); + * // ... + * query.select('+foo'); // Override foo's `select: false` without excluding `bar` + * + * // or you may use object notation, useful when + * // you have keys already prefixed with a "-" + * query.select({ a: 1, b: 1 }); + * query.select({ c: 0, d: 0 }); + * + * Additional calls to select can override the previous selection: + * query.select({ a: 1, b: 1 }).select({ b: 0 }); // selection is now { a: 1 } + * query.select({ a: 0, b: 0 }).select({ b: 1 }); // selection is now { a: 0 } + * + * + * @method select + * @memberOf Query + * @instance + * @param {object|string|Array} arg + * @return {Query} this + * @see SchemaType https://mongoosejs.com/docs/api/schematype.html + * @api public + */ + +Query.prototype.select = function select() { + let arg = arguments[0]; + if (!arg) return this; + + if (arguments.length !== 1) { + throw new MongooseError('Invalid select: select only takes 1 argument'); + } + + this._validate('select'); + + const fields = this._fields || (this._fields = {}); + const userProvidedFields = this._userProvidedFields || (this._userProvidedFields = {}); + let sanitizeProjection = undefined; + if (this.model != null && utils.hasUserDefinedProperty(this.model.db.options, 'sanitizeProjection')) { + sanitizeProjection = this.model.db.options.sanitizeProjection; + } else if (this.model != null && utils.hasUserDefinedProperty(this.model.base.options, 'sanitizeProjection')) { + sanitizeProjection = this.model.base.options.sanitizeProjection; + } else { + sanitizeProjection = this._mongooseOptions.sanitizeProjection; + } + + function sanitizeValue(value) { + return typeof value === 'string' && sanitizeProjection ? value = 1 : value; + } + arg = parseProjection(arg, true); // we want to keep the minus and pluses, so add boolean arg. + if (utils.isObject(arg)) { + if (this.selectedInclusively()) { + Object.entries(arg).forEach(([key, value]) => { + if (value) { + // Add the field to the projection + if (fields['-' + key] != null) { + delete fields['-' + key]; + } + fields[key] = userProvidedFields[key] = sanitizeValue(value); + } else { + // Remove the field from the projection + Object.keys(userProvidedFields).forEach(field => { + if (isSubpath(key, field)) { + delete fields[field]; + delete userProvidedFields[field]; + } + }); + } + }); + } else if (this.selectedExclusively()) { + Object.entries(arg).forEach(([key, value]) => { + if (!value) { + // Add the field to the projection + if (fields['+' + key] != null) { + delete fields['+' + key]; + } + fields[key] = userProvidedFields[key] = sanitizeValue(value); + } else { + // Remove the field from the projection + Object.keys(userProvidedFields).forEach(field => { + if (isSubpath(key, field)) { + delete fields[field]; + delete userProvidedFields[field]; + } + }); + } + }); + } else { + const keys = Object.keys(arg); + for (let i = 0; i < keys.length; ++i) { + const value = arg[keys[i]]; + const key = keys[i]; + fields[key] = sanitizeValue(value); + userProvidedFields[key] = sanitizeValue(value); + } + } + + return this; + } + + throw new TypeError('Invalid select() argument. Must be string or object.'); +}; + +/** + * Enable or disable schema level projections for this query. Enabled by default. + * Set to `false` to include fields with `select: false` in the query result by default. + * + * #### Example: + * + * const userSchema = new Schema({ + * email: { type: String, required: true }, + * passwordHash: { type: String, select: false, required: true } + * }); + * const UserModel = mongoose.model('User', userSchema); + * + * const doc = await UserModel.findOne().orFail().schemaLevelProjections(false); + * + * // Contains password hash, because `schemaLevelProjections()` overrides `select: false` + * doc.passwordHash; + * + * @method schemaLevelProjections + * @memberOf Query + * @instance + * @param {boolean} value + * @return {Query} this + * @see SchemaTypeOptions https://mongoosejs.com/docs/schematypes.html#all-schema-types + * @api public + */ + +Query.prototype.schemaLevelProjections = function schemaLevelProjections(value) { + this._mongooseOptions.schemaLevelProjections = value; + + return this; +}; + +/** + * Sets this query's `sanitizeProjection` option. If set, `sanitizeProjection` does + * two things: + * + * 1. Enforces that projection values are numbers, not strings. + * 2. Prevents using `+` syntax to override properties that are deselected by default. + * + * With `sanitizeProjection()`, you can pass potentially untrusted user data to `.select()`. + * + * #### Example + * + * const userSchema = new Schema({ + * name: String, + * password: { type: String, select: false } + * }); + * const UserModel = mongoose.model('User', userSchema); + * const { _id } = await UserModel.create({ name: 'John', password: 'secret' }) + * + * // The MongoDB server has special handling for string values that start with '$' + * // in projections, which can lead to unexpected leaking of sensitive data. + * let doc = await UserModel.findOne().select({ name: '$password' }); + * doc.name; // 'secret' + * doc.password; // undefined + * + * // With `sanitizeProjection`, Mongoose forces all projection values to be numbers + * doc = await UserModel.findOne().sanitizeProjection(true).select({ name: '$password' }); + * doc.name; // 'John' + * doc.password; // undefined + * + * // By default, Mongoose supports projecting in `password` using `+password` + * doc = await UserModel.findOne().select('+password'); + * doc.password; // 'secret' + * + * // With `sanitizeProjection`, Mongoose prevents projecting in `password` and other + * // fields that have `select: false` in the schema. + * doc = await UserModel.findOne().sanitizeProjection(true).select('+password'); + * doc.password; // undefined + * + * @method sanitizeProjection + * @memberOf Query + * @instance + * @param {boolean} value + * @return {Query} this + * @see sanitizeProjection https://thecodebarbarian.com/whats-new-in-mongoose-5-13-sanitizeprojection.html + * @api public + */ + +Query.prototype.sanitizeProjection = function sanitizeProjection(value) { + this._mongooseOptions.sanitizeProjection = value; + + return this; +}; + +/** + * Determines the MongoDB nodes from which to read. + * + * #### Preferences: + * + * ``` + * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags. + * secondary Read from secondary if available, otherwise error. + * primaryPreferred Read from primary if available, otherwise a secondary. + * secondaryPreferred Read from a secondary if available, otherwise read from the primary. + * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection. + * ``` + * + * Aliases + * + * ``` + * p primary + * pp primaryPreferred + * s secondary + * sp secondaryPreferred + * n nearest + * ``` + * + * #### Example: + * + * new Query().read('primary') + * new Query().read('p') // same as primary + * + * new Query().read('primaryPreferred') + * new Query().read('pp') // same as primaryPreferred + * + * new Query().read('secondary') + * new Query().read('s') // same as secondary + * + * new Query().read('secondaryPreferred') + * new Query().read('sp') // same as secondaryPreferred + * + * new Query().read('nearest') + * new Query().read('n') // same as nearest + * + * // read from secondaries with matching tags + * new Query().read('s', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]) + * + * Read more about how to use read preferences [here](https://www.mongodb.com/docs/manual/applications/replication/#read-preference). + * + * @method read + * @memberOf Query + * @instance + * @param {string} mode one of the listed preference options or aliases + * @param {Array} [tags] optional tags for this query + * @see mongodb https://www.mongodb.com/docs/manual/applications/replication/#read-preference + * @return {Query} this + * @api public + */ + +Query.prototype.read = function read(mode, tags) { + if (typeof mode === 'string') { + mode = handleReadPreferenceAliases(mode); + this.options.readPreference = { mode, tags }; + } else { + this.options.readPreference = mode; + } + return this; +}; + +/** + * Overwrite default `.toString` to make logging more useful + * + * @memberOf Query + * @instance + * @method toString + * @api private + */ + +Query.prototype.toString = function toString() { + if (this.op === 'count' || + this.op === 'countDocuments' || + this.op === 'find' || + this.op === 'findOne' || + this.op === 'deleteMany' || + this.op === 'deleteOne' || + this.op === 'findOneAndDelete' || + this.op === 'remove') { + return `${this.model.modelName}.${this.op}(${util.inspect(this._conditions)})`; + } + if (this.op === 'distinct') { + return `${this.model.modelName}.distinct('${this._distinct}', ${util.inspect(this._conditions)})`; + } + if (this.op === 'findOneAndReplace' || + this.op === 'findOneAndUpdate' || + this.op === 'replaceOne' || + this.op === 'update' || + this.op === 'updateMany' || + this.op === 'updateOne') { + return `${this.model.modelName}.${this.op}(${util.inspect(this._conditions)}, ${util.inspect(this._update)})`; + } + + // 'estimatedDocumentCount' or any others + return `${this.model.modelName}.${this.op}()`; +}; + +/** + * Sets the [MongoDB session](https://www.mongodb.com/docs/manual/reference/server-sessions/) + * associated with this query. Sessions are how you mark a query as part of a + * [transaction](https://mongoosejs.com/docs/transactions.html). + * + * Calling `session(null)` removes the session from this query. + * + * #### Example: + * + * const s = await mongoose.startSession(); + * await mongoose.model('Person').findOne({ name: 'Axl Rose' }).session(s); + * + * @method session + * @memberOf Query + * @instance + * @param {ClientSession} [session] from `await conn.startSession()` + * @see Connection.prototype.startSession() https://mongoosejs.com/docs/api/connection.html#Connection.prototype.startSession() + * @see mongoose.startSession() https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.startSession() + * @return {Query} this + * @api public + */ + +Query.prototype.session = function session(v) { + if (v == null) { + delete this.options.session; + } + this.options.session = v; + return this; +}; + +/** + * Sets the 3 write concern parameters for this query: + * + * - `w`: Sets the specified number of `mongod` servers, or tag set of `mongod` servers, that must acknowledge this write before this write is considered successful. + * - `j`: Boolean, set to `true` to request acknowledgement that this operation has been persisted to MongoDB's on-disk journal. + * - `wtimeout`: If [`w > 1`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()), the maximum amount of time to wait for this write to propagate through the replica set before this operation fails. The default is `0`, which means no timeout. + * + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndReplace()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the schema's [`writeConcern` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * + * #### Example: + * + * // The 'majority' option means the `deleteOne()` promise won't resolve + * // until the `deleteOne()` has propagated to the majority of the replica set + * await mongoose.model('Person'). + * deleteOne({ name: 'Ned Stark' }). + * writeConcern({ w: 'majority' }); + * + * @method writeConcern + * @memberOf Query + * @instance + * @param {object} writeConcern the write concern value to set + * @see WriteConcernSettings https://mongodb.github.io/node-mongodb-native/7.0/interfaces/WriteConcernSettings.html + * @return {Query} this + * @api public + */ + +Query.prototype.writeConcern = function writeConcern(val) { + if (val == null) { + delete this.options.writeConcern; + return this; + } + this.options.writeConcern = val; + return this; +}; + +/** + * Sets the specified number of `mongod` servers, or tag set of `mongod` servers, + * that must acknowledge this write before this write is considered successful. + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndReplace()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the schema's [`writeConcern.w` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * + * #### Example: + * + * // The 'majority' option means the `deleteOne()` promise won't resolve + * // until the `deleteOne()` has propagated to the majority of the replica set + * await mongoose.model('Person'). + * deleteOne({ name: 'Ned Stark' }). + * w('majority'); + * + * @method w + * @memberOf Query + * @instance + * @param {string|number} val 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://www.mongodb.com/docs/manual/reference/write-concern/#w-option). + * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#w-option + * @return {Query} this + * @api public + */ + +Query.prototype.w = function w(val) { + if (val == null) { + delete this.options.w; + } + if (this.options.writeConcern != null) { + this.options.writeConcern.w = val; + } else { + this.options.w = val; + } + return this; +}; + +/** + * Requests acknowledgement that this operation has been persisted to MongoDB's + * on-disk journal. + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndReplace()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the schema's [`writeConcern.j` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * + * #### Example: + * + * await mongoose.model('Person').deleteOne({ name: 'Ned Stark' }).j(true); + * + * @method j + * @memberOf Query + * @instance + * @param {boolean} val + * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#j-option + * @return {Query} this + * @api public + */ + +Query.prototype.j = function j(val) { + if (val == null) { + delete this.options.j; + } + if (this.options.writeConcern != null) { + this.options.writeConcern.j = val; + } else { + this.options.j = val; + } + return this; +}; + +/** + * If [`w > 1`](https://mongoosejs.com/docs/api/query.html#Query.prototype.w()), the maximum amount of time to + * wait for this write to propagate through the replica set before this + * operation fails. The default is `0`, which means no timeout. + * + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndReplace()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the schema's [`writeConcern.wtimeout` option](https://mongoosejs.com/docs/guide.html#writeConcern) + * + * #### Example: + * + * // The `deleteOne()` promise won't resolve until this `deleteOne()` has + * // propagated to at least `w = 2` members of the replica set. If it takes + * // longer than 1 second, this `deleteOne()` will fail. + * await mongoose.model('Person'). + * deleteOne({ name: 'Ned Stark' }). + * w(2). + * wtimeout(1000); + * + * @method wtimeout + * @memberOf Query + * @instance + * @param {number} ms number of milliseconds to wait + * @see mongodb https://www.mongodb.com/docs/manual/reference/write-concern/#wtimeout + * @return {Query} this + * @api public + */ + +Query.prototype.wtimeout = function wtimeout(ms) { + if (ms == null) { + delete this.options.wtimeout; + } + if (this.options.writeConcern != null) { + this.options.writeConcern.wtimeout = ms; + } else { + this.options.wtimeout = ms; + } + return this; +}; + +/** + * Sets the readConcern option for the query. + * + * #### Example: + * + * new Query().readConcern('local') + * new Query().readConcern('l') // same as local + * + * new Query().readConcern('available') + * new Query().readConcern('a') // same as available + * + * new Query().readConcern('majority') + * new Query().readConcern('m') // same as majority + * + * new Query().readConcern('linearizable') + * new Query().readConcern('lz') // same as linearizable + * + * new Query().readConcern('snapshot') + * new Query().readConcern('s') // same as snapshot + * + * + * #### Read Concern Level: + * + * ``` + * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). + * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). + * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure. + * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results. + * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data. + * ``` + * + * Aliases + * + * ``` + * l local + * a available + * m majority + * lz linearizable + * s snapshot + * ``` + * + * Read more about how to use read concern [here](https://www.mongodb.com/docs/manual/reference/read-concern/). + * + * @memberOf Query + * @method readConcern + * @param {'local'|'available'|'majority'|'snapshot'|'linearizable'|'l'|'a'|'m'|'s'|'lz'} level one of the listed read concern level or their aliases + * @see mongodb https://www.mongodb.com/docs/manual/reference/read-concern/ + * @return {Query} this + * @api public + */ + +/** + * Gets query options. + * + * #### Example: + * + * const query = new Query(); + * query.limit(10); + * query.setOptions({ maxTimeMS: 1000 }); + * query.getOptions(); // { limit: 10, maxTimeMS: 1000 } + * + * @return {object} the options + * @api public + */ + +Query.prototype.getOptions = function() { + return this.options; +}; + +/** + * Sets query options. Some options only make sense for certain operations. + * + * #### Options: + * + * The following options are only for `find()`: + * + * - [tailable](https://www.mongodb.com/docs/manual/core/tailable-cursors/) + * - [limit](https://www.mongodb.com/docs/manual/reference/method/cursor.limit/) + * - [skip](https://www.mongodb.com/docs/manual/reference/method/cursor.skip/) + * - [allowDiskUse](https://www.mongodb.com/docs/manual/reference/method/cursor.allowDiskUse/) + * - [batchSize](https://www.mongodb.com/docs/manual/reference/method/cursor.batchSize/) + * - [readPreference](https://www.mongodb.com/docs/manual/applications/replication/#read-preference) + * - [hint](https://www.mongodb.com/docs/manual/reference/method/cursor.hint/) + * - [comment](https://www.mongodb.com/docs/manual/reference/method/cursor.comment/) + * + * The following options are only for write operations: `updateOne()`, `updateMany()`, `replaceOne()`, `findOneAndUpdate()`, and `findByIdAndUpdate()`: + * + * - [upsert](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/) + * - [writeConcern](https://www.mongodb.com/docs/manual/reference/method/db.collection.update/) + * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): If `timestamps` is set in the schema, set this option to `false` to skip timestamps for that particular update. Has no effect if `timestamps` is not enabled in the schema options. + * - overwriteDiscriminatorKey: allow setting the discriminator key in the update. Will use the correct discriminator schema if the update changes the discriminator key. + * - overwriteImmutable: allow overwriting properties that are set to `immutable` in the schema. Defaults to false. + * + * The following options are only for `find()`, `findOne()`, `findById()`, `findOneAndUpdate()`, `findOneAndReplace()`, `findOneAndDelete()`, and `findByIdAndUpdate()`: + * + * - [lean](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) + * - [populate](https://mongoosejs.com/docs/populate.html) + * - [projection](https://mongoosejs.com/docs/api/query.html#Query.prototype.projection()) + * - sanitizeProjection + * - useBigInt64 + * + * The following options are only for all operations **except** `updateOne()`, `updateMany()`, `deleteOne()`, and `deleteMany()`: + * + * - [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) + * + * The following options are for `find()`, `findOne()`, `findOneAndUpdate()`, `findOneAndDelete()`, `updateOne()`, and `deleteOne()`: + * + * - [sort](https://www.mongodb.com/docs/manual/reference/method/cursor.sort/) + * + * The following options are for `findOneAndUpdate()` and `findOneAndDelete()` + * + * - includeResultMetadata + * + * The following options are for all operations: + * + * - [strict](https://mongoosejs.com/docs/guide.html#strict) + * - [collation](https://www.mongodb.com/docs/manual/reference/collation/) + * - [session](https://www.mongodb.com/docs/manual/reference/server-sessions/) + * - [explain](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/) + * - [middleware](https://mongoosejs.com/docs/middleware.html#skipping): set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks + * + * @param {object} options + * @return {Query} this + * @api public + */ + +Query.prototype.setOptions = function(options, overwrite) { + // overwrite is only for internal use + if (overwrite) { + // ensure that _mongooseOptions & options are two different objects + this._mongooseOptions = (options && clone(options)) || {}; + this.options = options || {}; + + if ('populate' in options) { + this.populate(this._mongooseOptions); + } + return this; + } + if (options == null) { + return this; + } + if (typeof options !== 'object') { + throw new MongooseError('Options must be an object, got "' + options + '"'); + } + + options = Object.assign({}, options); + + if (Array.isArray(options.populate)) { + const populate = options.populate; + delete options.populate; + const _numPopulate = populate.length; + for (let i = 0; i < _numPopulate; ++i) { + this.populate(populate[i]); + } + } + + if ('setDefaultsOnInsert' in options) { + this._mongooseOptions.setDefaultsOnInsert = options.setDefaultsOnInsert; + delete options.setDefaultsOnInsert; + } + if ('overwriteDiscriminatorKey' in options) { + this._mongooseOptions.overwriteDiscriminatorKey = options.overwriteDiscriminatorKey; + delete options.overwriteDiscriminatorKey; + } + if ('overwriteImmutable' in options) { + this._mongooseOptions.overwriteImmutable = options.overwriteImmutable; + delete options.overwriteImmutable; + } + if ('updatePipeline' in options) { + this._mongooseOptions.updatePipeline = options.updatePipeline; + delete options.updatePipeline; + } + if ('sanitizeProjection' in options) { + if (options.sanitizeProjection && !this._mongooseOptions.sanitizeProjection) { + sanitizeProjection(this._fields); + } + + this._mongooseOptions.sanitizeProjection = options.sanitizeProjection; + delete options.sanitizeProjection; + } + if ('sanitizeFilter' in options) { + this._mongooseOptions.sanitizeFilter = options.sanitizeFilter; + delete options.sanitizeFilter; + } + if ('timestamps' in options) { + this._mongooseOptions.timestamps = options.timestamps; + delete options.timestamps; + } + if ('defaults' in options) { + this._mongooseOptions.defaults = options.defaults; + // deleting options.defaults will cause 7287 to fail + } + if ('translateAliases' in options) { + this._mongooseOptions.translateAliases = options.translateAliases; + delete options.translateAliases; + } + if ('schemaLevelProjections' in options) { + this._mongooseOptions.schemaLevelProjections = options.schemaLevelProjections; + delete options.schemaLevelProjections; + } + + if (options.lean == null && this.schema && 'lean' in this.schema.options) { + this._mongooseOptions.lean = this.schema.options.lean; + } + + if (typeof options.limit === 'string') { + try { + options.limit = castNumber(options.limit); + } catch { + throw new CastError('Number', options.limit, 'limit'); + } + } + if (typeof options.skip === 'string') { + try { + options.skip = castNumber(options.skip); + } catch { + throw new CastError('Number', options.skip, 'skip'); + } + } + + // set arbitrary options + for (const key of Object.keys(options)) { + if (queryOptionMethods.has(key)) { + const args = Array.isArray(options[key]) ? + options[key] : + [options[key]]; + this[key].apply(this, args); + } else { + this.options[key] = options[key]; + } + } + + return this; +}; + +/** + * Sets the [`explain` option](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/), + * which makes this query return detailed execution stats instead of the actual + * query result. This method is useful for determining what index your queries + * use. + * + * Calling `query.explain(v)` is equivalent to `query.setOptions({ explain: v })` + * + * #### Example: + * + * const query = new Query(); + * const res = await query.find({ a: 1 }).explain('queryPlanner'); + * console.log(res); + * + * @param {'queryPlanner'|'executionStats'|'allPlansExecution'} [verbose] The verbosity mode. The default is 'queryPlanner' + * @return {Query} this + * @api public + */ + +Query.prototype.explain = function explain(verbose) { + if (arguments.length === 0) { + this.options.explain = true; + } else if (verbose === false) { + delete this.options.explain; + } else { + this.options.explain = verbose; + } + return this; +}; + +/** + * Sets the [`allowDiskUse` option](https://www.mongodb.com/docs/manual/reference/method/cursor.allowDiskUse/), + * which allows the MongoDB server to use more than 100 MB for this query's `sort()`. This option can + * let you work around `QueryExceededMemoryLimitNoDiskUseAllowed` errors from the MongoDB server. + * + * Note that this option requires MongoDB server >= 4.4. Setting this option is a no-op for MongoDB 4.2 + * and earlier. + * + * Calling `query.allowDiskUse(v)` is equivalent to `query.setOptions({ allowDiskUse: v })` + * + * #### Example: + * + * await query.find().sort({ name: 1 }).allowDiskUse(true); + * // Equivalent: + * await query.find().sort({ name: 1 }).allowDiskUse(); + * + * @param {boolean} [v] Enable/disable `allowDiskUse`. If called with 0 arguments, sets `allowDiskUse: true` + * @return {Query} this + * @api public + */ + +Query.prototype.allowDiskUse = function(v) { + if (arguments.length === 0) { + this.options.allowDiskUse = true; + } else if (v === false) { + delete this.options.allowDiskUse; + } else { + this.options.allowDiskUse = v; + } + return this; +}; + +/** + * Sets the [maxTimeMS](https://www.mongodb.com/docs/manual/reference/method/cursor.maxTimeMS/) + * option. This will tell the MongoDB server to abort if the query or write op + * has been running for more than `ms` milliseconds. + * + * Calling `query.maxTimeMS(v)` is equivalent to `query.setOptions({ maxTimeMS: v })` + * + * #### Example: + * + * const query = new Query(); + * // Throws an error 'operation exceeded time limit' as long as there's + * // >= 1 doc in the queried collection + * const res = await query.find({ $where: 'sleep(1000) || true' }).maxTimeMS(100); + * + * @param {number} [ms] The number of milliseconds + * @return {Query} this + * @api public + */ + +Query.prototype.maxTimeMS = function(ms) { + this.options.maxTimeMS = ms; + return this; +}; + +/** + * Returns the current query filter (also known as conditions) as a [POJO](https://masteringjs.io/tutorials/fundamentals/pojo). + * + * #### Example: + * + * const query = new Query(); + * query.find({ a: 1 }).where('b').gt(2); + * query.getFilter(); // { a: 1, b: { $gt: 2 } } + * + * @return {object} current query filter + * @api public + */ + +Query.prototype.getFilter = function() { + return this._conditions; +}; + +/** + * Returns the current query filter. Equivalent to `getFilter()`. + * + * You should use `getFilter()` instead of `getQuery()` where possible. `getQuery()` + * will likely be deprecated in a future release. + * + * #### Example: + * + * const query = new Query(); + * query.find({ a: 1 }).where('b').gt(2); + * query.getQuery(); // { a: 1, b: { $gt: 2 } } + * + * @return {object} current query filter + * @api public + */ + +Query.prototype.getQuery = function() { + return this._conditions; +}; + +/** + * Sets the query conditions to the provided JSON object. + * + * #### Example: + * + * const query = new Query(); + * query.find({ a: 1 }) + * query.setQuery({ a: 2 }); + * query.getQuery(); // { a: 2 } + * + * @param {object} new query conditions + * @return {undefined} + * @api public + */ + +Query.prototype.setQuery = function(val) { + this._conditions = val; +}; + +/** + * Returns the current update operations as a JSON object. + * + * #### Example: + * + * const query = new Query(); + * query.updateOne({}, { $set: { a: 5 } }); + * query.getUpdate(); // { $set: { a: 5 } } + * + * @return {object} current update operations + * @api public + */ + +Query.prototype.getUpdate = function() { + return this._update; +}; + +/** + * Sets the current update operation to new value. + * + * #### Example: + * + * const query = new Query(); + * query.updateOne({}, { $set: { a: 5 } }); + * query.setUpdate({ $set: { b: 6 } }); + * query.getUpdate(); // { $set: { b: 6 } } + * + * @param {object} new update operation + * @return {undefined} + * @api public + */ + +Query.prototype.setUpdate = function(val) { + this._update = clone(val); +}; + +/** + * Returns fields selection for this query. + * + * @method _fieldsForExec + * @return {object} + * @api private + * @memberOf Query + */ + +Query.prototype._fieldsForExec = function() { + if (this._fields == null) { + return null; + } + if (utils.hasOwnKeys(this._fields) === false) { + return null; + } + return clone(this._fields); +}; + + +/** + * Return an update document with corrected `$set` operations. + * + * @method _updateForExec + * @return {object} + * @api private + * @memberOf Query + */ + +Query.prototype._updateForExec = function() { + const update = clone(this._update, { + transform: false, + depopulate: true + }); + const ops = Object.keys(update); + let i = ops.length; + const ret = {}; + + while (i--) { + const op = ops[i]; + + if ('$' !== op[0]) { + // fix up $set sugar + if (!ret.$set) { + if (update.$set) { + ret.$set = update.$set; + } else { + ret.$set = {}; + } + } + ret.$set[op] = update[op]; + ops.splice(i, 1); + if (!~ops.indexOf('$set')) ops.push('$set'); + } else if ('$set' === op) { + if (!ret.$set) { + ret[op] = update[op]; + } + } else { + ret[op] = update[op]; + } + } + + return ret; +}; + +/** + * Makes sure _path is set. + * + * This method is inherited by `mquery` + * + * @method _ensurePath + * @param {string} method + * @api private + * @memberOf Query + */ + +/** + * Determines if `conds` can be merged using `mquery().merge()` + * + * @method canMerge + * @memberOf Query + * @instance + * @param {object} conds + * @return {boolean} + * @api private + */ + +/** + * Returns default options for this query. + * + * @param {Model} model + * @api private + */ + +Query.prototype._optionsForExec = function(model) { + const options = clone(this.options); + delete options.populate; + model = model || this.model; + + if (!model) { + return options; + } + applyReadConcern(model.schema, options); + // Apply schema-level `writeConcern` option + applyWriteConcern(model.schema, options); + + const asyncLocalStorage = this.model?.db?.base.transactionAsyncLocalStorage?.getStore(); + if (!Object.hasOwn(this.options, 'session') && asyncLocalStorage?.session != null) { + options.session = asyncLocalStorage.session; + } + + const readPreference = model?.schema?.options?.read; + if (!('readPreference' in options) && readPreference) { + options.readPreference = readPreference; + } + + if (options.upsert !== void 0) { + options.upsert = !!options.upsert; + } + if (options.writeConcern) { + if (options.j) { + options.writeConcern.j = options.j; + delete options.j; + } + if (options.w) { + options.writeConcern.w = options.w; + delete options.w; + } + if (options.wtimeout) { + options.writeConcern.wtimeout = options.wtimeout; + delete options.wtimeout; + } + } + + this._applyPaths(); + if (this._fields != null) { + this._fields = this._castFields(this._fields); + const projection = this._fieldsForExec(); + if (projection != null) { + options.projection = projection; + } + } + + if (this._mongooseOptions.populate) { + if (options.readPreference) { + for (const pop of Object.values(this._mongooseOptions.populate)) { + if (pop.options?.readPreference === undefined) { + if (!pop.options) { + pop.options = {}; + } + pop.options.readPreference = options.readPreference; + } + } + } + if (options.readConcern) { + for (const pop of Object.values(this._mongooseOptions.populate)) { + if (pop.options?.readConcern === undefined) { + if (!pop.options) { + pop.options = {}; + } + pop.options.readConcern = options.readConcern; + } + } + } + } + + return options; +}; + +/** + * Sets the lean option. + * + * Documents returned from queries with the `lean` option enabled are plain + * javascript objects, not [Mongoose Documents](https://mongoosejs.com/docs/api/document.html). They have no + * `save` method, getters/setters, virtuals, or other Mongoose features. + * + * #### Example: + * + * new Query().lean() // true + * new Query().lean(true) + * new Query().lean(false) + * + * const docs = await Model.find().lean(); + * docs[0] instanceof mongoose.Document; // false + * + * [Lean is great for high-performance, read-only cases](https://mongoosejs.com/docs/tutorials/lean.html), + * especially when combined + * with [cursors](https://mongoosejs.com/docs/queries.html#streaming). + * + * If you need virtuals, getters/setters, or defaults with `lean()`, you need + * to use a plugin. See: + * + * - [mongoose-lean-virtuals](https://plugins.mongoosejs.io/plugins/lean-virtuals) + * - [mongoose-lean-getters](https://plugins.mongoosejs.io/plugins/lean-getters) + * - [mongoose-lean-defaults](https://www.npmjs.com/package/mongoose-lean-defaults) + * + * @param {boolean|object} bool defaults to true + * @return {Query} this + * @api public + */ + +Query.prototype.lean = function(v) { + this._mongooseOptions.lean = arguments.length ? v : true; + return this; +}; + +/** + * Adds a `$set` to this query's update without changing the operation. + * This is useful for query middleware so you can add an update regardless + * of whether you use `updateOne()`, `updateMany()`, `findOneAndUpdate()`, etc. + * + * #### Example: + * + * // Updates `{ $set: { updatedAt: new Date() } }` + * new Query().updateOne({}, {}).set('updatedAt', new Date()); + * new Query().updateMany({}, {}).set({ updatedAt: new Date() }); + * + * @param {string|object} path path or object of key/value pairs to set + * @param {any} [val] the value to set + * @return {Query} this + * @api public + */ + +Query.prototype.set = function(path, val) { + if (typeof path === 'object') { + const keys = Object.keys(path); + for (const key of keys) { + this.set(key, path[key]); + } + return this; + } + + this._update = this._update || {}; + if (path in this._update) { + delete this._update[path]; + } + this._update.$set = this._update.$set || {}; + this._update.$set[path] = val; + return this; +}; + +/** + * For update operations, returns the value of a path in the update's `$set`. + * Useful for writing getters/setters that can work with both update operations + * and `save()`. + * + * #### Example: + * + * const query = Model.updateOne({}, { $set: { name: 'Jean-Luc Picard' } }); + * query.get('name'); // 'Jean-Luc Picard' + * + * @param {string|object} path path or object of key/value pairs to get + * @return {Query} this + * @api public + */ + +Query.prototype.get = function get(path) { + const update = this._update; + if (update == null) { + return void 0; + } + const $set = update.$set; + if ($set == null) { + return update[path]; + } + + if (utils.hasUserDefinedProperty(update, path)) { + return update[path]; + } + if (utils.hasUserDefinedProperty($set, path)) { + return $set[path]; + } + + return void 0; +}; + +/** + * Gets/sets the error flag on this query. If this flag is not null or + * undefined, the `exec()` promise will reject without executing. + * + * #### Example: + * + * Query().error(); // Get current error value + * Query().error(null); // Unset the current error + * Query().error(new Error('test')); // `exec()` will resolve with test + * Schema.pre('find', function() { + * if (!this.getQuery().userId) { + * this.error(new Error('Not allowed to query without setting userId')); + * } + * }); + * + * Note that query casting runs **after** hooks, so cast errors will override + * custom errors. + * + * #### Example: + * + * const TestSchema = new Schema({ num: Number }); + * const TestModel = db.model('Test', TestSchema); + * TestModel.find({ num: 'not a number' }).error(new Error('woops')).exec(function(error) { + * // `error` will be a cast error because `num` failed to cast + * }); + * + * @param {Error|null} err if set, `exec()` will fail fast before sending the query to MongoDB + * @return {Query} this + * @api public + */ + +Query.prototype.error = function error(err) { + if (arguments.length === 0) { + return this._error; + } + + this._error = err; + return this; +}; + +/** + * ignore + * @method _unsetCastError + * @instance + * @memberOf Query + * @api private + */ + +Query.prototype._unsetCastError = function _unsetCastError() { + if (this._error == null || !(this._error instanceof CastError)) { + return; + } + return this.error(null); +}; + +/** + * Getter/setter around the current mongoose-specific options for this query + * Below are the current Mongoose-specific options. + * + * - `populate`: an array representing what paths will be populated. Should have one entry for each call to [`Query.prototype.populate()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.populate()) + * - `lean`: if truthy, Mongoose will not [hydrate](https://mongoosejs.com/docs/api/model.html#Model.hydrate()) any documents that are returned from this query. See [`Query.prototype.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) for more information. + * - `strict`: controls how Mongoose handles keys that aren't in the schema for updates. This option is `true` by default, which means Mongoose will silently strip any paths in the update that aren't in the schema. See the [`strict` mode docs](https://mongoosejs.com/docs/guide.html#strict) for more information. + * - `strictQuery`: controls how Mongoose handles keys that aren't in the schema for the query `filter`. This option is `false` by default, which means Mongoose will allow `Model.find({ foo: 'bar' })` even if `foo` is not in the schema. See the [`strictQuery` docs](https://mongoosejs.com/docs/guide.html#strictQuery) for more information. + * - `nearSphere`: use `$nearSphere` instead of `near()`. See the [`Query.prototype.nearSphere()` docs](https://mongoosejs.com/docs/api/query.html#Query.prototype.nearSphere()) + * - `schemaLevelProjections`: if `false`, Mongoose will not apply schema-level `select: false` or `select: true` for this query + * + * Mongoose maintains a separate object for internal options because + * Mongoose sends `Query.prototype.options` to the MongoDB server, and the + * above options are not relevant for the MongoDB server. + * + * @param {object} options if specified, overwrites the current options + * @return {object} the options + * @api public + */ + +Query.prototype.mongooseOptions = function(v) { + if (arguments.length > 0) { + this._mongooseOptions = v; + } + return this._mongooseOptions; +}; + +/** + * ignore + * @method _castConditions + * @memberOf Query + * @api private + * @instance + */ + +Query.prototype._castConditions = function() { + let sanitizeFilterOpt = undefined; + if (this.model?.db.options?.sanitizeFilter != null) { + sanitizeFilterOpt = this.model.db.options.sanitizeFilter; + } else if (this.model?.base.options?.sanitizeFilter != null) { + sanitizeFilterOpt = this.model.base.options.sanitizeFilter; + } else { + sanitizeFilterOpt = this._mongooseOptions.sanitizeFilter; + } + + if (sanitizeFilterOpt) { + sanitizeFilter(this._conditions); + } + + try { + this.cast(this.model); + this._unsetCastError(); + } catch (err) { + this.error(err); + } +}; + +/*! + * ignore + */ + +function _castArrayFilters(query) { + try { + castArrayFilters(query); + } catch (err) { + query.error(err); + } +} + +/** + * Execute a `find()` + * + * @return {Query} this + * @api private + */ +Query.prototype._find = async function _find() { + this._applyTranslateAliases(); + this._castConditions(); + + if (this.error() != null) { + throw this.error(); + } + + const mongooseOptions = this._mongooseOptions; + const userProvidedFields = this._userProvidedFields || {}; + + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); + + // Separate options to pass down to `completeMany()` in case we need to + // set a session on the document + const completeManyOptions = { + session: this?.options?.session || null, + lean: mongooseOptions.lean || null + }; + + const options = this._optionsForExec(); + + const filter = this._conditions; + const fields = options.projection; + + const cursor = await this.mongooseCollection.find(filter, options); + if (options.explain) { + return cursor.explain(); + } + + let docs = await cursor.toArray(); + if (docs.length === 0) { + return docs; + } + + if (!mongooseOptions.populate) { + const versionKey = this.schema.options.versionKey; + if (mongooseOptions.lean?.versionKey === false && versionKey) { + docs.forEach((doc) => { + if (versionKey in doc) { + delete doc[versionKey]; + } + }); + } + return mongooseOptions.lean ? + _completeManyLean(this.model.schema, docs, null, completeManyOptions) : + this._completeMany(docs, fields, userProvidedFields, completeManyOptions); + } + + const pop = helpers.preparePopulationOptionsMQ(this, mongooseOptions); + + // _deferPopulate is used internally when we need to defer populate until after + // filtering (when using match functions with nested populate via hooks) + if (this.options?._deferPopulate) { + docs = mongooseOptions.lean ? + docs : + await this._completeMany(docs, fields, userProvidedFields, completeManyOptions); + this._deferredPopulate = pop; + return docs; + } + + if (mongooseOptions.lean) { + return this.model.populate(docs, pop); + } + + docs = await this._completeMany(docs, fields, userProvidedFields, completeManyOptions); + await this.model.populate(docs, pop); + + return docs; +}; + +/** + * Find all documents that match `selector`. The result will be an array of documents. + * + * If there are too many documents in the result to fit in memory, use + * [`Query.prototype.cursor()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.cursor()) + * + * #### Example: + * + * const arr = await Movie.find({ year: { $gte: 1980, $lte: 1989 } }); + * + * @param {object|ObjectId} [filter] mongodb filter. If not specified, returns all documents. + * @return {Query} this + * @api public + */ + +Query.prototype.find = function(conditions) { + if (typeof conditions === 'function' || + typeof arguments[1] === 'function') { + throw new MongooseError('Query.prototype.find() no longer accepts a callback'); + } + + this.op = 'find'; + + if (canMerge(conditions)) { + this.merge(conditions); + + prepareDiscriminatorCriteria(this); + } else if (conditions != null) { + this.error(new ObjectParameterError(conditions, 'filter', 'find')); + } + + return this; +}; + +/** + * Merges another Query or conditions object into this one. + * + * When a Query is passed, conditions, field selection and options are merged. + * + * @param {Query|object} source + * @return {Query} this + */ + +Query.prototype.merge = function(source) { + if (!source) { + if (source === null) { + this._conditions = null; + } + return this; + } + + this._conditions = this._conditions ?? {}; + + const opts = { overwrite: true }; + + if (source instanceof Query) { + // if source has a feature, apply it to ourselves + + if (source._conditions) { + opts.omit = {}; + if (this._conditions && this._conditions.$and && source._conditions.$and) { + opts.omit['$and'] = true; + this._conditions.$and = this._conditions.$and.concat(source._conditions.$and); + } + if (this._conditions && this._conditions.$or && source._conditions.$or) { + opts.omit['$or'] = true; + this._conditions.$or = this._conditions.$or.concat(source._conditions.$or); + } + utils.merge(this._conditions, source._conditions, opts); + } + + if (source._fields) { + this._fields || (this._fields = {}); + utils.merge(this._fields, source._fields, opts); + } + + if (source.options) { + this.options || (this.options = {}); + utils.merge(this.options, source.options, opts); + } + + if (source._update) { + this._update || (this._update = {}); + utils.mergeClone(this._update, source._update); + } + + if (source._distinct) { + this._distinct = source._distinct; + } + + utils.merge(this._mongooseOptions, source._mongooseOptions); + + return this; + } else if (this.model != null && source instanceof this.model.base.Types.ObjectId) { + utils.merge(this._conditions, { _id: source }, opts); + + return this; + } else if (source?.$__) { + source = source.toObject(internalToObjectOptions); + } + + opts.omit = {}; + if (Array.isArray(source.$and)) { + opts.omit['$and'] = true; + if (!this._conditions) { + this._conditions = {}; + } + this._conditions.$and = (this._conditions.$and || []).concat( + source.$and.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el) + ); + } + if (Array.isArray(source.$or)) { + opts.omit['$or'] = true; + if (!this._conditions) { + this._conditions = {}; + } + this._conditions.$or = (this._conditions.$or || []).concat( + source.$or.map(el => utils.isPOJO(el) ? utils.merge({}, el) : el) + ); + } + + // plain object + utils.merge(this._conditions, source, opts); + + return this; +}; + +/** + * Adds a collation to this op (MongoDB 3.4 and up) + * + * @param {object} value + * @return {Query} this + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/method/cursor.collation/#cursor.collation + * @api public + */ + +Query.prototype.collation = function(value) { + if (this.options == null) { + this.options = {}; + } + this.options.collation = value; + return this; +}; + +/** + * Hydrate a single doc from `findOne()`, `findOneAndUpdate()`, etc. + * + * @api private + */ + +Query.prototype._completeOne = function(doc, res, projection, callback) { + if (!doc && !this.options.includeResultMetadata) { + return callback(null, null); + } + + const model = this.model; + const userProvidedFields = this._userProvidedFields || {}; + // `populate`, `lean` + const mongooseOptions = this._mongooseOptions; + + const options = this.options; + if (!options.lean && mongooseOptions.lean) { + options.lean = mongooseOptions.lean; + } + + if (options.explain) { + return callback(null, doc); + } + + if (!mongooseOptions.populate) { + const versionKey = this.schema.options.versionKey; + if (mongooseOptions.lean?.versionKey === false && versionKey) { + if (versionKey in doc) { + delete doc[versionKey]; + } + } + return mongooseOptions.lean ? + _completeOneLean(model.schema, doc, null, res, options, callback) : + completeOne(model, doc, res, options, projection, userProvidedFields, + null, callback); + } + + const pop = helpers.preparePopulationOptionsMQ(this, this._mongooseOptions); + if (mongooseOptions.lean) { + return model.populate(doc, pop).then( + doc => { + _completeOneLean(model.schema, doc, null, res, options, callback); + }, + error => { + callback(error); + } + ); + } + + completeOne(model, doc, res, options, projection, userProvidedFields, [], (err, doc) => { + if (err != null) { + return callback(err); + } + model.populate(doc, pop).then(res => { callback(null, res); }, err => { callback(err); }); + }); +}; + +/** + * Given a model and an array of docs, hydrates all the docs to be instances + * of the model. Used to initialize docs returned from the db from `find()` + * + * @param {Array} docs + * @param {object} fields the projection used, including `select` from schemas + * @param {object} userProvidedFields the user-specified projection + * @param {object} [opts] + * @param {Array} [opts.populated] + * @param {ClientSession} [opts.session] + * @api private + */ + +Query.prototype._completeMany = async function _completeMany(docs, fields, userProvidedFields, opts) { + const model = this.model; + return Promise.all(docs.map(doc => new Promise((resolve, reject) => { + const rawDoc = doc; + doc = helpers.createModel(model, doc, fields, userProvidedFields); + if (opts.session != null) { + doc.$session(opts.session); + } + doc.$init(rawDoc, opts, (err) => { + if (err != null) { + return reject(err); + } + resolve(doc); + }); + }))); +}; + +/** + * Internal helper to execute a findOne() operation + * + * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/ + * @api private + */ + +Query.prototype._findOne = async function _findOne() { + this._applyTranslateAliases(); + this._castConditions(); + + if (this.error()) { + const err = this.error(); + throw err; + } + + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); + + const options = this._optionsForExec(); + + // don't pass in the conditions because we already merged them in + const doc = await this.mongooseCollection.findOne(this._conditions, options); + return new Promise((resolve, reject) => { + this._completeOne(doc, null, options.projection, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }); + }); +}; + +/** + * Declares the query a findOne operation. When executed, the first found document is passed to the callback. + * + * The result of the query is a single document, or `null` if no document was found. + * + * * *Note:* `conditions` is optional, and if `conditions` is null or undefined, + * mongoose will send an empty `findOne` command to MongoDB, which will return + * an arbitrary document. If you're querying by `_id`, use `Model.findById()` + * instead. + * + * This function triggers the following middleware. + * + * - `findOne()` + * + * #### Example: + * + * const query = Kitten.where({ color: 'white' }); + * const kitten = await query.findOne(); + * + * @param {object} [filter] mongodb selector + * @param {object} [projection] optional fields to return + * @param {object} [options] see [`setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} this + * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/ + * @see Query.select https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @api public + */ + +Query.prototype.findOne = function(conditions, projection, options) { + if (typeof conditions === 'function' || + typeof projection === 'function' || + typeof options === 'function' || + typeof arguments[3] === 'function') { + throw new MongooseError('Query.prototype.findOne() no longer accepts a callback'); + } + + this.op = 'findOne'; + + if (options) { + this.setOptions(options); + } + + if (projection) { + this.select(projection); + } + + if (canMerge(conditions)) { + this.merge(conditions); + + prepareDiscriminatorCriteria(this); + } else if (conditions != null) { + this.error(new ObjectParameterError(conditions, 'filter', 'findOne')); + } + + return this; +}; + + +/** + * Execute a countDocuments query + * + * @see countDocuments https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments + * @api private + */ + +Query.prototype._countDocuments = async function _countDocuments() { + this._applyTranslateAliases(); + + try { + this.cast(this.model); + } catch (err) { + this.error(err); + } + + if (this.error()) { + throw this.error(); + } + + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); + + const options = this._optionsForExec(); + + const conds = this._conditions; + + return this.mongooseCollection.countDocuments(conds, options); +}; + +/*! + * If `translateAliases` option is set, call `Model.translateAliases()` + * on the following query properties: filter, projection, update, distinct. + */ + +Query.prototype._applyTranslateAliases = function _applyTranslateAliases() { + let applyTranslateAliases = false; + if ('translateAliases' in this._mongooseOptions) { + applyTranslateAliases = this._mongooseOptions.translateAliases; + } else if (this.model?.schema?._userProvidedOptions?.translateAliases != null) { + applyTranslateAliases = this.model.schema._userProvidedOptions.translateAliases; + } else if (this.model?.base?.options?.translateAliases != null) { + applyTranslateAliases = this.model.base.options.translateAliases; + } + if (!applyTranslateAliases) { + return; + } + + if (this.model?.schema?.aliases && utils.hasOwnKeys(this.model.schema.aliases)) { + this.model.translateAliases(this._conditions, true); + this.model.translateAliases(this._fields, true); + this.model.translateAliases(this._update, true); + if (this._distinct != null && this.model.schema.aliases[this._distinct] != null) { + this._distinct = this.model.schema.aliases[this._distinct]; + } + } +}; + +/** + * Execute a estimatedDocumentCount() query + * + * @see estimatedDocumentCount https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#estimatedDocumentCount + * @api private + */ + +Query.prototype._estimatedDocumentCount = async function _estimatedDocumentCount() { + if (this.error()) { + throw this.error(); + } + + const options = this._optionsForExec(); + + return this.mongooseCollection.estimatedDocumentCount(options); +}; + +/** + * Specifies this query as a `estimatedDocumentCount()` query. Faster than + * using `countDocuments()` for large collections because + * `estimatedDocumentCount()` uses collection metadata rather than scanning + * the entire collection. + * + * `estimatedDocumentCount()` does **not** accept a filter. `Model.find({ foo: bar }).estimatedDocumentCount()` + * is equivalent to `Model.find().estimatedDocumentCount()` + * + * This function triggers the following middleware. + * + * - `estimatedDocumentCount()` + * + * #### Example: + * + * await Model.find().estimatedDocumentCount(); + * + * @param {object} [options] passed transparently to the [MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/EstimatedDocumentCountOptions.html) + * @return {Query} this + * @see estimatedDocumentCount https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#estimatedDocumentCount + * @api public + */ + +Query.prototype.estimatedDocumentCount = function(options) { + if (typeof options === 'function' || + typeof arguments[1] === 'function') { + throw new MongooseError('Query.prototype.estimatedDocumentCount() no longer accepts a callback'); + } + + this.op = 'estimatedDocumentCount'; + + if (options != null) { + this.setOptions(options); + } + + return this; +}; + +/** + * Specifies this query as a `countDocuments()` query. Behaves like `count()`, + * except it always does a full collection scan when passed an empty filter `{}`. + * + * There are also minor differences in how `countDocuments()` handles + * [`$where` and a couple geospatial operators](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments). + * versus `count()`. + * + * This function triggers the following middleware. + * + * - `countDocuments()` + * + * #### Example: + * + * const countQuery = model.where({ 'color': 'black' }).countDocuments(); + * + * query.countDocuments({ color: 'black' }).count().exec(); + * + * await query.countDocuments({ color: 'black' }); + * + * query.where('color', 'black').countDocuments().exec(); + * + * The `countDocuments()` function is similar to `count()`, but there are a + * [few operators that `countDocuments()` does not support](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments). + * Below are the operators that `count()` supports but `countDocuments()` does not, + * and the suggested replacement: + * + * - `$where`: [`$expr`](https://www.mongodb.com/docs/manual/reference/operator/query/expr/) + * - `$near`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$center`](https://www.mongodb.com/docs/manual/reference/operator/query/center/#op._S_center) + * - `$nearSphere`: [`$geoWithin`](https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/) with [`$centerSphere`](https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/#op._S_centerSphere) + * + * @param {object} [filter] mongodb selector + * @param {object} [options] + * @return {Query} this + * @see countDocuments https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#countDocuments + * @api public + */ + +Query.prototype.countDocuments = function(conditions, options) { + if (typeof conditions === 'function' || + typeof options === 'function' || + typeof arguments[2] === 'function') { + throw new MongooseError('Query.prototype.countDocuments() no longer accepts a callback'); + } + + this.op = 'countDocuments'; + + if (canMerge(conditions)) { + this.merge(conditions); + } + + if (options != null) { + this.setOptions(options); + } + + return this; +}; + +/** + * Execute a `distinct()` query + * + * @see distinct https://www.mongodb.com/docs/manual/reference/method/db.collection.distinct/ + * @api private + */ + +Query.prototype.__distinct = async function __distinct() { + this._applyTranslateAliases(); + this._castConditions(); + + if (this.error()) { + throw this.error(); + } + + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); + + const options = this._optionsForExec(); + + return this.mongooseCollection. + distinct(this._distinct, this._conditions, options); +}; + +/** + * Declares or executes a distinct() operation. + * + * This function does not trigger any middleware. + * + * #### Example: + * + * distinct(field, conditions, options) + * distinct(field, conditions) + * distinct(field) + * distinct() + * + * @param {string} [field] + * @param {object|Query} [filter] + * @param {object} [options] + * @return {Query} this + * @see distinct https://www.mongodb.com/docs/manual/reference/method/db.collection.distinct/ + * @api public + */ + +Query.prototype.distinct = function(field, conditions, options) { + if (typeof field === 'function' || + typeof conditions === 'function' || + typeof options === 'function' || + typeof arguments[3] === 'function') { + throw new MongooseError('Query.prototype.distinct() no longer accepts a callback'); + } + + this.op = 'distinct'; + + if (canMerge(conditions)) { + this.merge(conditions); + + prepareDiscriminatorCriteria(this); + } else if (conditions != null) { + this.error(new ObjectParameterError(conditions, 'filter', 'distinct')); + } + + if (field != null) { + this._distinct = field; + } + + if (options != null) { + this.setOptions(options); + } + + return this; +}; + +/** + * Sets the sort order + * + * If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`. + * + * If a string is passed, it must be a space delimited list of path names. The + * sort order of each path is ascending unless the path name is prefixed with `-` + * which will be treated as descending. + * + * #### Example: + * + * // sort by "field" ascending and "test" descending + * query.sort({ field: 'asc', test: -1 }); + * + * // equivalent + * query.sort('field -test'); + * + * // also possible is to use a array with array key-value pairs + * query.sort([['field', 'asc']]); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @param {object|string|Array>} arg + * @param {object} [options] + * @param {boolean} [options.override=false] If true, replace existing sort options with `arg` + * @return {Query} this + * @see cursor.sort https://www.mongodb.com/docs/manual/reference/method/cursor.sort/ + * @api public + */ + +Query.prototype.sort = function(arg, options) { + if (arguments.length > 2) { + throw new MongooseError('sort() takes at most 2 arguments'); + } + if (options != null && typeof options !== 'object') { + throw new MongooseError('sort() options argument must be an object or nullish'); + } + + if (this.options.sort == null) { + this.options.sort = {}; + } + if (options?.override) { + this.options.sort = {}; + } + const sort = this.options.sort; + if (typeof arg === 'string') { + const properties = arg.indexOf(' ') === -1 ? [arg] : arg.split(' '); + for (let property of properties) { + const ascend = '-' == property[0] ? -1 : 1; + if (ascend === -1) { + property = property.slice(1); + } + if (specialProperties.has(property)) { + continue; + } + sort[property] = ascend; + } + } else if (Array.isArray(arg)) { + for (const pair of arg) { + if (!Array.isArray(pair)) { + throw new TypeError('Invalid sort() argument, must be array of arrays'); + } + const key = '' + pair[0]; + if (specialProperties.has(key)) { + continue; + } + sort[key] = _handleSortValue(pair[1], key); + } + } else if (typeof arg === 'object' && arg != null && !(arg instanceof Map)) { + for (const key of Object.keys(arg)) { + if (specialProperties.has(key)) { + continue; + } + sort[key] = _handleSortValue(arg[key], key); + } + } else if (arg instanceof Map) { + for (let key of arg.keys()) { + key = '' + key; + if (specialProperties.has(key)) { + continue; + } + sort[key] = _handleSortValue(arg.get(key), key); + } + } else if (arg != null) { + throw new TypeError('Invalid sort() argument. Must be a string, object, array, or map.'); + } + + return this; +}; + +/*! + * Convert sort values + */ + +function _handleSortValue(val, key) { + if (val === 1 || val === 'asc' || val === 'ascending') { + return 1; + } + if (val === -1 || val === 'desc' || val === 'descending') { + return -1; + } + if (val?.$meta != null) { + return { $meta: val.$meta }; + } + throw new TypeError('Invalid sort value: { ' + key + ': ' + val + ' }'); +} + +/** + * Declare and/or execute this query as a `deleteOne()` operation. Works like + * remove, except it deletes at most one document regardless of the `single` + * option. + * + * This function triggers `deleteOne` middleware. + * + * #### Example: + * + * await Character.deleteOne({ name: 'Eddard Stark' }); + * + * This function calls the MongoDB driver's [`Collection#deleteOne()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteOne). + * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an + * object that contains 2 properties: + * + * - `acknowledged`: boolean + * - `deletedCount`: the number of documents deleted + * + * #### Example: + * + * const res = await Character.deleteOne({ name: 'Eddard Stark' }); + * // `1` if MongoDB deleted a doc, `0` if no docs matched the filter `{ name: ... }` + * res.deletedCount; + * + * @param {object|Query} [filter] mongodb selector + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @see DeleteResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/DeleteResult.html + * @see deleteOne https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteOne + * @api public + */ + +Query.prototype.deleteOne = function deleteOne(filter, options) { + if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Query.prototype.deleteOne() no longer accepts a callback'); + } + this.op = 'deleteOne'; + this.setOptions(options); + + if (canMerge(filter)) { + this.merge(filter); + + prepareDiscriminatorCriteria(this); + } else if (filter != null) { + this.error(new ObjectParameterError(filter, 'filter', 'deleteOne')); + } + + return this; +}; + +/** + * Internal thunk for `deleteOne()` + * + * @method _deleteOne + * @instance + * @memberOf Query + * @api private + */ + +Query.prototype._deleteOne = async function _deleteOne() { + this._applyTranslateAliases(); + this._castConditions(); + + // Check for empty/invalid filter with requireFilter option + checkRequireFilter(this._conditions, this.options); + + if (this.error() != null) { + throw this.error(); + } + + const options = this._optionsForExec(); + + return this.mongooseCollection.deleteOne(this._conditions, options); +}; + +/** + * Declare and/or execute this query as a `deleteMany()` operation. Works like + * remove, except it deletes _every_ document that matches `filter` in the + * collection, regardless of the value of `single`. + * + * This function triggers `deleteMany` middleware. + * + * #### Example: + * + * await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }); + * + * This function calls the MongoDB driver's [`Collection#deleteMany()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteMany). + * The returned [promise](https://mongoosejs.com/docs/queries.html) resolves to an + * object that contains 2 properties: + * + * - `acknowledged`: boolean + * - `deletedCount`: the number of documents deleted + * + * #### Example: + * + * const res = await Character.deleteMany({ name: /Stark/, age: { $gte: 18 } }); + * // `0` if no docs matched the filter, number of docs deleted otherwise + * res.deletedCount; + * + * @param {object|Query} [filter] mongodb selector + * @param {object} [options] optional see [`Query.prototype.setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @see DeleteResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/DeleteResult.html + * @see deleteMany https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#deleteMany + * @api public + */ + +Query.prototype.deleteMany = function(filter, options) { + if (typeof filter === 'function' || typeof options === 'function' || typeof arguments[2] === 'function') { + throw new MongooseError('Query.prototype.deleteMany() no longer accepts a callback'); + } + this.setOptions(options); + this.op = 'deleteMany'; + + if (canMerge(filter)) { + this.merge(filter); + + prepareDiscriminatorCriteria(this); + } else if (filter != null) { + this.error(new ObjectParameterError(filter, 'filter', 'deleteMany')); + } + + return this; +}; + +/** + * Execute a `deleteMany()` query + * + * @method _deleteMany + * @instance + * @memberOf Query + * @api private + */ + +Query.prototype._deleteMany = async function _deleteMany() { + this._applyTranslateAliases(); + this._castConditions(); + + // Check for empty/invalid filter with requireFilter option + checkRequireFilter(this._conditions, this.options); + + if (this.error() != null) { + throw this.error(); + } + + const options = this._optionsForExec(); + + return this.mongooseCollection.deleteMany(this._conditions, options); +}; + +/** + * hydrates a document + * + * @param {Model} model + * @param {Document} doc + * @param {object} res 3rd parameter to callback + * @param {object} fields + * @param {Query} self + * @param {Array} [pop] array of paths used in population + * @param {Function} callback + * @api private + */ + +function completeOne(model, doc, res, options, fields, userProvidedFields, pop, callback) { + if (options.includeResultMetadata && doc == null) { + _init(null); + return null; + } + + helpers.createModelAndInit(model, doc, fields, userProvidedFields, options, pop, _init); + + function _init(err, casted) { + if (err) { + return callback(err); + } + + + if (options.includeResultMetadata) { + if (doc && casted) { + if (options.session != null) { + casted.$session(options.session); + } + res.value = casted; + } else { + res.value = null; + } + return callback(null, res); + } + if (options.session != null) { + casted.$session(options.session); + } + callback(null, casted); + } +} + +/** + * If the model is a discriminator type and not root, then add the key & value to the criteria. + * @param {Query} query + * @api private + */ + +function prepareDiscriminatorCriteria(query) { + if (!query?.model?.schema) { + return; + } + + const schema = query.model.schema; + + if (schema?.discriminatorMapping && !schema.discriminatorMapping.isRoot) { + query._conditions[schema.discriminatorMapping.key] = schema.discriminatorMapping.value; + } +} + +/** + * Issues a mongodb `findOneAndUpdate()` command. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found + * document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndUpdate()` + * + * #### Available options + * + * - `new`: bool - if true, return the modified document rather than the original. defaults to false (changed in 4.0) + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `fields`: {Object|String} - Field selection. Equivalent to `.select(fields).findOneAndUpdate()` + * - `sort`: if multiple docs are found by the filter, sets the sort order to choose which doc to update + * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 + * - `runValidators`: if true, runs [update validators](https://mongoosejs.com/docs/validation.html#update-validators) on this command. Update validators validate the update operation against the model's schema. + * - `setDefaultsOnInsert`: `true` by default. If `setDefaultsOnInsert` and `upsert` are true, mongoose will apply the [defaults](https://mongoosejs.com/docs/defaults.html) specified in the model's schema if a new document is created. + * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false. + * + * #### Example: + * + * query.findOneAndUpdate(filter, update, options); // returns Query + * query.findOneAndUpdate(filter, update); // returns Query + * // Note that `Query#findOneAndUpdate()` with 1 arg treats the first arg as the `update`, NOT the `filter`. + * query.findOneAndUpdate(update); // returns Query + * query.findOneAndUpdate(); // returns Query + * + * @method findOneAndUpdate + * @memberOf Query + * @instance + * @param {object|Query} [filter] + * @param {object} [update] + * @param {object} [options] + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. + * @param {boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`. + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @param {boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html + * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @see ModifyResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html + * @see findOneAndUpdate https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#findOneAndUpdate + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndUpdate = function(filter, update, options) { + if (typeof filter === 'function' || + typeof update === 'function' || + typeof options === 'function' || + typeof arguments[3] === 'function') { + throw new MongooseError('Query.prototype.findOneAndUpdate() no longer accepts a callback'); + } + + this.op = 'findOneAndUpdate'; + this._validate(); + + switch (arguments.length) { + case 2: + options = undefined; + break; + case 1: + update = filter; + filter = options = undefined; + break; + } + + if (canMerge(filter)) { + this.merge(filter); + } else if (filter != null) { + this.error( + new ObjectParameterError(filter, 'filter', 'findOneAndUpdate') + ); + } + + options = options ? clone(options) : {}; + + if (options.projection) { + this.select(options.projection); + delete options.projection; + } + if (options.fields) { + this.select(options.fields); + delete options.fields; + } + + const globalReturnDocument = this?.model?.base?.options?.returnDocument; + const globalReturnOriginal = this?.model?.base?.options?.returnOriginal; + if (options.new == null && options.returnDocument == null && options.returnOriginal == null) { + if (globalReturnDocument != null) { + options.returnDocument = globalReturnDocument; + } else if (globalReturnOriginal != null) { + options.returnOriginal = globalReturnOriginal; + } + } + + const updatePipeline = this?.model?.base?.options?.updatePipeline; + if (options.updatePipeline == null && updatePipeline != null) { + options.updatePipeline = updatePipeline; + } + + this.setOptions(options); + + // apply doc + if (update) { + this._mergeUpdate(update); + } + + return this; +}; + +/** + * Execute a findOneAndUpdate operation + * + * @method _findOneAndUpdate + * @memberOf Query + * @api private + */ + +Query.prototype._findOneAndUpdate = async function _findOneAndUpdate() { + this._applyTranslateAliases(); + this._castConditions(); + + // Check for empty/invalid filter with requireFilter option + checkRequireFilter(this._conditions, this.options); + + _castArrayFilters(this); + + if (this.error()) { + throw this.error(); + } + + const dbOptions = this.model.db.options; + const baseOptions = this.model.base.options; + applyGlobalMaxTimeMS(this.options, dbOptions, baseOptions); + applyGlobalDiskUse(this.options, dbOptions, baseOptions); + + if ('strict' in this.options) { + this._mongooseOptions.strict = this.options.strict; + } + const options = this._optionsForExec(this.model); + convertNewToReturnDocument(options); + + this._update = this._castUpdate(this._update); + + this._update = setDefaultsOnInsert( + this._conditions, + this.model.schema, + this._update, + options, + this._mongooseOptions, + this + ); + + if (!this._update || utils.hasOwnKeys(this._update) === false) { + if (options.upsert) { + // still need to do the upsert to empty doc + const $set = clone(this._update); + delete $set._id; + this._update = { $set }; + } else { + this._execCount = 0; + const res = await this._findOne(); + return res; + } + } else if (this._update instanceof Error) { + throw this._update; + } else { + // In order to make MongoDB 2.6 happy (see + // https://jira.mongodb.org/browse/SERVER-12266 and related issues) + // if we have an actual update document but $set is empty, junk the $set. + if (this._update.$set && utils.hasOwnKeys(this._update.$set) === false) { + delete this._update.$set; + } + } + + const runValidators = _getOption(this, 'runValidators', false); + if (runValidators) { + await this.validate(this._update, options, false); + } + + if (typeof this._update.toBSON === 'function') { + this._update = this._update.toBSON(); + } + + let res = await this.mongooseCollection.findOneAndUpdate(this._conditions, this._update, options); + for (const fn of this._transforms) { + res = fn(res); + } + const doc = !options.includeResultMetadata ? res : res.value; + + return new Promise((resolve, reject) => { + this._completeOne(doc, res, options.projection, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }); + }); +}; + +/** + * Issues a MongoDB [findOneAndDelete](https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndDelete/) command. + * + * Finds a matching document, removes it, and returns the found document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndDelete()` + * + * #### Available options + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 + * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false. + * + * #### Example: + * + * A.where().findOneAndDelete(conditions, options) // return Query + * A.where().findOneAndDelete(conditions) // returns Query + * A.where().findOneAndDelete() // returns Query + * + * @method findOneAndDelete + * @memberOf Query + * @param {object} [filter] + * @param {object} [options] + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @return {Query} this + * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @api public + */ + +Query.prototype.findOneAndDelete = function(filter, options) { + if (typeof filter === 'function' || + typeof options === 'function' || + typeof arguments[2] === 'function') { + throw new MongooseError('Query.prototype.findOneAndDelete() no longer accepts a callback'); + } + + this.op = 'findOneAndDelete'; + this._validate(); + + if (canMerge(filter)) { + this.merge(filter); + } + + options && this.setOptions(options); + + return this; +}; + +/** + * Execute a `findOneAndDelete()` query + * + * @return {Query} this + * @method _findOneAndDelete + * @memberOf Query + * @api private + */ +Query.prototype._findOneAndDelete = async function _findOneAndDelete() { + this._applyTranslateAliases(); + this._castConditions(); + + // Check for empty/invalid filter with requireFilter option + checkRequireFilter(this._conditions, this.options); + + if (this.error() != null) { + throw this.error(); + } + + const includeResultMetadata = this.options.includeResultMetadata; + + const filter = this._conditions; + const options = this._optionsForExec(this.model); + + let res = await this.mongooseCollection.findOneAndDelete(filter, options); + for (const fn of this._transforms) { + res = fn(res); + } + const doc = !includeResultMetadata ? res : res.value; + + return new Promise((resolve, reject) => { + this._completeOne(doc, res, options.projection, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }); + }); +}; + +/** + * Issues a MongoDB [findOneAndReplace](https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndReplace/) command. + * + * Finds a matching document, removes it, and returns the found document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndReplace()` + * + * #### Available options + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * - `maxTimeMS`: puts a time limit on the query - requires mongodb >= 2.6.0 + * - `includeResultMetadata`: if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * - `requireFilter`: bool - if true, throws an error if the filter is empty (`{}`). Defaults to false. + * + * #### Example: + * + * A.where().findOneAndReplace(filter, replacement, options); // return Query + * A.where().findOneAndReplace(filter); // returns Query + * A.where().findOneAndReplace(); // returns Query + * + * @method findOneAndReplace + * @memberOf Query + * @param {object} [filter] + * @param {object} [replacement] + * @param {object} [options] + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`. + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndReplace = function(filter, replacement, options) { + if (typeof filter === 'function' || + typeof replacement === 'function' || + typeof options === 'function' || + typeof arguments[4] === 'function') { + throw new MongooseError('Query.prototype.findOneAndReplace() no longer accepts a callback'); + } + + this.op = 'findOneAndReplace'; + this._validate(); + + if (canMerge(filter)) { + this.merge(filter); + } else if (filter != null) { + this.error( + new ObjectParameterError(filter, 'filter', 'findOneAndReplace') + ); + } + + if (replacement != null) { + this._mergeUpdate(replacement); + } + + options = options || {}; + + const globalReturnDocument = this?.model?.base?.options?.returnDocument; + const globalReturnOriginal = this?.model?.base?.options?.returnOriginal; + if (options.new == null && options.returnDocument == null && options.returnOriginal == null) { + if (globalReturnDocument != null) { + options.returnDocument = globalReturnDocument; + } else if (globalReturnOriginal != null) { + options.returnOriginal = globalReturnOriginal; + } + } + + this.setOptions(options); + + return this; +}; + +/** + * Execute a findOneAndReplace() query + * + * @return {Query} this + * @method _findOneAndReplace + * @instance + * @memberOf Query + * @api private + */ +Query.prototype._findOneAndReplace = async function _findOneAndReplace() { + this._applyTranslateAliases(); + this._castConditions(); + + // Check for empty/invalid filter with requireFilter option + checkRequireFilter(this._conditions, this.options); + + if (this.error() != null) { + throw this.error(); + } + + if ('strict' in this.options) { + this._mongooseOptions.strict = this.options.strict; + delete this.options.strict; + } + + const filter = this._conditions; + const options = this._optionsForExec(); + convertNewToReturnDocument(options); + + const includeResultMetadata = this.options.includeResultMetadata; + + const modelOpts = { skipId: true }; + if ('strict' in this._mongooseOptions) { + modelOpts.strict = this._mongooseOptions.strict; + } + + const runValidators = _getOption(this, 'runValidators', false); + + try { + const update = new this.model(this._update, null, modelOpts); + if (runValidators) { + await update.validate(); + } else if (update.$__.validationError) { + throw update.$__.validationError; + } + this._update = update.toBSON(); + } catch (err) { + if (err instanceof ValidationError) { + throw err; + } + const validationError = new ValidationError(); + validationError.errors[err.path] = err; + throw validationError; + } + + let res = await this.mongooseCollection.findOneAndReplace(filter, this._update, options); + + for (const fn of this._transforms) { + res = fn(res); + } + + const doc = !includeResultMetadata ? res : res.value; + return new Promise((resolve, reject) => { + this._completeOne(doc, res, options.projection, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }); + }); +}; + +/** + * Finds a single document by its _id field. `findById(id)` is equivalent to + * `findOne({ _id: id })`. + * + * The `id` is cast based on the Schema before sending the command. + * + * This function triggers the following middleware. + * + * - `findOne()` + * + * @method findById + * @memberOf Query + * @instance + * @param {any} id value of `_id` to query by + * @param {object} [projection] optional fields to return + * @param {object} [options] see [`setOptions()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.setOptions()) + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @return {Query} this + * @see findOne https://www.mongodb.com/docs/manual/reference/method/db.collection.findOne/ + * @see Query.select https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @api public + */ + +Query.prototype.findById = function(id, projection, options) { + return this.findOne({ _id: id }, projection, options); +}; + + +/** + * Issues a mongodb findOneAndUpdate command by a document's _id field. + * `findByIdAndUpdate(id, ...)` is equivalent to `findOneAndUpdate({ _id: id }, ...)`. + * + * Finds a matching document, updates it according to the `update` arg, + * passing any `options`, and returns the found document (if any). + * + * This function triggers the following middleware. + * + * - `findOneAndUpdate()` + * + * @method findByIdAndUpdate + * @memberOf Query + * @instance + * @param {any} id value of `_id` to query by + * @param {object} [doc] + * @param {object} [options] + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. + * @param {boolean} [options.new=false] By default, `findOneAndUpdate()` returns the document as it was **before** `update` was applied. If you set `new: true`, `findOneAndUpdate()` will instead give you the object after `update` was applied. **Deprecated:** Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`. + * @param {object} [options.lean] if truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. See [`Query.lean()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.lean()) and [the Mongoose lean tutorial](https://mongoosejs.com/docs/tutorials/lean.html). + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.returnOriginal=null] An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. **Deprecated:** Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`. + * @param {'before'|'after'} [options.returnDocument='before'] Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @param {boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @see Tutorial https://mongoosejs.com/docs/tutorials/findoneandupdate.html + * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @see ModifyResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html + * @see findOneAndUpdate https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#findOneAndUpdate + * @return {Query} this + * @api public + */ + +Query.prototype.findByIdAndUpdate = function(id, update, options) { + return this.findOneAndUpdate({ _id: id }, update, options); +}; + +/** + * Issue a MongoDB `findOneAndDelete()` command by a document's _id field. + * In other words, `findByIdAndDelete(id)` is a shorthand for + * `findOneAndDelete({ _id: id })`. + * + * This function triggers the following middleware. + * + * - `findOneAndDelete()` + * + * @method findByIdAndDelete + * @memberOf Query + * @param {any} id value of `_id` to query by + * @param {object} [options] + * @param {boolean} [options.includeResultMetadata] if true, returns the full [ModifyResult from the MongoDB driver](https://mongodb.github.io/node-mongodb-native/7.0/interfaces/ModifyResult.html) rather than just the document + * @param {ClientSession} [options.session=null] The session associated with this query. See [transactions docs](https://mongoosejs.com/docs/transactions.html). + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @return {Query} this + * @see findAndModify command https://www.mongodb.com/docs/manual/reference/command/findAndModify/ + * @api public + */ + +Query.prototype.findByIdAndDelete = function(id, options) { + return this.findOneAndDelete({ _id: id }, options); +}; + +/** + * Support the `new` option as an alternative to `returnOriginal` for backwards + * compat. + * @api private + */ + +function convertNewToReturnDocument(options) { + if ('new' in options) { + const replacement = options['new'] ? '\'after\'' : '\'before\''; + utils.warn( + 'mongoose: the `new` option for `findOneAndUpdate()` and `findOneAndReplace()` is deprecated. ' + + 'Use `returnDocument: ' + replacement + '` instead.' + ); + options.returnDocument = options['new'] ? 'after' : 'before'; + delete options['new']; + } + if ('returnOriginal' in options) { + const replacement = options['returnOriginal'] ? '\'before\'' : '\'after\''; + utils.warn( + 'mongoose: the `returnOriginal` option for `findOneAndUpdate()` and `findOneAndReplace()` is deprecated. ' + + 'Use `returnDocument: ' + replacement + '` instead.' + ); + options.returnDocument = options['returnOriginal'] ? 'before' : 'after'; + delete options['returnOriginal']; + } + // Temporary since driver 4.0.0-beta does not support `returnDocument` + if (typeof options.returnDocument === 'string') { + options.returnOriginal = options.returnDocument === 'before'; + } +} + +/** + * Get options from query opts, falling back to the base mongoose object. + * @param {Query} query + * @param {object} option + * @param {any} def + * @api private + */ + +function _getOption(query, option, def) { + const opts = query._optionsForExec(query.model); + + if (option in opts) { + return opts[option]; + } + if (option in query.model.base.options) { + return query.model.base.options[option]; + } + return def; +} + +/*! + * ignore + */ + +function _completeOneLean(schema, doc, path, res, opts, callback) { + if (opts.lean && typeof opts.lean.transform === 'function') { + opts.lean.transform(doc); + + for (let i = 0; i < schema.childSchemas.length; i++) { + const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path; + const _schema = schema.childSchemas[i].schema; + const obj = mpath.get(childPath, doc); + if (obj == null) { + continue; + } + if (Array.isArray(obj)) { + for (let i = 0; i < obj.length; i++) { + opts.lean.transform(obj[i]); + } + } else { + opts.lean.transform(obj); + } + _completeOneLean(_schema, obj, childPath, res, opts); + } + if (callback) { + return callback(null, doc); + } else { + return; + } + } + if (opts.includeResultMetadata) { + return callback(null, res); + } + return callback(null, doc); +} + +/*! + * ignore + */ + +function _completeManyLean(schema, docs, path, opts) { + if (opts.lean && typeof opts.lean.transform === 'function') { + for (const doc of docs) { + opts.lean.transform(doc); + } + + for (let i = 0; i < schema.childSchemas.length; i++) { + const childPath = path ? path + '.' + schema.childSchemas[i].model.path : schema.childSchemas[i].model.path; + const _schema = schema.childSchemas[i].schema; + let doc = mpath.get(childPath, docs); + if (doc == null) { + continue; + } + doc = doc.flat(); + for (let i = 0; i < doc.length; i++) { + opts.lean.transform(doc[i]); + } + _completeManyLean(_schema, doc, childPath, opts); + } + } + + return docs; +} +/** + * Override mquery.prototype._mergeUpdate to handle mongoose objects in + * updates. + * + * @param {object} update + * @method _mergeUpdate + * @memberOf Query + * @instance + * @api private + */ + +Query.prototype._mergeUpdate = function(update) { + const updatePipeline = this._mongooseOptions.updatePipeline; + if (!updatePipeline && Array.isArray(update)) { + throw new MongooseError('Cannot pass an array to query updates unless the `updatePipeline` option is set.'); + } + if (!this._update) { + this._update = Array.isArray(update) ? [] : {}; + } + + if (update == null || (typeof update === 'object' && utils.hasOwnKeys(update) === false)) { + return; + } + + if (update instanceof Query) { + if (Array.isArray(this._update)) { + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update._update)})`); + } + if (update._update) { + utils.mergeClone(this._update, update._update); + } + } else if (Array.isArray(update)) { + if (!Array.isArray(this._update)) { + // `_update` may be empty object by default, like in `doc.updateOne()` + // because we create the query first, then run hooks, then apply the update. + if (this._update == null || utils.isEmptyObject(this._update)) { + this._update = []; + } else { + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update)})`); + } + } + this._update = this._update.concat(update); + } else { + if (Array.isArray(this._update)) { + throw new MongooseError(`Cannot mix array and object updates (current: ${_previewUpdate(this._update)}, incoming: ${_previewUpdate(update)})`); + } + utils.mergeClone(this._update, update); + } +}; + +function _previewUpdate(update) { + const preview = util.inspect(update, { + depth: 2, + maxArrayLength: 5, + breakLength: 80, + compact: true + }); + + if (preview.length > 200) { + return `${preview.slice(0, 197)}...`; + } + + return preview; +} + +/** + * Mongoose calls this function internally to validate the query if + * `runValidators` is set + * + * @param {object} castedDoc the update, after casting + * @param {object} options the options from `_optionsForExec()` + * @param {boolean} isOverwriting + * @method validate + * @memberOf Query + * @instance + * @api private + */ + +Query.prototype.validate = async function validate(castedDoc, options, isOverwriting) { + if (typeof arguments[3] === 'function') { + throw new MongooseError('Query.prototype.validate() no longer accepts a callback'); + } + + await _executePreHooks(this, 'validate'); + + if (isOverwriting) { + await castedDoc.$validate(); + } else { + const validationErrors = await updateValidators(this, this.model.schema, castedDoc, options); + if (validationErrors.length > 0) { + const err = new ValidationError(null); + for (const validationError of validationErrors) { + err.addError(validationError.path, validationError); + } + throw err; + } + } + + await _executePostHooks(this, null, null, 'validate'); +}; + +/** + * Execute an updateMany query + * + * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update() + * @method _updateMany + * @memberOf Query + * @instance + * @api private + */ +Query.prototype._updateMany = async function _updateMany() { + this._applyTranslateAliases(); + this._castConditions(); + checkRequireFilter(this._conditions, this.options); + _castArrayFilters(this); + if (this.error() != null) { + throw this.error(); + } + + const options = this._optionsForExec(this.model); + this._update = this._castUpdate(this._update); + if (this._update == null || utils.hasOwnKeys(this._update) === false) { + return { acknowledged: false }; + } + removeUnusedArrayFilters(this._update, options); + + this._update = setDefaultsOnInsert( + this._conditions, + this.model.schema, + this._update, + options, + this._mongooseOptions, + this + ); + + if (_getOption(this, 'runValidators', false)) { + await this.validate(this._update, options, false); + } + + if (typeof this._update.toBSON === 'function') { + this._update = this._update.toBSON(); + } + + return this.mongooseCollection.updateMany(this._conditions, this._update, options); +}; + +/** + * Execute an updateOne query + * + * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update() + * @method _updateOne + * @memberOf Query + * @instance + * @api private + */ +Query.prototype._updateOne = async function _updateOne() { + this._applyTranslateAliases(); + this._castConditions(); + checkRequireFilter(this._conditions, this.options); + _castArrayFilters(this); + if (this.error() != null) { + throw this.error(); + } + + const options = this._optionsForExec(this.model); + this._update = this._castUpdate(this._update); + if (this._update == null || utils.hasOwnKeys(this._update) === false) { + return { acknowledged: false }; + } + removeUnusedArrayFilters(this._update, options); + + this._update = setDefaultsOnInsert( + this._conditions, + this.model.schema, + this._update, + options, + this._mongooseOptions, + this + ); + + if (_getOption(this, 'runValidators', false)) { + await this.validate(this._update, options, false); + } + + if (typeof this._update.toBSON === 'function') { + this._update = this._update.toBSON(); + } + + return this.mongooseCollection.updateOne(this._conditions, this._update, options); +}; + +/** + * Execute a replaceOne query + * + * @see Model.replaceOne https://mongoosejs.com/docs/api/model.html#Model.replaceOne() + * @method _replaceOne + * @memberOf Query + * @instance + * @api private + */ +Query.prototype._replaceOne = async function _replaceOne() { + this._applyTranslateAliases(); + this._castConditions(); + checkRequireFilter(this._conditions, this.options); + _castArrayFilters(this); + if (this.error() != null) { + throw this.error(); + } + + const options = this._optionsForExec(this.model); + this._update = new this.model(this._update, null, { skipId: true }); + removeUnusedArrayFilters(this._update, options); + + if (_getOption(this, 'runValidators', false)) { + await this.validate(this._update, options, true); + } + + if (typeof this._update.toBSON === 'function') { + this._update = this._update.toBSON(); + } + + return this.mongooseCollection.replaceOne(this._conditions, this._update, options); +}; + +/** + * Declare and/or execute this query as an updateMany() operation. + * MongoDB will update _all_ documents that match `filter` (as opposed to just the first one). + * + * **Note** updateMany will _not_ fire update middleware. Use `pre('updateMany')` + * and `post('updateMany')` instead. + * + * #### Example: + * + * const res = await Person.updateMany({ name: /Stark$/ }, { isDeleted: true }); + * res.n; // Number of documents matched + * res.nModified; // Number of documents modified + * + * // Other supported syntaxes + * await Person.find({ name: /Stark$/ }).updateMany({ isDeleted: true }); // Using chaining syntax + * await Person.find().updateMany({ isDeleted: true }); // Set `isDeleted` on _all_ Person documents + * + * This function triggers the following middleware. + * + * - `updateMany()` + * + * @param {object} [filter] + * @param {object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted. + * @param {object} [options] + * @param {boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @param {boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update() + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/ + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output + * @api public + */ + +Query.prototype.updateMany = function(conditions, doc, options, callback) { + if (typeof options === 'function') { + // .update(conditions, doc, callback) + callback = options; + options = undefined; + } else if (typeof doc === 'function') { + // .update(doc, callback); + callback = doc; + doc = conditions; + conditions = {}; + options = undefined; + } else if (typeof conditions === 'function') { + // .update(callback) + callback = conditions; + conditions = undefined; + doc = undefined; + options = undefined; + } else if (typeof conditions === 'object' && !doc && !options && !callback) { + // .update(doc) + doc = conditions; + conditions = undefined; + options = undefined; + callback = undefined; + } + + return _update(this, 'updateMany', conditions, doc, options, callback); +}; + +/** + * Declare and/or execute this query as an updateOne() operation. + * MongoDB will update _only_ the first document that matches `filter`. + * + * - Use `replaceOne()` if you want to overwrite an entire document rather than using [atomic operators](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) like `$set`. + * + * **Note** updateOne will _not_ fire update middleware. Use `pre('updateOne')` + * and `post('updateOne')` instead. + * + * #### Example: + * + * const res = await Person.updateOne({ name: 'Jean-Luc Picard' }, { ship: 'USS Enterprise' }); + * res.acknowledged; // Indicates if this write result was acknowledged. If not, then all other members of this result will be undefined. + * res.matchedCount; // Number of documents that matched the filter + * res.modifiedCount; // Number of documents that were modified + * res.upsertedCount; // Number of documents that were upserted + * res.upsertedId; // Identifier of the inserted document (if an upsert took place) + * + * // Other supported syntaxes + * await Person.findOne({ name: 'Jean-Luc Picard' }).updateOne({ ship: 'USS Enterprise' }); // Using chaining syntax + * await Person.updateOne({ ship: 'USS Enterprise' }); // Updates first doc's `ship` property + * + * This function triggers the following middleware. + * + * - `updateOne()` + * + * @param {object} [filter] + * @param {object|Array} [update] the update command. If array, this update will be treated as an update pipeline and not casted. + * @param {object} [options] + * @param {boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.overwriteDiscriminatorKey=false] Mongoose removes discriminator key updates from `update` by default, set `overwriteDiscriminatorKey` to `true` to allow updating the discriminator key + * @param {boolean} [options.overwriteImmutable=false] Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update() + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/ + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output + * @api public + */ + +Query.prototype.updateOne = function(conditions, doc, options, callback) { + if (typeof options === 'function') { + // .update(conditions, doc, callback) + callback = options; + options = undefined; + } else if (typeof doc === 'function') { + // .update(doc, callback); + callback = doc; + doc = conditions; + conditions = {}; + options = undefined; + } else if (typeof conditions === 'function') { + // .update(callback) + callback = conditions; + conditions = undefined; + doc = undefined; + options = undefined; + } else if (typeof conditions === 'object' && !doc && !options && !callback) { + // .update(doc) + doc = conditions; + conditions = undefined; + options = undefined; + callback = undefined; + } + + return _update(this, 'updateOne', conditions, doc, options, callback); +}; + +/** + * Declare and/or execute this query as a replaceOne() operation. + * MongoDB will replace the existing document and will not accept any [atomic operators](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) (`$set`, etc.) + * + * **Note** replaceOne will _not_ fire update middleware. Use `pre('replaceOne')` + * and `post('replaceOne')` instead. + * + * #### Example: + * + * const res = await Person.replaceOne({ _id: 24601 }, { name: 'Jean Valjean' }); + * res.acknowledged; // Indicates if this write result was acknowledged. If not, then all other members of this result will be undefined. + * res.matchedCount; // Number of documents that matched the filter + * res.modifiedCount; // Number of documents that were modified + * res.upsertedCount; // Number of documents that were upserted + * res.upsertedId; // Identifier of the inserted document (if an upsert took place) + * + * This function triggers the following middleware. + * + * - `replaceOne()` + * + * @param {object} [filter] + * @param {object} [doc] the update command + * @param {object} [options] + * @param {boolean} [options.multipleCastError] by default, mongoose only returns the first error that occurred in casting the query. Turn on this option to aggregate all the cast errors. + * @param {boolean|'throw'} [options.strict] overwrites the schema's [strict mode option](https://mongoosejs.com/docs/guide.html#strict) + * @param {boolean} [options.upsert=false] if true, and no documents found, insert a new document + * @param {object} [options.writeConcern=null] sets the [write concern](https://www.mongodb.com/docs/manual/reference/write-concern/) for replica sets. Overrides the [schema-level write concern](https://mongoosejs.com/docs/guide.html#writeConcern) + * @param {boolean} [options.timestamps=null] If set to `false` and [schema-level timestamps](https://mongoosejs.com/docs/guide.html#timestamps) are enabled, skip timestamps for this update. Does nothing if schema-level timestamps are not set. + * @param {boolean} [options.translateAliases=null] If set to `true`, translates any schema-defined aliases in `filter`, `projection`, `update`, and `distinct`. Throws an error if there are any conflicts where both alias and raw property are defined on the same object. + * @param {boolean} [options.requireFilter=false] If true, throws an error if the filter is empty (`{}`) + * @return {Query} this + * @see Model.update https://mongoosejs.com/docs/api/model.html#Model.update() + * @see Query docs https://mongoosejs.com/docs/queries.html + * @see update https://www.mongodb.com/docs/manual/reference/method/db.collection.update/ + * @see UpdateResult https://mongodb.github.io/node-mongodb-native/7.0/interfaces/UpdateResult.html + * @see MongoDB docs https://www.mongodb.com/docs/manual/reference/command/update/#update-command-output + * @api public + */ + +Query.prototype.replaceOne = function(conditions, doc, options, callback) { + if (typeof options === 'function') { + // .update(conditions, doc, callback) + callback = options; + options = undefined; + } else if (typeof doc === 'function') { + // .update(doc, callback); + callback = doc; + doc = conditions; + conditions = {}; + options = undefined; + } else if (typeof conditions === 'function') { + // .update(callback) + callback = conditions; + conditions = undefined; + doc = undefined; + options = undefined; + } else if (typeof conditions === 'object' && !doc && !options && !callback) { + // .update(doc) + doc = conditions; + conditions = undefined; + options = undefined; + callback = undefined; + } + + return _update(this, 'replaceOne', conditions, doc, options, callback); +}; + +/** + * Internal helper for update, updateMany, updateOne, replaceOne + * @param {Query} query + * @param {string} op + * @param {object} filter + * @param {Document} [doc] + * @param {object} [options] + * @param {Function} callback + * @api private + */ + +function _update(query, op, filter, doc, options, callback) { + // make sure we don't send in the whole Document to merge() + query.op = op; + doc = doc || {}; + + // strict is an option used in the update checking, make sure it gets set + if (options != null) { + if ('strict' in options) { + query._mongooseOptions.strict = options.strict; + } + } + + if (!(filter instanceof Query) && + filter != null && + filter.toString() !== '[object Object]') { + query.error(new ObjectParameterError(filter, 'filter', op)); + } else { + query.merge(filter); + } + + const updatePipeline = query?.model?.base?.options?.updatePipeline; + if (updatePipeline != null && options?.updatePipeline == null) { + options = options || {}; + options.updatePipeline = updatePipeline; + } + + if (utils.isObject(options)) { + query.setOptions(options); + } + + query._mergeUpdate(doc); + + // Hooks + if (callback) { + query.exec(callback); + + return query; + } + + return query; +} + +/** + * Runs a function `fn` and treats the return value of `fn` as the new value + * for the query to resolve to. + * + * Any functions you pass to `transform()` will run **after** any post hooks. + * + * #### Example: + * + * const res = await MyModel.findOne().transform(res => { + * // Sets a `loadedAt` property on the doc that tells you the time the + * // document was loaded. + * return res == null ? + * res : + * Object.assign(res, { loadedAt: new Date() }); + * }); + * + * @method transform + * @memberOf Query + * @instance + * @param {Function} fn function to run to transform the query result + * @return {Query} this + */ + +Query.prototype.transform = function(fn) { + this._transforms.push(fn); + return this; +}; + +/** + * Make this query throw an error if no documents match the given `filter`. + * This is handy for integrating with async/await, because `orFail()` saves you + * an extra `if` statement to check if no document was found. + * + * #### Example: + * + * // Throws if no doc returned + * await Model.findOne({ foo: 'bar' }).orFail(); + * + * // Throws if no document was updated. Note that `orFail()` will still + * // throw if the only document that matches is `{ foo: 'bar', name: 'test' }`, + * // because `orFail()` will throw if no document was _updated_, not + * // if no document was _found_. + * await Model.updateOne({ foo: 'bar' }, { name: 'test' }).orFail(); + * + * // Throws "No docs found!" error if no docs match `{ foo: 'bar' }` + * await Model.find({ foo: 'bar' }).orFail(new Error('No docs found!')); + * + * // Throws "Not found" error if no document was found + * await Model.findOneAndUpdate({ foo: 'bar' }, { name: 'test' }). + * orFail(() => Error('Not found')); + * + * @method orFail + * @memberOf Query + * @instance + * @param {Function|Error} [err] optional error to throw if no docs match `filter`. If not specified, `orFail()` will throw a `DocumentNotFoundError` + * @return {Query} this + */ + +Query.prototype.orFail = function(err) { + this.transform(res => { + switch (this.op) { + case 'find': + if (res.length === 0) { + throw _orFailError(err, this); + } + break; + case 'findOne': + if (res == null) { + throw _orFailError(err, this); + } + break; + case 'replaceOne': + case 'updateMany': + case 'updateOne': + if (res?.matchedCount === 0) { + throw _orFailError(err, this); + } + break; + case 'findOneAndDelete': + case 'findOneAndUpdate': + case 'findOneAndReplace': + if (this.options.includeResultMetadata && res != null && res.value == null) { + throw _orFailError(err, this); + } + if (!this.options.includeResultMetadata && res == null) { + throw _orFailError(err, this); + } + break; + case 'deleteMany': + case 'deleteOne': + if (res.deletedCount === 0) { + throw _orFailError(err, this); + } + break; + default: + break; + } + + return res; + }); + return this; +}; + +/** + * Get the error to throw for `orFail()` + * @param {Error|undefined} err + * @param {Query} query + * @api private + */ + +function _orFailError(err, query) { + if (typeof err === 'function') { + err = err.call(query); + } + + if (err == null) { + err = new DocumentNotFoundError(query.getQuery(), query.model.modelName); + } + + return err; +} + +/** + * Wrapper function to call isPathSelectedInclusive on a query. + * @param {string} path + * @return {boolean} + * @api public + */ + +Query.prototype.isPathSelectedInclusive = function(path) { + return isPathSelectedInclusive(this._fields, path); +}; + +/** + * Executes the query + * + * #### Example: + * + * const promise = query.exec(); + * const promise = query.exec('update'); + * + * @param {string|Function} [operation] + * @return {Promise} + * @api public + */ + +Query.prototype.exec = async function exec(op) { + if (typeof op === 'function' || (arguments.length >= 2 && typeof arguments[1] === 'function')) { + throw new MongooseError('Query.prototype.exec() no longer accepts a callback'); + } + + this._validateOp(); + if (typeof op === 'string') { + this.op = op; + } + + if (this.op == null) { + throw new MongooseError('Query must have `op` before executing'); + } + if (this.model == null) { + throw new MongooseError('Query must have an associated model before executing'); + } + + const thunk = opToThunk.get(this.op); + if (!thunk) { + throw new MongooseError('Query has invalid `op`: "' + this.op + '"'); + } + + if (this.options?.sort && typeof this.options.sort === 'object' && Object.hasOwn(this.options.sort, '')) { + throw new MongooseError('Invalid field "" passed to sort()'); + } + + if (this._execCount > 0) { + let str = this.toString(); + if (str.length > 60) { + str = str.slice(0, 60) + '...'; + } + throw new MongooseError('Query was already executed: ' + str); + } + this._execCount++; + + let skipWrappedFunction = null; + try { + await this._hooks.execPre('exec', this, []); + } catch (err) { + if (err instanceof Kareem.skipWrappedFunction) { + skipWrappedFunction = err; + } else { + throw err; + } + } + + let res; + + let error = null; + try { + await _executePreHooks(this); + res = skipWrappedFunction ? skipWrappedFunction.args[0] : await this[thunk](); + + for (const fn of this._transforms) { + res = fn(res); + } + } catch (err) { + if (err instanceof Kareem.skipWrappedFunction) { + res = err.args[0]; + } else { + error = err; + } + + error = this.model.schema._transformDuplicateKeyError(error); + } + + res = await _executePostHooks(this, res, error); + + await this._hooks.execPost('exec', this, []); + + return res; +}; + +/*! + * ignore + */ + +async function _executePostHooks(query, res, error, op) { + if (query._queryMiddleware == null) { + if (error != null) { + throw error; + } + return res; + } + const filter = buildMiddlewareFilter(query.options, 'post'); + const opts = { filter }; + if (error) { + opts.error = error; + } + + // `result` is array of return args, but queries only return one result. + const [result] = await query._queryMiddleware.execPost( + op || query.op, + query, + [res], + opts + ); + return result; +} + +/*! + * ignore + */ + +function _executePreHooks(query, op) { + if (query._queryMiddleware == null) { + return; + } + const filter = buildMiddlewareFilter(query.options, 'pre'); + return query._queryMiddleware.execPre( + op || query.op, + query, + [], + { filter } + ); +} + +/** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * + * More about [`then()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/then). + * + * @param {Function} [resolve] + * @param {Function} [reject] + * @return {Promise} + * @api public + */ + +Query.prototype.then = function(resolve, reject) { + return this.exec().then(resolve, reject); +}; + +/** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * Like `.then()`, but only takes a rejection handler. + * + * More about [Promise `catch()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/catch). + * + * @param {Function} [reject] + * @return {Promise} + * @api public + */ + +Query.prototype.catch = function(reject) { + return this.exec().then(null, reject); +}; + +/** + * Executes the query returning a `Promise` which will be + * resolved with `.finally()` chained. + * + * More about [Promise `finally()` in JavaScript](https://thecodebarbarian.com/using-promise-finally-in-node-js.html). + * + * @param {Function} [onFinally] + * @return {Promise} + * @api public + */ + +Query.prototype.finally = function(onFinally) { + return this.exec().finally(onFinally); +}; + +/** + * Returns a string representation of this query. + * + * More about [`toString()` in JavaScript](https://masteringjs.io/tutorials/fundamentals/tostring). + * + * #### Example: + * const q = Model.find(); + * console.log(q); // Prints "Query { find }" + * + * @return {string} + * @api public + * @method [Symbol.toStringTag] + * @memberOf Query + */ + +Query.prototype[Symbol.toStringTag] = function toString() { + return `Query { ${this.op} }`; +}; + +/** + * Add pre [middleware](https://mongoosejs.com/docs/middleware.html) to this query instance. Doesn't affect + * other queries. + * + * #### Example: + * + * const q1 = Question.find({ answer: 42 }); + * q1.pre(function middleware() { + * console.log(this.getFilter()); + * }); + * await q1.exec(); // Prints "{ answer: 42 }" + * + * // Doesn't print anything, because `middleware()` is only + * // registered on `q1`. + * await Question.find({ answer: 42 }); + * + * @param {Function} fn + * @return {Promise} + * @api public + */ + +Query.prototype.pre = function(fn) { + this._hooks.pre('exec', fn); + return this; +}; + +/** + * Add post [middleware](https://mongoosejs.com/docs/middleware.html) to this query instance. Doesn't affect + * other queries. + * + * #### Example: + * + * const q1 = Question.find({ answer: 42 }); + * q1.post(function middleware() { + * console.log(this.getFilter()); + * }); + * await q1.exec(); // Prints "{ answer: 42 }" + * + * // Doesn't print anything, because `middleware()` is only + * // registered on `q1`. + * await Question.find({ answer: 42 }); + * + * @param {Function} fn + * @return {Promise} + * @api public + */ + +Query.prototype.post = function(fn) { + this._hooks.post('exec', fn); + return this; +}; + +/** + * Casts obj for an update command. + * + * @param {object} obj + * @return {object} obj after casting its values + * @method _castUpdate + * @memberOf Query + * @instance + * @api private + */ + +Query.prototype._castUpdate = function _castUpdate(obj) { + let schema = this.schema; + + const discriminatorKey = schema.options.discriminatorKey; + const baseSchema = schema._baseSchema || schema; + if (this._mongooseOptions.overwriteDiscriminatorKey && + obj[discriminatorKey] != null && + baseSchema.discriminators) { + const _schema = Object.values(baseSchema.discriminators).find( + discriminator => discriminator.discriminatorMapping.value === obj[discriminatorKey] + ); + if (_schema != null) { + schema = _schema; + } + } + + let upsert; + if ('upsert' in this.options) { + upsert = this.options.upsert; + } + + return castUpdate(schema, obj, { + strict: this._mongooseOptions.strict, + upsert: upsert, + arrayFilters: this.options.arrayFilters, + overwriteDiscriminatorKey: this._mongooseOptions.overwriteDiscriminatorKey, + overwriteImmutable: this._mongooseOptions.overwriteImmutable + }, this, this._conditions); +}; + +/** + * Specifies paths which should be populated with other documents. + * + * #### Example: + * + * let book = await Book.findOne().populate('authors'); + * book.title; // 'Node.js in Action' + * book.authors[0].name; // 'TJ Holowaychuk' + * book.authors[1].name; // 'Nathan Rajlich' + * + * let books = await Book.find().populate({ + * path: 'authors', + * // `match` and `sort` apply to the Author model, + * // not the Book model. These options do not affect + * // which documents are in `books`, just the order and + * // contents of each book document's `authors`. + * match: { name: new RegExp('.*h.*', 'i') }, + * sort: { name: -1 } + * }); + * books[0].title; // 'Node.js in Action' + * // Each book's `authors` are sorted by name, descending. + * books[0].authors[0].name; // 'TJ Holowaychuk' + * books[0].authors[1].name; // 'Marc Harter' + * + * books[1].title; // 'Professional AngularJS' + * // Empty array, no authors' name has the letter 'h' + * books[1].authors; // [] + * + * Paths are populated after the query executes and a response is received. A + * separate query is then executed for each path specified for population. After + * a response for each query has also been returned, the results are passed to + * the callback. + * + * @param {object|string|Array} path either the path(s) to populate or an object specifying all parameters + * @param {object|string} [select] Field selection for the population query + * @param {Model} [model] The model you wish to use for population. If not specified, populate will look up the model by the name in the Schema's `ref` field. + * @param {object} [match] Conditions for the population query + * @param {object} [options] Options for the population query (sort, etc) + * @param {string} [options.path=null] The path to populate. + * @param {boolean} [options.retainNullValues=false] by default, Mongoose removes null and undefined values from populated arrays. Use this option to make `populate()` retain `null` and `undefined` array entries. + * @param {boolean} [options.getters=false] if true, Mongoose will call any getters defined on the `localField`. By default, Mongoose gets the raw value of `localField`. For example, you would need to set this option to `true` if you wanted to [add a `lowercase` getter to your `localField`](https://mongoosejs.com/docs/schematypes.html#schematype-options). + * @param {boolean} [options.clone=false] When you do `BlogPost.find().populate('author')`, blog posts with the same author will share 1 copy of an `author` doc. Enable this option to make Mongoose clone populated docs before assigning them. + * @param {object|Function} [options.match=null] Add an additional filter to the populate query. Can be a filter object containing [MongoDB query syntax](https://www.mongodb.com/docs/manual/tutorial/query-documents/), or a function that returns a filter object. + * @param {Function} [options.transform=null] Function that Mongoose will call on every populated document that allows you to transform the populated document. + * @param {object} [options.options=null] Additional options like `limit` and `lean`. + * @see population https://mongoosejs.com/docs/populate.html + * @see Query#select https://mongoosejs.com/docs/api/query.html#Query.prototype.select() + * @see Model.populate https://mongoosejs.com/docs/api/model.html#Model.populate() + * @return {Query} this + * @api public + */ + +Query.prototype.populate = function(...args) { + // Bail when given no truthy arguments + if (!args.some(Boolean)) { + return this; + } + + const res = utils.populate.apply(null, args); + + const opts = this._mongooseOptions; + + if (opts.lean != null) { + const lean = opts.lean; + for (const populateOptions of res) { + if (populateOptions?.options?.lean == null) { + populateOptions.options = populateOptions.options || {}; + populateOptions.options.lean = lean; + } + } + } + + if (!utils.isObject(opts.populate)) { + opts.populate = {}; + } + + const pop = opts.populate; + + for (const populateOptions of res) { + const path = populateOptions.path; + if (pop[path]?.populate && populateOptions.populate) { + populateOptions.populate = pop[path].populate.concat(populateOptions.populate); + } + + pop[populateOptions.path] = populateOptions; + } + return this; +}; + +/** + * Gets a list of paths to be populated by this query + * + * #### Example: + * + * bookSchema.pre('findOne', function() { + * let keys = this.getPopulatedPaths(); // ['author'] + * }); + * ... + * Book.findOne({}).populate('author'); + * + * #### Example: + * + * // Deep populate + * const q = L1.find().populate({ + * path: 'level2', + * populate: { path: 'level3' } + * }); + * q.getPopulatedPaths(); // ['level2', 'level2.level3'] + * + * @return {Array} an array of strings representing populated paths + * @api public + */ + +Query.prototype.getPopulatedPaths = function getPopulatedPaths() { + const obj = this._mongooseOptions.populate || {}; + const ret = Object.keys(obj); + for (const path of Object.keys(obj)) { + const pop = obj[path]; + if (!Array.isArray(pop.populate)) { + continue; + } + _getPopulatedPaths(ret, pop.populate, path + '.'); + } + return ret; +}; + +/*! + * ignore + */ + +function _getPopulatedPaths(list, arr, prefix) { + for (const pop of arr) { + list.push(prefix + pop.path); + if (!Array.isArray(pop.populate)) { + continue; + } + _getPopulatedPaths(list, pop.populate, prefix + pop.path + '.'); + } +} + +/** + * Casts this query to the schema of `model` + * + * #### Note: + * + * If `obj` is present, it is cast instead of this query. + * + * @param {Model} [model] the model to cast to. If not set, defaults to `this.model` + * @param {object} [obj] + * @return {object} + * @api public + */ + +Query.prototype.cast = function(model, obj) { + obj || (obj = this._conditions); + model = model || this.model; + const discriminatorKey = model.schema.options.discriminatorKey; + if (obj != null && + Object.hasOwn(obj, discriminatorKey)) { + model = getDiscriminatorByValue(model.discriminators, obj[discriminatorKey]) || model; + } + + const opts = { upsert: this.options?.upsert }; + if (this.options) { + if ('strict' in this.options) { + opts.strict = this.options.strict; + } + if ('strictQuery' in this.options) { + opts.strictQuery = this.options.strictQuery; + } + } + if ('sanitizeFilter' in this._mongooseOptions) { + opts.sanitizeFilter = this._mongooseOptions.sanitizeFilter; + } + + try { + return cast(model.schema, obj, opts, this); + } catch (err) { + // CastError, assign model + if (typeof err.setModel === 'function') { + err.setModel(model); + } + throw err; + } +}; + +/** + * Casts selected field arguments for field selection with mongo 2.2 + * + * query.select({ ids: { $elemMatch: { $in: [hexString] }}) + * + * @param {object} fields + * @see https://github.com/Automattic/mongoose/issues/1091 + * @see https://www.mongodb.com/docs/manual/reference/projection/elemMatch/ + * @api private + */ + +Query.prototype._castFields = function _castFields(fields) { + let selected, + elemMatchKeys, + keys, + key, + out; + + if (fields) { + keys = Object.keys(fields); + elemMatchKeys = []; + + // collect $elemMatch args + for (let i = 0; i < keys.length; ++i) { + key = keys[i]; + if (fields[key].$elemMatch) { + selected || (selected = {}); + selected[key] = fields[key]; + elemMatchKeys.push(key); + } + } + } + + if (selected) { + // they passed $elemMatch, cast em + try { + out = this.cast(this.model, selected); + } catch (err) { + return err; + } + + // apply the casted field args + for (let i = 0; i < elemMatchKeys.length; ++i) { + key = elemMatchKeys[i]; + fields[key] = out[key]; + } + } + + return fields; +}; + +/** + * Applies schematype selected options to this query. + * @api private + */ + +Query.prototype._applyPaths = function applyPaths() { + if (!this.model) { + return; + } + this._fields = this._fields || {}; + + let sanitizeProjection = undefined; + if (this.model != null && utils.hasUserDefinedProperty(this.model.db.options, 'sanitizeProjection')) { + sanitizeProjection = this.model.db.options.sanitizeProjection; + } else if (this.model != null && utils.hasUserDefinedProperty(this.model.base.options, 'sanitizeProjection')) { + sanitizeProjection = this.model.base.options.sanitizeProjection; + } else { + sanitizeProjection = this._mongooseOptions.sanitizeProjection; + } + + const schemaLevelProjections = this._mongooseOptions.schemaLevelProjections ?? true; + + if (schemaLevelProjections) { + helpers.applyPaths(this._fields, this.model.schema, sanitizeProjection); + } + + let _selectPopulatedPaths = true; + + if ('selectPopulatedPaths' in this.model.base.options) { + _selectPopulatedPaths = this.model.base.options.selectPopulatedPaths; + } + if ('selectPopulatedPaths' in this.model.schema.options) { + _selectPopulatedPaths = this.model.schema.options.selectPopulatedPaths; + } + + if (_selectPopulatedPaths) { + selectPopulatedFields(this._fields, this._userProvidedFields, this._mongooseOptions.populate); + } +}; + +/** + * Returns a wrapper around a [mongodb driver cursor](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html). + * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function. + * + * The `.cursor()` function triggers pre find hooks, but **not** post find hooks. + * + * #### Example: + * + * // There are 2 ways to use a cursor. First, as a stream: + * Thing. + * find({ name: /^hello/ }). + * cursor(). + * on('data', function(doc) { console.log(doc); }). + * on('end', function() { console.log('Done!'); }); + * + * // Or you can use `.next()` to manually get the next doc in the stream. + * // `.next()` returns a promise, so you can use promises or callbacks. + * const cursor = Thing.find({ name: /^hello/ }).cursor(); + * cursor.next(function(error, doc) { + * console.log(doc); + * }); + * + * // Because `.next()` returns a promise, you can use co + * // to easily iterate through all documents without loading them + * // all into memory. + * const cursor = Thing.find({ name: /^hello/ }).cursor(); + * for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) { + * console.log(doc); + * } + * + * #### Valid options + * + * - `transform`: optional function which accepts a mongoose document. The return value of the function will be emitted on `data` and returned by `.next()`. + * + * @return {QueryCursor} + * @param {object} [options] + * @see QueryCursor https://mongoosejs.com/docs/api/querycursor.html + * @api public + */ + +Query.prototype.cursor = function cursor(opts) { + if (opts) { + this.setOptions(opts); + } + + try { + this.cast(this.model); + } catch (err) { + return (new QueryCursor(this))._markError(err); + } + + return new QueryCursor(this); +}; + +// the rest of these are basically to support older Mongoose syntax with mquery + +/** + * Sets the tailable option (for use with capped collections). + * + * #### Example: + * + * query.tailable(); // true + * query.tailable(true); + * query.tailable(false); + * + * // Set both `tailable` and `awaitData` options + * query.tailable({ awaitData: true }); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @param {boolean} bool defaults to true + * @param {object} [opts] options to set + * @param {boolean} [opts.awaitData] false by default. Set to true to keep the cursor open even if there's no data. + * @param {number} [opts.maxAwaitTimeMS] the maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true + * @see tailable https://www.mongodb.com/docs/manual/tutorial/create-tailable-cursor/ + * @api public + */ + +Query.prototype.tailable = function(val, opts) { + // we need to support the tailable({ awaitData : true }) as well as the + // tailable(true, {awaitData :true}) syntax that mquery does not support + if (typeof val?.constructor === 'function' && val.constructor.name === 'Object') { + opts = val; + val = true; + } + + if (val === undefined) { + val = true; + } + + if (opts && typeof opts === 'object') { + for (const key of Object.keys(opts)) { + if (key === 'awaitData' || key === 'awaitdata') { // backwards compat, see gh-10875 + // For backwards compatibility + this.options['awaitData'] = !!opts[key]; + } else { + this.options[key] = opts[key]; + } + } + } + + this.options.tailable = arguments.length ? !!val : true; + + return this; +}; + +/** + * Declares an intersects query for `geometry()`. + * + * #### Example: + * + * query.where('path').intersects().geometry({ + * type: 'LineString', + * coordinates: [[180.0, 11.0], [180, 9.0]] + * }); + * + * query.where('path').intersects({ + * type: 'LineString', + * coordinates: [[180.0, 11.0], [180, 9.0]] + * }); + * + * #### Note: + * + * **MUST** be used after `where()`. + * + * #### Note: + * + * In Mongoose 3.7, `intersects` changed from a getter to a function. If you need the old syntax, use [this](https://github.com/ebensing/mongoose-within). + * + * @method intersects + * @memberOf Query + * @instance + * @param {object} [arg] + * @return {Query} this + * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/ + * @see geoIntersects https://www.mongodb.com/docs/manual/reference/operator/geoIntersects/ + * @api public + */ + +/** + * Specifies a `$geometry` condition + * + * #### Example: + * + * const polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]] + * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA }) + * + * // or + * const polyB = [[ 0, 0 ], [ 1, 1 ]] + * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB }) + * + * // or + * const polyC = [ 0, 0 ] + * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC }) + * + * // or + * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC }) + * + * The argument is assigned to the most recent path passed to `where()`. + * + * #### Note: + * + * `geometry()` **must** come after either `intersects()` or `within()`. + * + * The `object` argument must contain `type` and `coordinates` properties. + * - type {String} + * - coordinates {Array} + * + * @method geometry + * @memberOf Query + * @instance + * @param {object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples. + * @return {Query} this + * @see $geometry https://www.mongodb.com/docs/manual/reference/operator/geometry/ + * @see Geospatial Support Enhancements https://www.mongodb.com/docs/manual/release-notes/2.4/#geospatial-support-enhancements + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @api public + */ + +/** + * Specifies a `$near` or `$nearSphere` condition + * + * These operators return documents sorted by distance. + * + * #### Example: + * + * query.where('loc').near({ center: [10, 10] }); + * query.where('loc').near({ center: [10, 10], maxDistance: 5 }); + * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true }); + * query.near('loc', { center: [10, 10], maxDistance: 5 }); + * + * @method near + * @memberOf Query + * @instance + * @param {string} [path] + * @param {object} val + * @return {Query} this + * @see $near https://www.mongodb.com/docs/manual/reference/operator/near/ + * @see $nearSphere https://www.mongodb.com/docs/manual/reference/operator/nearSphere/ + * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/ + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @api public + */ + +/** + * Overwriting mquery is needed to support a couple different near() forms found in older + * versions of mongoose + * near([1,1]) + * near(1,1) + * near(field, [1,2]) + * near(field, 1, 2) + * In addition to all of the normal forms supported by mquery + * + * @method near + * @memberOf Query + * @instance + * @api private + */ + +Query.prototype.near = function() { + const params = []; + const sphere = this._mongooseOptions.nearSphere; + + // TODO refactor + + if (arguments.length === 1) { + if (Array.isArray(arguments[0])) { + params.push({ center: arguments[0], spherical: sphere }); + } else if (typeof arguments[0] === 'string') { + // just passing a path + params.push(arguments[0]); + } else if (utils.isObject(arguments[0])) { + if (typeof arguments[0].spherical !== 'boolean') { + arguments[0].spherical = sphere; + } + params.push(arguments[0]); + } else { + throw new TypeError('invalid argument'); + } + } else if (arguments.length === 2) { + if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { + params.push({ center: [arguments[0], arguments[1]], spherical: sphere }); + } else if (typeof arguments[0] === 'string' && Array.isArray(arguments[1])) { + params.push(arguments[0]); + params.push({ center: arguments[1], spherical: sphere }); + } else if (typeof arguments[0] === 'string' && utils.isObject(arguments[1])) { + params.push(arguments[0]); + if (typeof arguments[1].spherical !== 'boolean') { + arguments[1].spherical = sphere; + } + params.push(arguments[1]); + } else { + throw new TypeError('invalid argument'); + } + } else if (arguments.length === 3) { + if (typeof arguments[0] === 'string' && typeof arguments[1] === 'number' + && typeof arguments[2] === 'number') { + params.push(arguments[0]); + params.push({ center: [arguments[1], arguments[2]], spherical: sphere }); + } else { + throw new TypeError('invalid argument'); + } + } else { + throw new TypeError('invalid argument'); + } + + return Query.base.near.apply(this, params); +}; + +/** + * _DEPRECATED_ Specifies a `$nearSphere` condition + * + * #### Example: + * + * query.where('loc').nearSphere({ center: [10, 10], maxDistance: 5 }); + * + * **Deprecated.** Use `query.near()` instead with the `spherical` option set to `true`. + * + * #### Example: + * + * query.where('loc').near({ center: [10, 10], spherical: true }); + * + * @deprecated + * @see near() https://mongoosejs.com/docs/api/query.html#Query.prototype.near() + * @see $near https://www.mongodb.com/docs/manual/reference/operator/near/ + * @see $nearSphere https://www.mongodb.com/docs/manual/reference/operator/nearSphere/ + * @see $maxDistance https://www.mongodb.com/docs/manual/reference/operator/maxDistance/ + */ + +Query.prototype.nearSphere = function() { + this._mongooseOptions.nearSphere = true; + this.near.apply(this, arguments); + return this; +}; + +/** + * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js) + * This function *only* works for `find()` queries. + * You do not need to call this function explicitly, the JavaScript runtime + * will call it for you. + * + * #### Example: + * + * for await (const doc of Model.aggregate([{ $sort: { name: 1 } }])) { + * console.log(doc.name); + * } + * + * @method [Symbol.asyncIterator] + * @memberOf Query + * @instance + * @api public + */ + +Query.prototype[Symbol.asyncIterator] = function queryAsyncIterator() { + // Set so QueryCursor knows it should transform results for async iterators into `{ value, done }` syntax + this._mongooseOptions._asyncIterator = true; + return this.cursor(); +}; + +/** + * Specifies a `$polygon` condition + * + * #### Example: + * + * query.where('loc').within().polygon([10, 20], [13, 25], [7, 15]); + * query.polygon('loc', [10, 20], [13, 25], [7, 15]); + * + * @method polygon + * @memberOf Query + * @instance + * @param {string|Array} [path] + * @param {...Array|object} [coordinatePairs] + * @return {Query} this + * @see $polygon https://www.mongodb.com/docs/manual/reference/operator/polygon/ + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @api public + */ + +/** + * Specifies a `$box` condition + * + * #### Example: + * + * const lowerLeft = [40.73083, -73.99756] + * const upperRight= [40.741404, -73.988135] + * + * query.where('loc').within().box(lowerLeft, upperRight) + * query.box({ ll : lowerLeft, ur : upperRight }) + * + * @method box + * @memberOf Query + * @instance + * @see $box https://www.mongodb.com/docs/manual/reference/operator/box/ + * @see within() Query#within https://mongoosejs.com/docs/api/query.html#Query.prototype.within() + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @param {object|Array} val1 Lower Left Coordinates OR an object of lower-left(ll) and upper-right(ur) Coordinates + * @param {Array} [val2] Upper Right Coordinates + * @return {Query} this + * @api public + */ + +/** + * this is needed to support the mongoose syntax of: + * box(field, { ll : [x,y], ur : [x2,y2] }) + * box({ ll : [x,y], ur : [x2,y2] }) + * + * @method box + * @memberOf Query + * @instance + * @api private + */ + +Query.prototype.box = function(ll, ur) { + if (!Array.isArray(ll) && utils.isObject(ll)) { + ur = ll.ur; + ll = ll.ll; + } + return Query.base.box.call(this, ll, ur); +}; + +/** + * Specifies a `$center` or `$centerSphere` condition. + * + * #### Example: + * + * const area = { center: [50, 50], radius: 10, unique: true } + * query.where('loc').within().circle(area) + * // alternatively + * query.circle('loc', area); + * + * // spherical calculations + * const area = { center: [50, 50], radius: 10, unique: true, spherical: true } + * query.where('loc').within().circle(area) + * // alternatively + * query.circle('loc', area); + * + * @method circle + * @memberOf Query + * @instance + * @param {string} [path] + * @param {object} area + * @return {Query} this + * @see $center https://www.mongodb.com/docs/manual/reference/operator/center/ + * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/ + * @see $geoWithin https://www.mongodb.com/docs/manual/reference/operator/geoWithin/ + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @api public + */ + +/** + * _DEPRECATED_ Alias for [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle()) + * + * **Deprecated.** Use [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle()) instead. + * + * @deprecated + * @method center + * @memberOf Query + * @instance + * @api public + */ + +Query.prototype.center = Query.base.circle; + +/** + * _DEPRECATED_ Specifies a `$centerSphere` condition + * + * **Deprecated.** Use [circle](https://mongoosejs.com/docs/api/query.html#Query.prototype.circle()) instead. + * + * #### Example: + * + * const area = { center: [50, 50], radius: 10 }; + * query.where('loc').within().centerSphere(area); + * + * @deprecated + * @param {string} [path] + * @param {object} val + * @return {Query} this + * @see MongoDB Geospatial Indexing https://www.mongodb.com/docs/manual/core/geospatial-indexes/ + * @see $centerSphere https://www.mongodb.com/docs/manual/reference/operator/centerSphere/ + * @api public + */ + +Query.prototype.centerSphere = function() { + if (typeof arguments[0]?.constructor === 'function' && arguments[0].constructor.name === 'Object') { + arguments[0].spherical = true; + } + + if (typeof arguments[1]?.constructor === 'function' && arguments[1].constructor.name === 'Object') { + arguments[1].spherical = true; + } + + Query.base.circle.apply(this, arguments); +}; + +/** + * Determines if field selection has been made. + * + * @method selected + * @memberOf Query + * @instance + * @return {boolean} + * @api public + */ + +/** + * Determines if inclusive field selection has been made. + * + * query.selectedInclusively(); // false + * query.select('name'); + * query.selectedInclusively(); // true + * + * @method selectedInclusively + * @memberOf Query + * @instance + * @return {boolean} + * @api public + */ + +Query.prototype.selectedInclusively = function selectedInclusively() { + return isInclusive(this._fields); +}; + +/** + * Determines if exclusive field selection has been made. + * + * query.selectedExclusively(); // false + * query.select('-name'); + * query.selectedExclusively(); // true + * query.selectedInclusively(); // false + * + * @method selectedExclusively + * @memberOf Query + * @instance + * @return {boolean} + * @api public + */ + +Query.prototype.selectedExclusively = function selectedExclusively() { + return isExclusive(this._fields); +}; + +/** + * The model this query is associated with. + * + * #### Example: + * + * const q = MyModel.find(); + * q.model === MyModel; // true + * + * @api public + * @property model + * @memberOf Query + * @instance + */ + +Query.prototype.model; + +/** + * Determine if we can merge the given value as a query filter. Override for mquery.canMerge() to allow null + */ + +function canMerge(value) { + return value instanceof Query || utils.isObject(value) || value === null; + +} + +/*! + * Export + */ + +module.exports = Query; diff --git a/backend/node_modules/mongoose/lib/queryHelpers.js b/backend/node_modules/mongoose/lib/queryHelpers.js new file mode 100644 index 0000000..a654bfb --- /dev/null +++ b/backend/node_modules/mongoose/lib/queryHelpers.js @@ -0,0 +1,395 @@ +'use strict'; + +/*! + * Module dependencies + */ + +const PopulateOptions = require('./options/populateOptions'); +const checkEmbeddedDiscriminatorKeyProjection = + require('./helpers/discriminator/checkEmbeddedDiscriminatorKeyProjection'); +const get = require('./helpers/get'); +const getDiscriminatorByValue = + require('./helpers/discriminator/getDiscriminatorByValue'); +const isDefiningProjection = require('./helpers/projection/isDefiningProjection'); +const clone = require('./helpers/clone'); +const isPathSelectedInclusive = require('./helpers/projection/isPathSelectedInclusive'); + +/** + * Prepare a set of path options for query population. This is the MongooseQuery + * version + * + * @param {Query} query + * @param {object} options + * @return {Array} + */ + +exports.preparePopulationOptionsMQ = function preparePopulationOptionsMQ(query, options) { + const _populate = query._mongooseOptions.populate; + const pop = Object.keys(_populate).reduce((vals, key) => vals.concat([_populate[key]]), []); + + // lean options should trickle through all queries + if (options.lean != null) { + pop + .filter(p => p?.options?.lean == null) + .forEach(makeLean(options.lean)); + } + + const session = query?.options?.session || null; + if (session != null) { + pop.forEach(path => { + if (path.options == null) { + path.options = { session: session }; + return; + } + if (!('session' in path.options)) { + path.options.session = session; + } + }); + } + + const projection = query._fieldsForExec(); + for (let i = 0; i < pop.length; ++i) { + if (pop[i] instanceof PopulateOptions) { + pop[i] = new PopulateOptions({ + ...pop[i], + _queryProjection: projection, + _localModel: query.model + }); + } else { + pop[i]._queryProjection = projection; + pop[i]._localModel = query.model; + } + } + + return pop; +}; + +/** + * If the document is a mapped discriminator type, it returns a model instance for that type, otherwise, + * it returns an instance of the given model. + * + * @param {Model} model + * @param {object} doc + * @param {object} fields + * + * @return {Document} + */ +exports.createModel = function createModel(model, doc, fields, userProvidedFields, options) { + model.hooks.execPreSync('createModel', doc); + const discriminatorMapping = model.schema ? + model.schema.discriminatorMapping : + null; + + const key = discriminatorMapping?.isRoot ? + discriminatorMapping.key : + null; + + const value = doc[key]; + if (key && value && model.discriminators) { + const discriminator = model.discriminators[value] || getDiscriminatorByValue(model.discriminators, value); + if (discriminator) { + const _fields = clone(userProvidedFields); + exports.applyPaths(_fields, discriminator.schema); + const _opts = { strict: options?.strict }; + return new discriminator(undefined, _fields, _opts); + } + } + + const _opts = { + skipId: true, + isNew: false, + willInit: true + }; + if (options != null && 'defaults' in options) { + _opts.defaults = options.defaults; + } + if (options != null && 'strict' in options) { + _opts.strict = options.strict; + } + return new model(undefined, fields, _opts); +}; + +/*! + * ignore + */ + +exports.createModelAndInit = function createModelAndInit(model, doc, fields, userProvidedFields, options, populatedIds, callback) { + const initOpts = {}; + if (populatedIds) { + initOpts.populated = populatedIds; + } + if (options?.middleware != null) { + initOpts.middleware = options.middleware; + } + + const casted = exports.createModel(model, doc, fields, userProvidedFields, options); + try { + casted.$init(doc, initOpts, callback); + } catch (error) { + callback(error, casted); + } +}; + +/*! + * ignore + */ + +exports.applyPaths = function applyPaths(fields, schema, sanitizeProjection) { + // determine if query is selecting or excluding fields + let exclude; + let keys; + const minusPathsToSkip = new Set(); + + if (fields) { + keys = Object.keys(fields); + + // Collapse minus paths + const minusPaths = []; + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + if (keys[i][0] !== '-') { + continue; + } + + delete fields[key]; + if (key === '-_id') { + fields['_id'] = 0; + } else { + minusPaths.push(key.slice(1)); + } + } + + keys = Object.keys(fields); + for (let keyIndex = 0; keyIndex < keys.length; ++keyIndex) { + if (keys[keyIndex][0] === '+') { + continue; + } + const field = fields[keys[keyIndex]]; + // Skip `$meta` and `$slice` + if (!isDefiningProjection(field)) { + continue; + } + if (keys[keyIndex] === '_id' && keys.length > 1) { + continue; + } + if (keys[keyIndex] === schema.options.discriminatorKey && keys.length > 1 && field != null && !field) { + continue; + } + exclude = !field; + break; + } + + // Potentially add back minus paths based on schema-level path config + // and whether the projection is inclusive + for (const path of minusPaths) { + const type = schema.path(path); + // If the path isn't selected by default or the projection is not + // inclusive, minus path is treated as equivalent to `key: 0`. + // But we also allow using `-name` to remove `name` from an inclusive + // projection if `name` has schema-level `select: true`. + if (!type?.selected || exclude !== false) { + fields[path] = 0; + exclude = true; + } else if (type?.selected && exclude === false) { + // Make a note of minus paths that are overwriting paths that are + // included by default. + minusPathsToSkip.add(path); + } + } + } + + // if selecting, apply default schematype select:true fields + // if excluding, apply schematype select:false fields + const selected = []; + const excluded = []; + const stack = []; + + analyzeSchema(schema); + switch (exclude) { + case true: + for (const fieldName of excluded) { + fields[fieldName] = 0; + } + break; + case false: + if (schema?.paths['_id']?.options?.select === false) { + fields._id = 0; + } + + for (const fieldName of selected) { + if (minusPathsToSkip.has(fieldName)) { + continue; + } + if (isPathSelectedInclusive(fields, fieldName)) { + continue; + } + fields[fieldName] = fields[fieldName] || 1; + } + break; + case undefined: + if (fields == null) { + break; + } + // Any leftover plus paths must in the schema, so delete them (gh-7017) + for (const key of Object.keys(fields || {})) { + if (key.startsWith('+')) { + delete fields[key]; + } + } + + // user didn't specify fields, implies returning all fields. + // only need to apply excluded fields and delete any plus paths + for (const fieldName of excluded) { + if (fields[fieldName] != null) { + // Skip applying default projections to fields with non-defining + // projections, like `$slice` + continue; + } + fields[fieldName] = 0; + } + break; + } + + function analyzeSchema(schema, prefix) { + prefix || (prefix = ''); + + // avoid recursion + if (stack.indexOf(schema) !== -1) { + return []; + } + stack.push(schema); + + const addedPaths = []; + schema.eachPath(function(path, type) { + if (prefix) path = prefix + '.' + path; + if (type.$isSchemaMap || path.endsWith('.$*')) { + const plusPath = '+' + path; + const hasPlusPath = fields && plusPath in fields; + if (type.options?.select === false && !hasPlusPath) { + excluded.push(path); + } + return; + } + let addedPath = analyzePath(path, type); + // arrays + if (addedPath == null && !Array.isArray(type) && type.$isMongooseArray && !type.$isMongooseDocumentArray) { + addedPath = analyzePath(path, type.embeddedSchemaType); + } + if (addedPath != null) { + addedPaths.push(addedPath); + } + + // nested schemas + if (type.schema) { + const _addedPaths = analyzeSchema(type.schema, path); + + // Special case: if discriminator key is the only field that would + // be projected in, remove it. + if (exclude === false) { + checkEmbeddedDiscriminatorKeyProjection(fields, path, type.schema, + selected, _addedPaths); + } + } + }); + stack.pop(); + return addedPaths; + } + + function analyzePath(path, type) { + if (fields == null) { + return; + } + + // If schema-level selected not set, nothing to do + if (typeof type.selected !== 'boolean') { + return; + } + + // User overwriting default exclusion + if (type.selected === false && fields[path]) { + if (sanitizeProjection) { + fields[path] = 0; + } + + return; + } + + // If set to 0, we're explicitly excluding the discriminator key. Can't do this for all fields, + // because we have tests that assert that using `-path` to exclude schema-level `select: true` + // fields counts as an exclusive projection. See gh-11546 + if (!exclude && type.selected && path === schema.options.discriminatorKey && fields[path] != null && !fields[path]) { + delete fields[path]; + return; + } + + if (exclude === false && type.selected && fields[path] != null && !fields[path]) { + delete fields[path]; + return; + } + + const plusPath = '+' + path; + const hasPlusPath = fields && plusPath in fields; + if (hasPlusPath) { + // forced inclusion + delete fields[plusPath]; + + // if there are other fields being included, add this one + // if no other included fields, leave this out (implied inclusion) + if (exclude === false && keys.length > 1 && !~keys.indexOf(path) && !sanitizeProjection) { + fields[path] = 1; + } else if (exclude == null && sanitizeProjection && type.selected === false) { + fields[path] = 0; + } + + return; + } + + // check for parent exclusions + const pieces = path.split('.'); + let cur = ''; + for (let i = 0; i < pieces.length; ++i) { + cur += cur.length ? '.' + pieces[i] : pieces[i]; + if (excluded.indexOf(cur) !== -1) { + return; + } + } + + // Special case: if user has included a parent path of a discriminator key, + // don't explicitly project in the discriminator key because that will + // project out everything else under the parent path + if (!exclude && (type?.options?.$skipDiscriminatorCheck || false)) { + let cur = ''; + for (let i = 0; i < pieces.length; ++i) { + cur += (cur.length === 0 ? '' : '.') + pieces[i]; + const projection = get(fields, cur, false) || get(fields, cur + '.$', false); + if (projection && typeof projection !== 'object') { + return; + } + } + } + + (type.selected ? selected : excluded).push(path); + return path; + } +}; + +/** + * Set each path query option to lean + * + * @param {object} option + */ + +function makeLean(val) { + return function(option) { + option.options || (option.options = {}); + + if (Array.isArray(val?.virtuals)) { + val = Object.assign({}, val); + val.virtuals = val.virtuals. + filter(path => typeof path === 'string' && path.startsWith(option.path + '.')). + map(path => path.slice(option.path.length + 1)); + } + + option.options.lean = val; + }; +} diff --git a/backend/node_modules/mongoose/lib/schema.js b/backend/node_modules/mongoose/lib/schema.js new file mode 100644 index 0000000..6ed5471 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema.js @@ -0,0 +1,3225 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const EventEmitter = require('events').EventEmitter; +const Kareem = require('kareem'); +const MongooseError = require('./error/mongooseError'); +const SchemaType = require('./schemaType'); +const SchemaTypeOptions = require('./options/schemaTypeOptions'); +const VirtualOptions = require('./options/virtualOptions'); +const VirtualType = require('./virtualType'); +const addAutoId = require('./helpers/schema/addAutoId'); +const clone = require('./helpers/clone'); +const get = require('./helpers/get'); +const getConstructorName = require('./helpers/getConstructorName'); +const getIndexes = require('./helpers/schema/getIndexes'); +const handleReadPreferenceAliases = require('./helpers/query/handleReadPreferenceAliases'); +const idGetter = require('./helpers/schema/idGetter'); +const isIndexSpecEqual = require('./helpers/indexes/isIndexSpecEqual'); +const merge = require('./helpers/schema/merge'); +const mpath = require('mpath'); +const setPopulatedVirtualValue = require('./helpers/populate/setPopulatedVirtualValue'); +const setupTimestamps = require('./helpers/timestamps/setupTimestamps'); +const symbols = require('./schema/symbols'); +const utils = require('./utils'); +const validateRef = require('./helpers/populate/validateRef'); + +const hasNumericSubpathRegex = /\.\d+(\.|$)/; + +let MongooseTypes; + +const queryHooks = require('./constants').queryMiddlewareFunctions; +const documentHooks = require('./constants').documentMiddlewareFunctions; +const hookNames = queryHooks.concat(documentHooks). + reduce((s, hook) => s.add(hook), new Set()); + +const isPOJO = utils.isPOJO; + +let id = 0; + +const numberRE = /^\d+$/; + +/** + * Schema constructor. + * + * #### Example: + * + * const child = new Schema({ name: String }); + * const schema = new Schema({ name: String, age: Number, children: [child] }); + * const Tree = mongoose.model('Tree', schema); + * + * // setting schema options + * new Schema({ name: String }, { id: false, autoIndex: false }) + * + * #### Options: + * + * - [autoIndex](https://mongoosejs.com/docs/guide.html#autoIndex): bool - defaults to null (which means use the connection's autoIndex option) + * - [autoCreate](https://mongoosejs.com/docs/guide.html#autoCreate): bool - defaults to null (which means use the connection's autoCreate option) + * - [bufferCommands](https://mongoosejs.com/docs/guide.html#bufferCommands): bool - defaults to true + * - [bufferTimeoutMS](https://mongoosejs.com/docs/guide.html#bufferTimeoutMS): number - defaults to 10000 (10 seconds). If `bufferCommands` is enabled, the amount of time Mongoose will wait for connectivity to be restablished before erroring out. + * - [capped](https://mongoosejs.com/docs/guide.html#capped): bool | number | object - defaults to false + * - [collection](https://mongoosejs.com/docs/guide.html#collection): string - no default + * - [discriminatorKey](https://mongoosejs.com/docs/guide.html#discriminatorKey): string - defaults to `__t` + * - [id](https://mongoosejs.com/docs/guide.html#id): bool - defaults to true + * - [_id](https://mongoosejs.com/docs/guide.html#_id): bool - defaults to true + * - [minimize](https://mongoosejs.com/docs/guide.html#minimize): bool - controls [document#toObject](https://mongoosejs.com/docs/api/document.html#Document.prototype.toObject()) behavior when called manually - defaults to true + * - [read](https://mongoosejs.com/docs/guide.html#read): string + * - [readConcern](https://mongoosejs.com/docs/guide.html#readConcern): object - defaults to null, use to set a default [read concern](https://www.mongodb.com/docs/manual/reference/read-concern/) for all queries. + * - [writeConcern](https://mongoosejs.com/docs/guide.html#writeConcern): object - defaults to null, use to override [the MongoDB server's default write concern settings](https://www.mongodb.com/docs/manual/reference/write-concern/) + * - [shardKey](https://mongoosejs.com/docs/guide.html#shardKey): object - defaults to `null` + * - [strict](https://mongoosejs.com/docs/guide.html#strict): bool - defaults to true + * - [strictQuery](https://mongoosejs.com/docs/guide.html#strictQuery): bool - defaults to false + * - [toJSON](https://mongoosejs.com/docs/guide.html#toJSON) - object - no default + * - [toObject](https://mongoosejs.com/docs/guide.html#toObject) - object - no default + * - [typeKey](https://mongoosejs.com/docs/guide.html#typeKey) - string - defaults to 'type' + * - [validateBeforeSave](https://mongoosejs.com/docs/guide.html#validateBeforeSave) - bool - defaults to `true` + * - [validateModifiedOnly](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()) - bool - defaults to `false` + * - [versionKey](https://mongoosejs.com/docs/guide.html#versionKey): string or object - defaults to "__v" + * - [optimisticConcurrency](https://mongoosejs.com/docs/guide.html#optimisticConcurrency): bool or string[] or { exclude: string[] } - defaults to false. Set to true to enable [optimistic concurrency](https://thecodebarbarian.com/whats-new-in-mongoose-5-10-optimistic-concurrency.html) for all fields. Set to a string array to enable optimistic concurrency only for the specified fields; note that this **replaces** the default array versioning behavior. Set to `{ exclude: string[] }` to enable optimistic concurrency for all fields except the specified ones; this also replaces the default array versioning. + * - [collation](https://mongoosejs.com/docs/guide.html#collation): object - defaults to null (which means use no collation) + * - [timeseries](https://mongoosejs.com/docs/guide.html#timeseries): object - defaults to null (which means this schema's collection won't be a timeseries collection) + * - [selectPopulatedPaths](https://mongoosejs.com/docs/guide.html#selectPopulatedPaths): boolean - defaults to `true` + * - [skipVersioning](https://mongoosejs.com/docs/guide.html#skipVersioning): object - paths to exclude from versioning + * - [timestamps](https://mongoosejs.com/docs/guide.html#timestamps): object or boolean - defaults to `false`. If true, Mongoose adds `createdAt` and `updatedAt` properties to your schema and manages those properties for you. + * - [pluginTags](https://mongoosejs.com/docs/guide.html#pluginTags): array of strings - defaults to `undefined`. If set and plugin called with `tags` option, will only apply that plugin to schemas with a matching tag. + * - [virtuals](https://mongoosejs.com/docs/tutorials/virtuals.html#virtuals-via-schema-options): object - virtuals to define, alias for [`.virtual`](https://mongoosejs.com/docs/api/schema.html#Schema.prototype.virtual()) + * - [collectionOptions]: object with options passed to [`createCollection()`](https://www.mongodb.com/docs/manual/reference/method/db.createCollection/) when calling `Model.createCollection()` or `autoCreate` set to true. + * - [encryptionType]: the encryption type for the schema. Valid options are `csfle` or `queryableEncryption`. See https://mongoosejs.com/docs/field-level-encryption.html. + * - [lean]: boolean - set to true to make all queries use [lean](https://mongoosejs.com/docs/tutorials/lean.html) by default. Defaults to false. + * + * #### Options for Nested Schemas: + * + * - `excludeIndexes`: bool - defaults to `false`. If `true`, skip building indexes on this schema's paths. + * + * #### Note: + * + * _When nesting schemas, (`children` in the example above), always declare the child schema first before passing it into its parent._ + * + * @param {object|Schema|Array} [definition] Can be one of: object describing schema paths, or schema to copy, or array of objects and schemas + * @param {object} [options] + * @inherits NodeJS EventEmitter https://nodejs.org/api/events.html#class-eventemitter + * @event `init`: Emitted after the schema is compiled into a `Model`. + * @api public + */ + +function Schema(obj, options) { + if (!(this instanceof Schema)) { + return new Schema(obj, options); + } + + this.obj = obj; + this.paths = {}; + this.aliases = {}; + this.subpaths = {}; + this.virtuals = {}; + this.singleNestedPaths = {}; + this.nested = {}; + this.inherits = {}; + this.callQueue = []; + this._indexes = []; + this._searchIndexes = []; + this.methods = options?.methods || {}; + this.methodOptions = {}; + this.statics = options?.statics || {}; + this.tree = {}; + this.query = options?.query || {}; + this.childSchemas = []; + this.plugins = []; + // For internal debugging. Do not use this to try to save a schema in MDB. + this.$id = ++id; + this.mapPaths = []; + this.encryptedFields = {}; + + this.s = { + hooks: new Kareem() + }; + this.options = this.defaultOptions(options); + + // build paths + if (Array.isArray(obj)) { + for (const definition of obj) { + this.add(definition); + } + } else if (obj) { + this.add(obj); + } + + // build virtual paths + if (options?.virtuals) { + const virtuals = options.virtuals; + const pathNames = Object.keys(virtuals); + for (const pathName of pathNames) { + const pathOptions = virtuals[pathName].options ? virtuals[pathName].options : undefined; + const virtual = this.virtual(pathName, pathOptions); + + if (virtuals[pathName].get) { + virtual.get(virtuals[pathName].get); + } + + if (virtuals[pathName].set) { + virtual.set(virtuals[pathName].set); + } + } + } + + // check if _id's value is a subdocument (gh-2276) + const _idSubDoc = obj?._id && utils.isObject(obj._id); + + // ensure the documents get an auto _id unless disabled + const auto_id = !this.paths['_id'] && + (this.options._id) && !_idSubDoc; + + if (auto_id) { + addAutoId(this); + } + + this.setupTimestamp(this.options.timestamps); +} + +/** + * Create virtual properties with alias field + * @api private + */ +function aliasFields(schema, paths) { + for (const path of Object.keys(paths)) { + let alias = null; + if (paths[path] != null) { + alias = paths[path]; + } else { + const options = get(schema.paths[path], 'options'); + if (options == null) { + continue; + } + + alias = options.alias; + } + + if (!alias) { + continue; + } + + const prop = schema.paths[path].path; + if (Array.isArray(alias)) { + for (const a of alias) { + if (typeof a !== 'string') { + throw new MongooseError('Invalid value for alias option on ' + prop + ', got ' + a); + } + + schema.aliases[a] = prop; + + schema. + virtual(a). + get((function(p) { + return function() { + if (typeof this.get === 'function') { + return this.get(p); + } + return this[p]; + }; + })(prop)). + set((function(p) { + return function(v) { + return this.$set(p, v); + }; + })(prop)); + } + + continue; + } + + if (typeof alias !== 'string') { + throw new MongooseError('Invalid value for alias option on ' + prop + ', got ' + alias); + } + + schema.aliases[alias] = prop; + + schema. + virtual(alias). + get((function(p) { + return function() { + if (typeof this.get === 'function') { + return this.get(p); + } + return this[p]; + }; + })(prop)). + set((function(p) { + return function(v) { + return this.$set(p, v); + }; + })(prop)); + } +} + +/*! + * Inherit from EventEmitter. + */ +Schema.prototype = Object.create(EventEmitter.prototype); +Schema.prototype.constructor = Schema; +Schema.prototype.instanceOfSchema = true; + +/*! + * ignore + */ + +Object.defineProperty(Schema.prototype, '$schemaType', { + configurable: false, + enumerable: false, + writable: true +}); + +/** + * Array of child schemas (from document arrays and single nested subdocs) + * and their corresponding compiled models. Each element of the array is + * an object with 2 properties: `schema` and `model`. + * + * This property is typically only useful for plugin authors and advanced users. + * You do not need to interact with this property at all to use mongoose. + * + * @api public + * @property childSchemas + * @memberOf Schema + * @instance + */ + +Object.defineProperty(Schema.prototype, 'childSchemas', { + configurable: false, + enumerable: true, + writable: true +}); + +/** + * Object containing all virtuals defined on this schema. + * The objects' keys are the virtual paths and values are instances of `VirtualType`. + * + * This property is typically only useful for plugin authors and advanced users. + * You do not need to interact with this property at all to use mongoose. + * + * #### Example: + * + * const schema = new Schema({}); + * schema.virtual('answer').get(() => 42); + * + * console.log(schema.virtuals); // { answer: VirtualType { path: 'answer', ... } } + * console.log(schema.virtuals['answer'].getters[0].call()); // 42 + * + * @api public + * @property virtuals + * @memberOf Schema + * @instance + */ + +Object.defineProperty(Schema.prototype, 'virtuals', { + configurable: false, + enumerable: true, + writable: true +}); + +/** + * The original object passed to the schema constructor + * + * #### Example: + * + * const schema = new Schema({ a: String }).add({ b: String }); + * schema.obj; // { a: String } + * + * @api public + * @property obj + * @memberOf Schema + * @instance + */ + +Schema.prototype.obj; + +/** + * The paths defined on this schema. The keys are the top-level paths + * in this schema, and the values are instances of the SchemaType class. + * + * #### Example: + * + * const schema = new Schema({ name: String }, { _id: false }); + * schema.paths; // { name: SchemaString { ... } } + * + * schema.add({ age: Number }); + * schema.paths; // { name: SchemaString { ... }, age: SchemaNumber { ... } } + * + * @api public + * @property paths + * @memberOf Schema + * @instance + */ + +Schema.prototype.paths; + +/** + * Schema as a tree + * + * #### Example: + * + * { + * '_id' : ObjectId + * , 'nested' : { + * 'key' : String + * } + * } + * + * @api private + * @property tree + * @memberOf Schema + * @instance + */ + +Schema.prototype.tree; + +/** + * Creates a new schema with the given definition and options. Equivalent to `new Schema(definition, options)`. + * + * `Schema.create()` is primarily useful for automatic schema type inference in TypeScript. + * + * #### Example: + * + * const schema = Schema.create({ name: String }, { toObject: { virtuals: true } }); + * // Equivalent: + * const schema2 = new Schema({ name: String }, { toObject: { virtuals: true } }); + * + * @param {object} definition + * @param {object} [options] + * @return {Schema} the new schema + * @api public + * @memberOf Schema + * @static + */ + +Schema.create = function create(definition, options) { + return new Schema(definition, options); +}; + +/** + * Returns a deep copy of the schema + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * const clone = schema.clone(); + * clone === schema; // false + * clone.path('name'); // SchemaString { ... } + * + * @return {Schema} the cloned schema + * @api public + * @memberOf Schema + * @instance + */ + +Schema.prototype.clone = function() { + const s = this._clone(); + + // Bubble up `init` for backwards compat + s.on('init', v => this.emit('init', v)); + + return s; +}; + +/*! + * ignore + */ + +Schema.prototype._clone = function _clone(Constructor) { + Constructor = Constructor || (this.base == null ? Schema : this.base.Schema); + + const s = new Constructor({}, this._userProvidedOptions); + s.base = this.base; + s.obj = this.obj; + s.options = clone(this.options); + s.callQueue = this.callQueue.map(function(f) { return f; }); + s.methods = clone(this.methods); + s.methodOptions = clone(this.methodOptions); + s.statics = clone(this.statics); + s.query = clone(this.query); + s.plugins = Array.prototype.slice.call(this.plugins); + s._indexes = clone(this._indexes); + s._searchIndexes = clone(this._searchIndexes); + s.s.hooks = this.s.hooks.clone(); + + s.tree = clone(this.tree); + s.paths = Object.fromEntries( + Object.entries(this.paths).map(([key, value]) => ([key, value.clone()])) + ); + s.nested = clone(this.nested); + s.subpaths = clone(this.subpaths); + for (const schemaType of Object.values(s.paths)) { + if (schemaType.$isSingleNested) { + const path = schemaType.path; + for (const key of Object.keys(schemaType.schema.paths)) { + s.singleNestedPaths[path + '.' + key] = schemaType.schema.paths[key]; + } + for (const key of Object.keys(schemaType.schema.singleNestedPaths)) { + s.singleNestedPaths[path + '.' + key] = + schemaType.schema.singleNestedPaths[key]; + } + for (const key of Object.keys(schemaType.schema.subpaths)) { + s.singleNestedPaths[path + '.' + key] = + schemaType.schema.subpaths[key]; + } + for (const key of Object.keys(schemaType.schema.nested)) { + s.singleNestedPaths[path + '.' + key] = 'nested'; + } + } + } + s._gatherChildSchemas(); + + s.virtuals = clone(this.virtuals); + s.$globalPluginsApplied = this.$globalPluginsApplied; + s.$isRootDiscriminator = this.$isRootDiscriminator; + s.$implicitlyCreated = this.$implicitlyCreated; + s.$id = ++id; + s.$originalSchemaId = this.$id; + s.mapPaths = [].concat(this.mapPaths); + + if (this.discriminatorMapping != null) { + s.discriminatorMapping = Object.assign({}, this.discriminatorMapping); + } + if (this.discriminators != null) { + s.discriminators = Object.assign({}, this.discriminators); + } + if (this._applyDiscriminators != null) { + s._applyDiscriminators = new Map(this._applyDiscriminators); + } + + s.aliases = Object.assign({}, this.aliases); + + s.encryptedFields = clone(this.encryptedFields); + + return s; +}; + +/** + * Returns a new schema that has the picked `paths` from this schema. + * + * This method is analagous to [Lodash's `pick()` function](https://lodash.com/docs/4.17.15#pick) for Mongoose schemas. + * + * #### Example: + * + * const schema = Schema({ name: String, age: Number }); + * // Creates a new schema with the same `name` path as `schema`, + * // but no `age` path. + * const newSchema = schema.pick(['name']); + * + * newSchema.path('name'); // SchemaString { ... } + * newSchema.path('age'); // undefined + * + * @param {string[]} paths List of Paths to pick for the new Schema + * @param {object} [options] Options to pass to the new Schema Constructor (same as `new Schema(.., Options)`). Defaults to `this.options` if not set. + * @return {Schema} + * @api public + */ + +Schema.prototype.pick = function(paths, options) { + const newSchema = new Schema({}, options || this.options); + if (!Array.isArray(paths)) { + throw new MongooseError('Schema#pick() only accepts an array argument, ' + + 'got "' + typeof paths + '"'); + } + + for (const path of paths) { + if (this._hasEncryptedField(path)) { + const encrypt = this.encryptedFields[path]; + const schemaType = this.path(path); + newSchema.add({ + [path]: { + encrypt, + [this.options.typeKey]: schemaType + } + }); + } else if (this.nested[path]) { + newSchema.add({ [path]: get(this.tree, path) }); + } else { + const schematype = this.path(path); + if (schematype == null) { + throw new MongooseError('Path `' + path + '` is not in the schema'); + } + newSchema.add({ [path]: schematype }); + } + } + + if (!this._hasEncryptedFields()) { + newSchema.options.encryptionType = null; + } + + return newSchema; +}; + +/** + * Returns a new schema that has the `paths` from the original schema, minus the omitted ones. + * + * This method is analagous to [Lodash's `omit()` function](https://lodash.com/docs/#omit) for Mongoose schemas. + * + * #### Example: + * + * const schema = Schema({ name: String, age: Number }); + * // Creates a new schema omitting the `age` path + * const newSchema = schema.omit(['age']); + * + * newSchema.path('name'); // SchemaString { ... } + * newSchema.path('age'); // undefined + * + * @param {string[]} paths List of Paths to omit for the new Schema + * @param {object} [options] Options to pass to the new Schema Constructor (same as `new Schema(.., Options)`). Defaults to `this.options` if not set. + * @return {Schema} + * @api public + */ + +Schema.prototype.omit = function(paths, options) { + const newSchema = new Schema(this, options || this.options); + if (!Array.isArray(paths)) { + throw new MongooseError( + 'Schema#omit() only accepts an array argument, ' + + 'got "' + + typeof paths + + '"' + ); + } + + newSchema.remove(paths); + + for (const nested in newSchema.singleNestedPaths) { + if (paths.includes(nested)) { + delete newSchema.singleNestedPaths[nested]; + } + } + + return newSchema; +}; + +/** + * Returns default options for this schema, merged with `options`. + * + * @param {object} [options] Options to overwrite the default options + * @return {object} The merged options of `options` and the default options + * @api private + */ + +Schema.prototype.defaultOptions = function(options) { + this._userProvidedOptions = options == null ? {} : clone(options); + const baseOptions = this.base?.options || {}; + const defaultStrict = baseOptions.strict ?? true; + const defaultStrictQuery = baseOptions.strictQuery ?? false; + const defaultId = baseOptions.id ?? true; + options = { + strict: defaultStrict, + strictQuery: defaultStrictQuery, + bufferCommands: true, + capped: false, // { size, max, autoIndexId } + versionKey: '__v', + optimisticConcurrency: false, + minimize: true, + autoIndex: null, + discriminatorKey: '__t', + shardKey: null, + read: null, + validateBeforeSave: true, + validateModifiedOnly: false, + // the following are only applied at construction time + _id: true, + id: defaultId, + typeKey: 'type', + ...options + }; + + // Treat `undefined` as "not provided" for these options + options.strict ??= defaultStrict; + options.strictQuery ??= defaultStrictQuery; + options.id ??= defaultId; + + if (options.versionKey && typeof options.versionKey !== 'string') { + throw new MongooseError('`versionKey` must be falsy or string, got `' + (typeof options.versionKey) + '`'); + } + + if (typeof options.read === 'string') { + options.read = handleReadPreferenceAliases(options.read); + } else if (Array.isArray(options.read) && typeof options.read[0] === 'string') { + options.read = { + mode: handleReadPreferenceAliases(options.read[0]), + tags: options.read[1] + }; + } + + if (options.optimisticConcurrency && !options.versionKey) { + throw new MongooseError('Must set `versionKey` if using `optimisticConcurrency`'); + } + + return options; +}; + +/** + * Inherit a Schema by applying a discriminator on an existing Schema. + * + * + * #### Example: + * + * const eventSchema = new mongoose.Schema({ timestamp: Date }, { discriminatorKey: 'kind' }); + * + * const clickedEventSchema = new mongoose.Schema({ element: String }, { discriminatorKey: 'kind' }); + * const ClickedModel = eventSchema.discriminator('clicked', clickedEventSchema); + * + * const Event = mongoose.model('Event', eventSchema); + * + * Event.discriminators['clicked']; // Model { clicked } + * + * const doc = await Event.create({ kind: 'clicked', element: '#hero' }); + * doc.element; // '#hero' + * doc instanceof ClickedModel; // true + * + * @param {string} name the name of the discriminator + * @param {Schema} schema the discriminated Schema + * @param {object} [options] discriminator options + * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter. + * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning. + * @param {boolean} [options.overwriteModels=false] by default, Mongoose does not allow you to define a discriminator with the same name as another discriminator. Set this to allow overwriting discriminators with the same name. + * @param {boolean} [options.mergeHooks=true] By default, Mongoose merges the base schema's hooks with the discriminator schema's hooks. Set this option to `false` to make Mongoose use the discriminator schema's hooks instead. + * @param {boolean} [options.mergePlugins=true] By default, Mongoose merges the base schema's plugins with the discriminator schema's plugins. Set this option to `false` to make Mongoose use the discriminator schema's plugins instead. + * @return {Schema} the Schema instance + * @api public + */ +Schema.prototype.discriminator = function(name, schema, options) { + this._applyDiscriminators = this._applyDiscriminators || new Map(); + this._applyDiscriminators.set(name, { schema, options }); + + return this; +}; + +/*! + * Get the document middleware for this schema, filtering out any hooks that are specific to queries. + */ +Schema.prototype._getDocumentMiddleware = function _getDocumentMiddleware() { + return this.s.hooks. + filter(hook => { + if (hook.name === 'updateOne' || hook.name === 'deleteOne') { + return !!hook['document']; + } + if (hook.name === 'remove' || hook.name === 'init') { + return hook['document'] == null || !!hook['document']; + } + if (hook.query != null || hook.document != null) { + return hook.document !== false; + } + return true; + }). + filter(hook => { + // If user has overwritten the method, don't apply built-in middleware + if (this.methods[hook.name]) { + return !hook.fn[symbols.builtInMiddleware]; + } + + return true; + }); +}; + +/*! + * Get this schema's default toObject/toJSON options, including Mongoose global + * options. + */ + +Schema.prototype._defaultToObjectOptions = function(json) { + const path = json ? 'toJSON' : 'toObject'; + if (this._defaultToObjectOptionsMap && this._defaultToObjectOptionsMap[path]) { + return this._defaultToObjectOptionsMap[path]; + } + + const baseOptions = this.base?.options?.[path] || {}; + const schemaOptions = this.options[path] || {}; + // merge base default options with Schema's set default options if available. + // `clone` is necessary here because `utils.options` directly modifies the second input. + const defaultOptions = Object.assign({}, baseOptions, schemaOptions); + + this._defaultToObjectOptionsMap = this._defaultToObjectOptionsMap || {}; + this._defaultToObjectOptionsMap[path] = defaultOptions; + return defaultOptions; +}; + +/** + * Sets the encryption type of the schema, if a value is provided, otherwise + * returns the encryption type. + * + * @param {'csfle' | 'queryableEncryption' | null | undefined} encryptionType plain object with paths to add, or another schema + */ +Schema.prototype.encryptionType = function encryptionType(encryptionType) { + if (arguments.length === 0) { + return this.options.encryptionType; + } + if (!(typeof encryptionType === 'string' || encryptionType === null)) { + throw new MongooseError('invalid `encryptionType`: ${encryptionType}'); + } + this.options.encryptionType = encryptionType; +}; + +/** + * Adds key path / schema type pairs to this schema. + * + * #### Example: + * + * const ToySchema = new Schema(); + * ToySchema.add({ name: 'string', color: 'string', price: 'number' }); + * + * const TurboManSchema = new Schema(); + * // You can also `add()` another schema and copy over all paths, virtuals, + * // getters, setters, indexes, methods, and statics. + * TurboManSchema.add(ToySchema).add({ year: Number }); + * + * @param {object|Schema} obj plain object with paths to add, or another schema + * @param {string} [prefix] path to prefix the newly added paths with + * @return {Schema} the Schema instance + * @api public + */ + +Schema.prototype.add = function add(obj, prefix) { + if (obj instanceof Schema || obj?.instanceOfSchema) { + merge(this, obj); + return this; + } + + // Special case: setting top-level `_id` to false should convert to disabling + // the `_id` option. This behavior never worked before 5.4.11 but numerous + // codebases use it (see gh-7516, gh-7512). + if (obj._id === false && prefix == null) { + this.options._id = false; + } + + prefix = prefix || ''; + // avoid prototype pollution + if (prefix === '__proto__.' || prefix === 'constructor.' || prefix === 'prototype.') { + return this; + } + + const keys = Object.keys(obj); + const typeKey = this.options.typeKey; + for (const key of keys) { + if (utils.specialProperties.has(key)) { + continue; + } + + const fullPath = prefix + key; + const val = obj[key]; + + if (val == null) { + throw new TypeError('Invalid value for schema path `' + fullPath + + '`, got value "' + val + '"'); + } + // Retain `_id: false` but don't set it as a path, re: gh-8274. + if (key === '_id' && val === false) { + continue; + } + // Deprecate setting schema paths to primitive types (gh-7558) + let isMongooseTypeString = false; + if (typeof val === 'string') { + // Handle the case in which the type is specified as a string (eg. 'date', 'oid', ...) + const MongooseTypes = this.base?.Schema.Types ?? Schema.Types; + const upperVal = val.charAt(0).toUpperCase() + val.substring(1); + isMongooseTypeString = MongooseTypes[upperVal] != null; + } + if ( + key !== '_id' && + ((typeof val !== 'object' && typeof val !== 'function' && !isMongooseTypeString) || + val == null) + ) { + throw new TypeError(`Invalid schema configuration: \`${val}\` is not ` + + `a valid type at path \`${key}\`. See ` + + 'https://bit.ly/mongoose-schematypes for a list of valid schema types.'); + } + if (val instanceof VirtualType || (val.constructor?.name ?? null) === 'VirtualType') { + this.virtual(val); + continue; + } + + if (Array.isArray(val) && val.length === 1 && val[0] == null) { + throw new TypeError('Invalid value for schema Array path `' + fullPath + + '`, got value "' + val[0] + '"'); + } + + if (!(isPOJO(val) || val instanceof SchemaTypeOptions)) { + // Special-case: Non-options definitely a path so leaf at this node + // Examples: Schema instances, SchemaType instances + if (prefix) { + this.nested[prefix.substring(0, prefix.length - 1)] = true; + } + this.path(prefix + key, val); + if (!val[0]?.instanceOfSchema && utils.isPOJO(val[0]?.discriminators)) { + const schemaType = this.path(prefix + key); + for (const key in val[0].discriminators) { + schemaType.discriminator(key, val[0].discriminators[key]); + } + } + } else if (utils.hasOwnKeys(val) === false) { + // Special-case: {} always interpreted as Mixed path so leaf at this node + if (prefix) { + this.nested[prefix.substring(0, prefix.length - 1)] = true; + } + this.path(fullPath, val); // mixed type + } else if (!val[typeKey] || (typeKey === 'type' && isPOJO(val.type) && val.type.type)) { + // Special-case: POJO with no bona-fide type key - interpret as tree of deep paths so recurse + // nested object `{ last: { name: String } }`. Avoid functions with `.type` re: #10807 because + // NestJS sometimes adds `Date.type`. + this.nested[fullPath] = true; + this.add(val, fullPath + '.'); + } else { + // There IS a bona-fide type key that may also be a POJO + const _typeDef = val[typeKey]; + if (isPOJO(_typeDef) && utils.hasOwnKeys(_typeDef)) { + // If a POJO is the value of a type key, make it a subdocument + if (prefix) { + this.nested[prefix.substring(0, prefix.length - 1)] = true; + } + + const childSchemaOptions = {}; + if (this._userProvidedOptions.typeKey) { + childSchemaOptions.typeKey = this._userProvidedOptions.typeKey; + } + // propagate 'strict' option to child schema + if (this._userProvidedOptions.strict != null) { + childSchemaOptions.strict = this._userProvidedOptions.strict; + } + if (this._userProvidedOptions.toObject != null) { + childSchemaOptions.toObject = utils.omit(this._userProvidedOptions.toObject, ['transform']); + } + if (this._userProvidedOptions.toJSON != null) { + childSchemaOptions.toJSON = utils.omit(this._userProvidedOptions.toJSON, ['transform']); + } + + const _schema = new Schema(_typeDef, childSchemaOptions); + _schema.$implicitlyCreated = true; + const schemaWrappedPath = Object.assign({}, val, { [typeKey]: _schema }); + this.path(prefix + key, schemaWrappedPath); + } else { + // Either the type is non-POJO or we interpret it as Mixed anyway + if (prefix) { + this.nested[prefix.substring(0, prefix.length - 1)] = true; + } + this.path(prefix + key, val); + if (!val?.instanceOfSchema && utils.isPOJO(val?.discriminators)) { + const schemaType = this.path(prefix + key); + for (const key in val.discriminators) { + schemaType.discriminator(key, val.discriminators[key]); + } + } + } + } + + if (val.instanceOfSchema && val.encryptionType() != null) { + // schema.add({ field: }) + if (this.encryptionType() != val.encryptionType()) { + throw new MongooseError('encryptionType of a nested schema must match the encryption type of the parent schema.'); + } + + for (const [encryptedField, encryptedFieldConfig] of Object.entries(val.encryptedFields)) { + const path = fullPath + '.' + encryptedField; + this._addEncryptedField(path, encryptedFieldConfig); + } + } else if (typeof val === 'object' && 'encrypt' in val) { + // schema.add({ field: { type: , encrypt: { ... }}}) + const { encrypt } = val; + + if (this.encryptionType() == null) { + throw new MongooseError('encryptionType must be provided'); + } + + this._addEncryptedField(fullPath, encrypt); + } else { + // if the field was already encrypted and we re-configure it to be unencrypted, remove + // the encrypted field configuration + this._removeEncryptedField(fullPath); + } + } + + const aliasObj = Object.fromEntries( + Object.entries(obj).map(([key]) => ([prefix + key, null])) + ); + aliasFields(this, aliasObj); + return this; +}; + +/** + * @param {string} path + * @param {object} fieldConfig + * + * @api private + */ +Schema.prototype._addEncryptedField = function _addEncryptedField(path, fieldConfig) { + const type = this.path(path).autoEncryptionType(); + if (type == null) { + throw new MongooseError(`Invalid BSON type for FLE field: '${path}'`); + } + + this.encryptedFields[path] = clone(fieldConfig); +}; + +/** + * @param {string} path + * + * @api private + */ +Schema.prototype._removeEncryptedField = function _removeEncryptedField(path) { + delete this.encryptedFields[path]; +}; + +/** + * @api private + * + * @returns {boolean} + */ +Schema.prototype._hasEncryptedFields = function _hasEncryptedFields() { + return utils.hasOwnKeys(this.encryptedFields); +}; + +/** + * @param {string} path + * @returns {boolean} + * + * @api private + */ +Schema.prototype._hasEncryptedField = function _hasEncryptedField(path) { + return path in this.encryptedFields; +}; + + +/** + * Builds an encryptedFieldsMap for the schema. + * + * @api private + */ +Schema.prototype._buildEncryptedFields = function() { + const fields = Object.entries(this.encryptedFields).map( + ([path, config]) => { + const bsonType = this.path(path).autoEncryptionType(); + // { path, bsonType, keyId, queries? } + return { path, bsonType, ...config }; + }); + + return { fields }; +}; + +/** + * Builds a schemaMap for the schema, if the schema is configured for client-side field level encryption. + * + * @api private + */ +Schema.prototype._buildSchemaMap = function() { + /** + * `schemaMap`s are JSON schemas, which use the following structure to represent objects: + * { field: { bsonType: 'object', properties: { ... } } } + * + * for example, a schema that looks like this `{ a: { b: int32 } }` would be encoded as + * `{ a: { bsonType: 'object', properties: { b: < encryption configuration > } } }` + * + * This function takes an array of path segments, an output object (that gets mutated) and + * a value to be associated with the full path, and constructs a valid CSFLE JSON schema path for + * the object. This works for deeply nested properties as well. + * + * @param {string[]} path array of path components + * @param {object} object the object in which to build a JSON schema of `path`'s properties + * @param {object} value the value to associate with the path in object + */ + function buildNestedPath(path, object, value) { + let i = 0, component = path[i]; + for (; i < path.length - 1; ++i, component = path[i]) { + object[component] = object[component] == null ? { + bsonType: 'object', + properties: {} + } : object[component]; + object = object[component].properties; + } + object[component] = value; + } + + const schemaMapPropertyReducer = (accum, [path, propertyConfig]) => { + const bsonType = this.path(path).autoEncryptionType(); + const pathComponents = path.split('.'); + const configuration = { encrypt: { ...propertyConfig, bsonType } }; + buildNestedPath(pathComponents, accum, configuration); + return accum; + }; + + const properties = Object.entries(this.encryptedFields).reduce( + schemaMapPropertyReducer, + {}); + + return { + bsonType: 'object', + properties + }; +}; + +/** + * Add an alias for `path`. This means getting or setting the `alias` + * is equivalent to getting or setting the `path`. + * + * #### Example: + * + * const toySchema = new Schema({ n: String }); + * + * // Make 'name' an alias for 'n' + * toySchema.alias('n', 'name'); + * + * const Toy = mongoose.model('Toy', toySchema); + * const turboMan = new Toy({ n: 'Turbo Man' }); + * + * turboMan.name; // 'Turbo Man' + * turboMan.n; // 'Turbo Man' + * + * turboMan.name = 'Turbo Man Action Figure'; + * turboMan.n; // 'Turbo Man Action Figure' + * + * await turboMan.save(); // Saves { _id: ..., n: 'Turbo Man Action Figure' } + * + * + * @param {string} path real path to alias + * @param {string|string[]} alias the path(s) to use as an alias for `path` + * @return {Schema} the Schema instance + * @api public + */ + +Schema.prototype.alias = function alias(path, alias) { + aliasFields(this, { [path]: alias }); + return this; +}; + +/** + * Remove an index by name or index specification. + * + * removeIndex only removes indexes from your schema object. Does **not** affect the indexes + * in MongoDB. + * + * #### Example: + * + * const ToySchema = new Schema({ name: String, color: String, price: Number }); + * + * // Add a new index on { name, color } + * ToySchema.index({ name: 1, color: 1 }); + * + * // Remove index on { name, color } + * // Keep in mind that order matters! `removeIndex({ color: 1, name: 1 })` won't remove the index + * ToySchema.removeIndex({ name: 1, color: 1 }); + * + * // Add an index with a custom name + * ToySchema.index({ color: 1 }, { name: 'my custom index name' }); + * // Remove index by name + * ToySchema.removeIndex('my custom index name'); + * + * @param {object|string} index name or index specification + * @return {Schema} the Schema instance + * @api public + */ + +Schema.prototype.removeIndex = function removeIndex(index) { + if (arguments.length > 1) { + throw new MongooseError('removeIndex() takes only 1 argument'); + } + + if (typeof index !== 'object' && typeof index !== 'string') { + throw new MongooseError('removeIndex() may only take either an object or a string as an argument'); + } + + if (typeof index === 'object') { + for (let i = this._indexes.length - 1; i >= 0; --i) { + if (isIndexSpecEqual(this._indexes[i][0], index)) { + this._indexes.splice(i, 1); + } + } + } else { + for (let i = this._indexes.length - 1; i >= 0; --i) { + if (this._indexes[i][1] != null && this._indexes[i][1].name === index) { + this._indexes.splice(i, 1); + } + } + } + + return this; +}; + +/** + * Remove all indexes from this schema. + * + * clearIndexes only removes indexes from your schema object. Does **not** affect the indexes + * in MongoDB. + * + * #### Example: + * + * const ToySchema = new Schema({ name: String, color: String, price: Number }); + * ToySchema.index({ name: 1 }); + * ToySchema.index({ color: 1 }); + * + * // Remove all indexes on this schema + * ToySchema.clearIndexes(); + * + * ToySchema.indexes(); // [] + * + * @return {Schema} the Schema instance + * @api public + */ + +Schema.prototype.clearIndexes = function clearIndexes() { + this._indexes.length = 0; + + return this; +}; + +/** + * Add an [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) that Mongoose will create using `Model.createSearchIndex()`. + * This function only works when connected to MongoDB Atlas. + * + * #### Example: + * + * const ToySchema = new Schema({ name: String, color: String, price: Number }); + * ToySchema.searchIndex({ name: 'test', definition: { mappings: { dynamic: true } } }); + * + * @param {object} description index options, including `name` and `definition` + * @param {string} description.name + * @param {object} description.definition + * @return {Schema} the Schema instance + * @api public + */ + +Schema.prototype.searchIndex = function searchIndex(description) { + this._searchIndexes.push(description); + + return this; +}; + +/** + * Reserved document keys. + * + * Keys in this object are names that are warned in schema declarations + * because they have the potential to break Mongoose/ Mongoose plugins functionality. If you create a schema + * using `new Schema()` with one of these property names, Mongoose will log a warning. + * + * - _posts + * - _pres + * - collection + * - emit + * - errors + * - get + * - init + * - isModified + * - isNew + * - listeners + * - modelName + * - on + * - once + * - populated + * - prototype + * - remove + * - removeListener + * - save + * - schema + * - toObject + * - validate + * + * _NOTE:_ Use of these terms as method names is permitted, but play at your own risk, as they may be existing mongoose document methods you are stomping on. + * + * const schema = new Schema(..); + * schema.methods.init = function () {} // potentially breaking + * + * @property reserved + * @memberOf Schema + * @static + */ + +Schema.reserved = Object.create(null); +Schema.prototype.reserved = Schema.reserved; + +const reserved = Schema.reserved; +// Core object +reserved['prototype'] = +// EventEmitter +reserved.emit = +reserved.listeners = +reserved.removeListener = + +// document properties and functions +reserved.collection = +reserved.errors = +reserved.get = +reserved.init = +reserved.isModified = +reserved.isNew = +reserved.populated = +reserved.remove = +reserved.save = +reserved.toObject = +reserved.validate = 1; +reserved.collection = 1; + +/** + * Gets/sets schema paths. + * + * Sets a path (if arity 2) + * Gets a path (if arity 1) + * + * #### Example: + * + * schema.path('name') // returns a SchemaType + * schema.path('name', Number) // changes the schemaType of `name` to Number + * + * @param {string} path The name of the Path to get / set + * @param {object} [obj] The Type to set the path to, if provided the path will be SET, otherwise the path will be GET + * @api public + */ + +Schema.prototype.path = function(path, obj) { + if (obj === undefined) { + if (this.paths[path] != null) { + return this.paths[path]; + } + // Convert to '.$' to check subpaths re: gh-6405 + const cleanPath = _pathToPositionalSyntax(path); + let schematype = _getPath(this, path, cleanPath); + if (schematype != null) { + return schematype; + } + + // Look for maps + const mapPath = getMapPath(this, path); + if (mapPath != null) { + return mapPath; + } + + // Look if a parent of this path is mixed + schematype = this.hasMixedParent(cleanPath); + if (schematype != null) { + return schematype; + } + + // subpaths? + return hasNumericSubpathRegex.test(path) + ? getPositionalPath(this, path, cleanPath) + : undefined; + } + + // some path names conflict with document methods + const firstPieceOfPath = path.split('.')[0]; + if (reserved[firstPieceOfPath] && !this.options.suppressReservedKeysWarning) { + const errorMessage = `\`${firstPieceOfPath}\` is a reserved schema pathname and may break some functionality. ` + + 'You are allowed to use it, but use at your own risk. ' + + 'To disable this warning pass `suppressReservedKeysWarning` as a schema option.'; + + utils.warn(errorMessage); + } + + if (typeof obj === 'object' && utils.hasUserDefinedProperty(obj, 'ref')) { + validateRef(obj.ref, path); + } + + // update the tree + const subpaths = path.split(/\./); + const last = subpaths.pop(); + let branch = this.tree; + let fullPath = ''; + + for (const sub of subpaths) { + if (utils.specialProperties.has(sub)) { + throw new MongooseError('Cannot set special property `' + sub + '` on a schema'); + } + fullPath = fullPath += (fullPath.length > 0 ? '.' : '') + sub; + if (!branch[sub]) { + this.nested[fullPath] = true; + branch[sub] = {}; + } + if (typeof branch[sub] !== 'object') { + const msg = 'Cannot set nested path `' + path + '`. ' + + 'Parent path `' + + fullPath + + '` already set to type ' + branch[sub].name + + '.'; + throw new MongooseError(msg); + } + branch = branch[sub]; + } + + branch[last] = clone(obj); + + this.paths[path] = this.interpretAsType(path, obj, this.options); + const schemaType = this.paths[path]; + + // If overwriting an existing path, make sure to clear the childSchemas + this.childSchemas = this.childSchemas.filter(childSchema => childSchema.path !== path); + + if (schemaType.$isSchemaMap) { + // Maps can have arbitrary keys, so `$*` is internal shorthand for "any key" + // The '$' is to imply this path should never be stored in MongoDB so we + // can easily build a regexp out of this path, and '*' to imply "any key." + const mapPath = path + '.$*'; + + this.paths[mapPath] = schemaType.$__schemaType; + this.mapPaths.push(this.paths[mapPath]); + if (schemaType.$__schemaType.$isSingleNested) { + this.childSchemas.push({ + schema: schemaType.$__schemaType.schema, + model: schemaType.$__schemaType.Constructor, + path: path + }); + } + } + + if (schemaType.$isSingleNested) { + for (const key of Object.keys(schemaType.schema.paths)) { + this.singleNestedPaths[path + '.' + key] = schemaType.schema.paths[key]; + } + for (const key of Object.keys(schemaType.schema.singleNestedPaths)) { + this.singleNestedPaths[path + '.' + key] = + schemaType.schema.singleNestedPaths[key]; + } + for (const key of Object.keys(schemaType.schema.subpaths)) { + this.singleNestedPaths[path + '.' + key] = + schemaType.schema.subpaths[key]; + } + for (const key of Object.keys(schemaType.schema.nested)) { + this.singleNestedPaths[path + '.' + key] = 'nested'; + } + + Object.defineProperty(schemaType.schema, 'base', { + configurable: true, + enumerable: false, + writable: false, + value: this.base + }); + + schemaType.Constructor.base = this.base; + this.childSchemas.push({ + schema: schemaType.schema, + model: schemaType.Constructor, + path: path + }); + } else if (schemaType.$isMongooseDocumentArray) { + Object.defineProperty(schemaType.schema, 'base', { + configurable: true, + enumerable: false, + writable: false, + value: this.base + }); + + schemaType.Constructor.base = this.base; + this.childSchemas.push({ + schema: schemaType.schema, + model: schemaType.Constructor, + path: path + }); + } + + if (schemaType.$isMongooseArray && !schemaType.$isMongooseDocumentArray) { + let arrayPath = path; + let _schemaType = schemaType; + + const toAdd = []; + while (_schemaType.$isMongooseArray) { + arrayPath = arrayPath + '.$'; + + _schemaType.embeddedSchemaType._arrayPath = arrayPath; + _schemaType.embeddedSchemaType._arrayParentPath = path; + _schemaType = _schemaType.embeddedSchemaType; + + this.subpaths[arrayPath] = _schemaType; + } + + for (const _schemaType of toAdd) { + this.subpaths[_schemaType.path] = _schemaType; + } + } + + if (schemaType.$isMongooseDocumentArray) { + for (const key of Object.keys(schemaType.schema.paths)) { + const _schemaType = schemaType.schema.paths[key]; + this.subpaths[path + '.' + key] = _schemaType; + if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) { + _schemaType.$parentSchemaDocArray = schemaType; + } + } + for (const key of Object.keys(schemaType.schema.subpaths)) { + const _schemaType = schemaType.schema.subpaths[key]; + this.subpaths[path + '.' + key] = _schemaType; + if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) { + _schemaType.$parentSchemaDocArray = schemaType; + } + } + for (const key of Object.keys(schemaType.schema.singleNestedPaths)) { + const _schemaType = schemaType.schema.singleNestedPaths[key]; + this.subpaths[path + '.' + key] = _schemaType; + if (typeof _schemaType === 'object' && _schemaType != null && _schemaType.$parentSchemaDocArray == null) { + _schemaType.$parentSchemaDocArray = schemaType; + } + } + } + + return this; +}; + +/*! + * ignore + */ + +Schema.prototype._gatherChildSchemas = function _gatherChildSchemas() { + const childSchemas = []; + + for (const path of Object.keys(this.paths)) { + if (typeof path !== 'string') { + continue; + } + const schematype = this.paths[path]; + if (schematype.$isMongooseDocumentArray || schematype.$isSingleNested) { + childSchemas.push({ + schema: schematype.schema, + model: schematype.Constructor, + path: path + }); + } else if (schematype.$isSchemaMap && schematype.$__schemaType.$isSingleNested) { + childSchemas.push({ + schema: schematype.$__schemaType.schema, + model: schematype.$__schemaType.Constructor, + path: path + }); + } + } + + this.childSchemas = childSchemas; + return childSchemas; +}; + +/*! + * ignore + */ + +function _getPath(schema, path, cleanPath) { + if (Object.hasOwn(schema.paths, path)) { + return schema.paths[path]; + } + if (Object.hasOwn(schema.subpaths, cleanPath)) { + const subpath = schema.subpaths[cleanPath]; + if (subpath === 'nested') { + return undefined; + } + return subpath; + } + if (Object.hasOwn(schema.singleNestedPaths, cleanPath) && typeof schema.singleNestedPaths[cleanPath] === 'object') { + const singleNestedPath = schema.singleNestedPaths[cleanPath]; + if (singleNestedPath === 'nested') { + return undefined; + } + return singleNestedPath; + } + + return null; +} + +/*! + * ignore + */ + +function _pathToPositionalSyntax(path) { + if (!/\.\d+/.test(path)) { + return path; + } + return path.replace(/\.\d+\./g, '.$.').replace(/\.\d+$/, '.$'); +} + +/*! + * ignore + */ + +function getMapPath(schema, path) { + if (schema.mapPaths.length === 0) { + return null; + } + for (const val of schema.mapPaths) { + const cleanPath = val.path.replace(/\.\$\*/g, ''); + if (path === cleanPath || (path.startsWith(cleanPath + '.') && path.slice(cleanPath.length + 1).indexOf('.') === -1)) { + return val; + } else if (val.schema && path.startsWith(cleanPath + '.')) { + let remnant = path.slice(cleanPath.length + 1); + remnant = remnant.slice(remnant.indexOf('.') + 1); + return val.schema.paths[remnant]; + } else if (val.$isSchemaMap && path.startsWith(cleanPath + '.')) { + let remnant = path.slice(cleanPath.length + 1); + remnant = remnant.slice(remnant.indexOf('.') + 1); + const presplitPath = val.$__schemaType._presplitPath; + if (remnant.indexOf('.') === -1 && presplitPath[presplitPath.length - 1] === '$*') { + // Handle map of map of primitives + return val.$__schemaType; + } else if (remnant.indexOf('.') !== -1 && val.$__schemaType.schema && presplitPath[presplitPath.length - 1] === '$*') { + // map of map of subdocs (recursive) + return val.$__schemaType.schema.path(remnant.slice(remnant.indexOf('.') + 1)); + } + } + } + + return null; +} + +/** + * The Mongoose instance this schema is associated with + * + * @property base + * @api private + */ + +Object.defineProperty(Schema.prototype, 'base', { + configurable: true, + enumerable: false, + writable: true, + value: null +}); + +/** + * Converts type arguments into Mongoose Types. + * + * @param {string} path + * @param {object} obj constructor + * @param {object} options schema options + * @api private + */ + +Schema.prototype.interpretAsType = function(path, obj, options) { + if (obj instanceof SchemaType) { + if (obj.path === path) { + return obj; + } + const clone = obj.clone(); + clone.path = path; + return clone; + } + + // If this schema has an associated Mongoose object, use the Mongoose object's + // copy of SchemaTypes re: gh-7158 gh-6933 + const MongooseTypes = this.base?.Schema.Types ?? Schema.Types; + const Types = this.base?.Types ?? require('./types'); + + if (!utils.isPOJO(obj) && !(obj instanceof SchemaTypeOptions)) { + const constructorName = utils.getFunctionName(obj.constructor); + if (constructorName !== 'Object') { + const oldObj = obj; + obj = {}; + obj[options.typeKey] = oldObj; + } + } + + // Get the type making sure to allow keys named "type" + // and default to mixed if not specified. + // { type: { type: String, default: 'freshcut' } } + let type = obj[options.typeKey] && (obj[options.typeKey] instanceof Function || options.typeKey !== 'type' || !obj.type.type) + ? obj[options.typeKey] + : {}; + + if (type instanceof SchemaType) { + if (type.path === path) { + return type; + } + const clone = type.clone(); + clone.path = path; + return clone; + } + + let name; + + if (utils.isPOJO(type) || type === 'mixed') { + return new MongooseTypes.Mixed(path, obj, null, this); + } + + if (Array.isArray(type) || type === Array || type === 'array' || type === MongooseTypes.Array) { + // if it was specified through { type } look for `cast` + let cast = (type === Array || type === 'array') + ? obj.cast || obj.of + : type[0]; + + // new Schema({ path: [new Schema({ ... })] }) + if (cast?.instanceOfSchema) { + if (!(cast instanceof Schema)) { + if (this.options._isMerging) { + cast = new Schema(cast); + } else { + throw new TypeError('Schema for array path `' + path + + '` is from a different copy of the Mongoose module. ' + + 'Please make sure you\'re using the same version ' + + 'of Mongoose everywhere with `npm list mongoose`. If you are still ' + + 'getting this error, please add `new Schema()` around the path: ' + + `${path}: new Schema(...)`); + } + } + return new MongooseTypes.DocumentArray(path, cast, obj, null, this); + } + if (cast && + cast[options.typeKey] && + cast[options.typeKey].instanceOfSchema) { + if (!(cast[options.typeKey] instanceof Schema)) { + if (this.options._isMerging) { + cast[options.typeKey] = new Schema(cast[options.typeKey]); + } else { + throw new TypeError('Schema for array path `' + path + + '` is from a different copy of the Mongoose module. ' + + 'Please make sure you\'re using the same version ' + + 'of Mongoose everywhere with `npm list mongoose`. If you are still ' + + 'getting this error, please add `new Schema()` around the path: ' + + `${path}: new Schema(...)`); + } + } + return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast, this); + } + if (typeof cast !== 'undefined') { + if (Array.isArray(cast) || cast.type === Array || cast.type == 'Array') { + if (cast?.type == 'Array') { + cast.type = Array; + } + return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj, null, this); + } + } + + // Handle both `new Schema({ arr: [{ subpath: String }] })` and `new Schema({ arr: [{ type: { subpath: string } }] })` + const castFromTypeKey = (cast != null && cast[options.typeKey] && (options.typeKey !== 'type' || !cast.type.type)) ? + cast[options.typeKey] : + cast; + if (typeof cast === 'string') { + cast = MongooseTypes[cast.charAt(0).toUpperCase() + cast.substring(1)]; + } else if (utils.isPOJO(castFromTypeKey)) { + if (utils.hasOwnKeys(castFromTypeKey)) { + // The `minimize` and `typeKey` options propagate to child schemas + // declared inline, like `{ arr: [{ val: { $type: String } }] }`. + // See gh-3560 + const childSchemaOptions = { minimize: options.minimize }; + if (options.typeKey) { + childSchemaOptions.typeKey = options.typeKey; + } + // propagate 'strict' option to child schema + if (Object.hasOwn(options, 'strict')) { + childSchemaOptions.strict = options.strict; + } + if (Object.hasOwn(options, 'strictQuery')) { + childSchemaOptions.strictQuery = options.strictQuery; + } + if (Object.hasOwn(options, 'toObject')) { + childSchemaOptions.toObject = utils.omit(options.toObject, ['transform']); + } + if (Object.hasOwn(options, 'toJSON')) { + childSchemaOptions.toJSON = utils.omit(options.toJSON, ['transform']); + } + + if (Object.hasOwn(this._userProvidedOptions, '_id')) { + childSchemaOptions._id = this._userProvidedOptions._id; + } else if (Schema.Types.DocumentArray.defaultOptions._id != null) { + childSchemaOptions._id = Schema.Types.DocumentArray.defaultOptions._id; + } + + const childSchema = new Schema(castFromTypeKey, childSchemaOptions); + childSchema.$implicitlyCreated = true; + return new MongooseTypes.DocumentArray(path, childSchema, obj, null, this); + } else { + // Special case: empty object becomes mixed + return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj, null, this); + } + } + + if (cast) { + type = cast[options.typeKey] && (options.typeKey !== 'type' || !cast.type.type) + ? cast[options.typeKey] + : cast; + if (Array.isArray(type)) { + return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj, null, this); + } + + name = typeof type === 'string' + ? type + : type.schemaName || utils.getFunctionName(type); + + // For Jest 26+, see #10296 + if (name === 'ClockDate') { + name = 'Date'; + } + + if (name === void 0) { + throw new TypeError('Invalid schema configuration: ' + + `Could not determine the embedded type for array \`${path}\`. ` + + 'See https://mongoosejs.com/docs/guide.html#definition for more info on supported schema syntaxes.'); + } + if (!Object.hasOwn(MongooseTypes, name)) { + throw new TypeError('Invalid schema configuration: ' + + `\`${name}\` is not a valid type within the array \`${path}\`.` + + 'See https://bit.ly/mongoose-schematypes for a list of valid schema types.'); + } + + if (name === 'Union' && typeof cast === 'object') { + cast.parentSchema = this; + } + } + + return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options, this); + } + + if (type?.instanceOfSchema) { + return new MongooseTypes.Subdocument(type, path, obj, this); + } + + if (Buffer.isBuffer(type)) { + name = 'Buffer'; + } else if (typeof type === 'function' || typeof type === 'object') { + name = type.schemaName || utils.getFunctionName(type); + } else if (type === Types.ObjectId) { + name = 'ObjectId'; + } else if (type === Types.Decimal128) { + name = 'Decimal128'; + } else { + name = type == null ? '' + type : type.toString(); + } + + if (name) { + name = name.charAt(0).toUpperCase() + name.substring(1); + } + // Special case re: gh-7049 because the bson `ObjectID` class' capitalization + // doesn't line up with Mongoose's. + if (name === 'ObjectID') { + name = 'ObjectId'; + } + // For Jest 26+, see #10296 + if (name === 'ClockDate') { + name = 'Date'; + } + + if (name === void 0) { + throw new TypeError(`Invalid schema configuration: \`${path}\` schematype definition is ` + + 'invalid. See ' + + 'https://mongoosejs.com/docs/guide.html#definition for more info on supported schema syntaxes.'); + } + if (MongooseTypes[name] == null) { + throw new TypeError(`Invalid schema configuration: \`${name}\` is not ` + + `a valid type at path \`${path}\`. See ` + + 'https://bit.ly/mongoose-schematypes for a list of valid schema types.'); + } + + const schemaType = new MongooseTypes[name](path, obj, options, this); + + return schemaType; +}; + +/** + * Iterates the schemas paths similar to Array#forEach. + * + * The callback is passed the pathname and the schemaType instance. + * + * #### Example: + * + * const userSchema = new Schema({ name: String, registeredAt: Date }); + * userSchema.eachPath((pathname, schematype) => { + * // Prints twice: + * // name SchemaString { ... } + * // registeredAt SchemaDate { ... } + * console.log(pathname, schematype); + * }); + * + * @param {Function} fn callback function + * @return {Schema} this + * @api public + */ + +Schema.prototype.eachPath = function(fn) { + const keys = Object.keys(this.paths); + const len = keys.length; + + for (let i = 0; i < len; ++i) { + fn(keys[i], this.paths[keys[i]]); + } + + return this; +}; + +/** + * Returns an Array of path strings that are required by this schema. + * + * #### Example: + * + * const s = new Schema({ + * name: { type: String, required: true }, + * age: { type: String, required: true }, + * notes: String + * }); + * s.requiredPaths(); // [ 'age', 'name' ] + * + * @api public + * @param {boolean} invalidate Refresh the cache + * @return {Array} + */ + +Schema.prototype.requiredPaths = function requiredPaths(invalidate) { + if (this._requiredpaths && !invalidate) { + return this._requiredpaths; + } + + const paths = Object.keys(this.paths); + let i = paths.length; + const ret = []; + + while (i--) { + const path = paths[i]; + if (this.paths[path].isRequired) { + ret.push(path); + } + } + this._requiredpaths = ret; + return this._requiredpaths; +}; + +/** + * Returns indexes from fields and schema-level indexes (cached). + * + * @api private + * @return {Array} + */ + +Schema.prototype.indexedPaths = function indexedPaths() { + if (this._indexedpaths) { + return this._indexedpaths; + } + this._indexedpaths = this.indexes(); + return this._indexedpaths; +}; + +/** + * Returns the pathType of `path` for this schema. + * + * Given a path, returns whether it is a real, virtual, nested, or ad-hoc/undefined path. + * + * #### Example: + * + * const s = new Schema({ name: String, nested: { foo: String } }); + * s.virtual('foo').get(() => 42); + * s.pathType('name'); // "real" + * s.pathType('nested'); // "nested" + * s.pathType('foo'); // "virtual" + * s.pathType('fail'); // "adhocOrUndefined" + * + * @param {string} path + * @return {string} + * @api public + */ + +Schema.prototype.pathType = function(path) { + if (Object.hasOwn(this.paths, path)) { + return 'real'; + } + if (Object.hasOwn(this.virtuals, path)) { + return 'virtual'; + } + if (Object.hasOwn(this.nested, path)) { + return 'nested'; + } + + // Convert to '.$' to check subpaths re: gh-6405 + const cleanPath = _pathToPositionalSyntax(path); + + if (Object.hasOwn(this.subpaths, cleanPath) || Object.hasOwn(this.subpaths, path)) { + return 'real'; + } + + const singleNestedPath = Object.hasOwn(this.singleNestedPaths, cleanPath) || Object.hasOwn(this.singleNestedPaths, path); + if (singleNestedPath) { + return singleNestedPath === 'nested' ? 'nested' : 'real'; + } + + // Look for maps + const mapPath = getMapPath(this, path); + if (mapPath != null) { + return 'real'; + } + + if (/\.\d+\.|\.\d+$/.test(path)) { + return getPositionalPathType(this, path, cleanPath); + } + return 'adhocOrUndefined'; +}; + +/** + * Returns true iff this path is a child of a mixed schema. + * + * @param {string} path + * @return {boolean} + * @api private + */ + +Schema.prototype.hasMixedParent = function(path) { + const subpaths = path.split(/\./g); + path = ''; + for (let i = 0; i < subpaths.length; ++i) { + path = i > 0 ? path + '.' + subpaths[i] : subpaths[i]; + if (Object.hasOwn(this.paths, path) && + this.paths[path] instanceof MongooseTypes.Mixed) { + return this.paths[path]; + } + } + + return null; +}; + +/** + * Setup updatedAt and createdAt timestamps to documents if enabled + * + * @param {boolean|object} timestamps timestamps options + * @api private + */ +Schema.prototype.setupTimestamp = function(timestamps) { + return setupTimestamps(this, timestamps); +}; + +/** + * ignore. Deprecated re: #6405 + * @param {any} self + * @param {string} path + * @api private + */ + +function getPositionalPathType(self, path, cleanPath) { + const subpaths = path.split(/\.(\d+)\.|\.(\d+)$/).filter(Boolean); + if (subpaths.length < 2) { + return Object.hasOwn(self.paths, subpaths[0]) ? + self.paths[subpaths[0]] : + 'adhocOrUndefined'; + } + + let val = self.path(subpaths[0]); + let isNested = false; + if (!val) { + return 'adhocOrUndefined'; + } + + const last = subpaths.length - 1; + + for (let i = 1; i < subpaths.length; ++i) { + isNested = false; + const subpath = subpaths[i]; + + if (i === last && val && !/\D/.test(subpath)) { + if (val.$isMongooseDocumentArray) { + val = val.embeddedSchemaType; + } else if (val instanceof MongooseTypes.Array) { + // StringSchema, NumberSchema, etc + val = val.embeddedSchemaType; + } else { + val = undefined; + } + break; + } + + // ignore if its just a position segment: path.0.subpath + if (!/\D/.test(subpath)) { + // Nested array + if (val instanceof MongooseTypes.Array && i !== last) { + val = val.embeddedSchemaType; + } + continue; + } + + if (!val?.schema) { + val = undefined; + break; + } + + const type = val.schema.pathType(subpath); + isNested = (type === 'nested'); + val = val.schema.path(subpath); + } + + self.subpaths[cleanPath] = val; + if (val) { + return 'real'; + } + if (isNested) { + return 'nested'; + } + return 'adhocOrUndefined'; +} + + +/*! + * ignore + */ + +function getPositionalPath(self, path, cleanPath) { + getPositionalPathType(self, path, cleanPath); + return self.subpaths[cleanPath]; +} + +/** + * Adds a method call to the queue. + * + * #### Example: + * + * schema.methods.print = function() { console.log(this); }; + * schema.queue('print', []); // Print the doc every one is instantiated + * + * const Model = mongoose.model('Test', schema); + * new Model({ name: 'test' }); // Prints '{"_id": ..., "name": "test" }' + * + * @param {string} name name of the document method to call later + * @param {Array} args arguments to pass to the method + * @api public + */ + +Schema.prototype.queue = function(name, args) { + this.callQueue.push([name, args]); + return this; +}; + +/** + * Defines a pre hook for the model. + * + * #### Example: + * + * const toySchema = new Schema({ name: String, created: Date }); + * + * toySchema.pre('save', function() { + * if (!this.created) this.created = new Date; + * }); + * + * toySchema.pre('validate', function() { + * if (this.name !== 'Woody') this.name = 'Woody'; + * }); + * + * // Equivalent to calling `pre()` on `find`, `findOne`, `findOneAndUpdate`. + * toySchema.pre(/^find/, function() { + * console.log(this.getFilter()); + * }); + * + * // Equivalent to calling `pre()` on `updateOne`, `findOneAndUpdate`. + * toySchema.pre(['updateOne', 'findOneAndUpdate'], function() { + * console.log(this.getFilter()); + * }); + * + * toySchema.pre('deleteOne', function() { + * // Runs when you call `Toy.deleteOne()` + * }); + * + * toySchema.pre('deleteOne', { document: true }, function() { + * // Runs when you call `doc.deleteOne()` + * }); + * + * @param {string|RegExp|string[]} methodName The method name or regular expression to match method name + * @param {object} [options] + * @param {boolean} [options.document] If `name` is a hook for both document and query middleware, set to `true` to run on document middleware. For example, set `options.document` to `true` to apply this hook to `Document#deleteOne()` rather than `Query#deleteOne()`. + * @param {boolean} [options.query] If `name` is a hook for both document and query middleware, set to `true` to run on query middleware. + * @param {Function} callback + * @api public + */ + +Schema.prototype.pre = function(name) { + if (name instanceof RegExp) { + const remainingArgs = Array.prototype.slice.call(arguments, 1); + for (const fn of hookNames) { + if (name.test(fn)) { + this.pre.apply(this, [fn].concat(remainingArgs)); + } + } + return this; + } + if (Array.isArray(name)) { + const remainingArgs = Array.prototype.slice.call(arguments, 1); + for (const el of name) { + this.pre.apply(this, [el].concat(remainingArgs)); + } + return this; + } + this.s.hooks.pre.apply(this.s.hooks, arguments); + return this; +}; + +/** + * Defines a post hook for the document + * + * const schema = new Schema(..); + * schema.post('save', function (doc) { + * console.log('this fired after a document was saved'); + * }); + * + * schema.post('find', function(docs) { + * console.log('this fired after you ran a find query'); + * }); + * + * schema.post(/Many$/, function(res) { + * console.log('this fired after you ran `updateMany()` or `deleteMany()`'); + * }); + * + * const Model = mongoose.model('Model', schema); + * + * const m = new Model(..); + * await m.save(); + * console.log('this fires after the `post` hook'); + * + * await m.find(); + * console.log('this fires after the post find hook'); + * + * @param {string|RegExp|string[]} methodName The method name or regular expression to match method name + * @param {object} [options] + * @param {boolean} [options.document] If `name` is a hook for both document and query middleware, set to `true` to run on document middleware. + * @param {boolean} [options.query] If `name` is a hook for both document and query middleware, set to `true` to run on query middleware. + * @param {Function} fn callback + * @see middleware https://mongoosejs.com/docs/middleware.html + * @see kareem https://npmjs.org/package/kareem + * @api public + */ + +Schema.prototype.post = function(name) { + if (name instanceof RegExp) { + const remainingArgs = Array.prototype.slice.call(arguments, 1); + for (const fn of hookNames) { + if (name.test(fn)) { + this.post.apply(this, [fn].concat(remainingArgs)); + } + } + return this; + } + if (Array.isArray(name)) { + const remainingArgs = Array.prototype.slice.call(arguments, 1); + for (const el of name) { + this.post.apply(this, [el].concat(remainingArgs)); + } + return this; + } + this.s.hooks.post.apply(this.s.hooks, arguments); + return this; +}; + +/** + * Registers a plugin for this schema. + * + * #### Example: + * + * const s = new Schema({ name: String }); + * s.plugin(schema => console.log(schema.path('name').path)); + * mongoose.model('Test', s); // Prints 'name' + * + * Or with Options: + * + * const s = new Schema({ name: String }); + * s.plugin((schema, opts) => console.log(opts.text, schema.path('name').path), { text: "Schema Path Name:" }); + * mongoose.model('Test', s); // Prints 'Schema Path Name: name' + * + * @param {Function} plugin The Plugin's callback + * @param {object} [opts] Options to pass to the plugin + * @param {boolean} [opts.deduplicate=false] If true, ignore duplicate plugins (same `fn` argument using `===`) + * @see plugins https://mongoosejs.com/docs/plugins.html + * @api public + */ + +Schema.prototype.plugin = function(fn, opts) { + if (typeof fn !== 'function') { + throw new MongooseError('First param to `schema.plugin()` must be a function, ' + + 'got "' + (typeof fn) + '"'); + } + + + if (opts?.deduplicate) { + for (const plugin of this.plugins) { + if (plugin.fn === fn) { + return this; + } + } + } + this.plugins.push({ fn: fn, opts: opts }); + + fn(this, opts); + return this; +}; + +/** + * Adds an instance method to documents constructed from Models compiled from this schema. + * + * #### Example: + * + * const schema = kittySchema = new Schema(..); + * + * schema.method('meow', function () { + * console.log('meeeeeoooooooooooow'); + * }) + * + * const Kitty = mongoose.model('Kitty', schema); + * + * const fizz = new Kitty; + * fizz.meow(); // meeeeeooooooooooooow + * + * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods. + * + * schema.method({ + * purr: function () {} + * , scratch: function () {} + * }); + * + * // later + * const fizz = new Kitty; + * fizz.purr(); + * fizz.scratch(); + * + * NOTE: `Schema.method()` adds instance methods to the `Schema.methods` object. You can also add instance methods directly to the `Schema.methods` object as seen in the [guide](https://mongoosejs.com/docs/guide.html#methods) + * + * @param {string|object} name The Method Name for a single function, or an Object of "string-function" pairs. + * @param {Function} [fn] The Function in a single-function definition. + * @api public + */ + +Schema.prototype.method = function(name, fn, options) { + if (typeof name !== 'string') { + for (const i in name) { + this.methods[i] = name[i]; + this.methodOptions[i] = clone(options); + } + } else { + this.methods[name] = fn; + this.methodOptions[name] = clone(options); + } + return this; +}; + +/** + * Adds static "class" methods to Models compiled from this schema. + * + * #### Example: + * + * const schema = new Schema(..); + * // Equivalent to `schema.statics.findByName = function(name) {}`; + * schema.static('findByName', function(name) { + * return this.find({ name: name }); + * }); + * + * const Drink = mongoose.model('Drink', schema); + * await Drink.findByName('LaCroix'); + * + * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as methods. + * + * schema.static({ + * findByName: function () {..} + * , findByCost: function () {..} + * }); + * + * const Drink = mongoose.model('Drink', schema); + * await Drink.findByName('LaCroix'); + * await Drink.findByCost(3); + * + * If a hash of name/fn pairs is passed as the only argument, each name/fn pair will be added as statics. + * + * @param {string|object} name The Method Name for a single function, or an Object of "string-function" pairs. + * @param {Function} [fn] The Function in a single-function definition. + * @api public + * @see Statics https://mongoosejs.com/docs/guide.html#statics + */ + +Schema.prototype.static = function(name, fn) { + if (typeof name !== 'string') { + for (const i in name) { + this.statics[i] = name[i]; + } + } else { + this.statics[name] = fn; + } + return this; +}; + +/** + * Defines an index (most likely compound) for this schema. + * + * #### Example: + * + * schema.index({ first: 1, last: -1 }) + * + * @param {object} fields The Fields to index, with the order, available values: `1 | -1 | '2d' | '2dsphere' | 'geoHaystack' | 'hashed' | 'text'` + * @param {object} [options] Options to pass to [MongoDB driver's `createIndex()` function](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#createIndex) + * @param {string | number} [options.expires=null] Mongoose-specific syntactic sugar, uses [ms](https://www.npmjs.com/package/ms) to convert `expires` option into seconds for the `expireAfterSeconds` in the above link. + * @param {string} [options.language_override=null] Tells mongodb to use the specified field instead of `language` for parsing text indexes. + * @api public + */ + +Schema.prototype.index = function(fields, options) { + fields || (fields = {}); + options || (options = {}); + + if (options.expires) { + utils.expires(options); + } + for (const key in fields) { + if (this.aliases[key]) { + fields = utils.renameObjKey(fields, key, this.aliases[key]); + } + } + for (const field of Object.keys(fields)) { + if (fields[field] === 'ascending' || fields[field] === 'asc') { + fields[field] = 1; + } else if (fields[field] === 'descending' || fields[field] === 'desc') { + fields[field] = -1; + } + } + + this._indexes.push([fields, options]); + return this; +}; + +/** + * Sets a schema option. + * + * #### Example: + * + * schema.set('strict'); // 'true' by default + * schema.set('strict', false); // Sets 'strict' to false + * schema.set('strict'); // 'false' + * + * @param {string} key The name of the option to set the value to + * @param {object} [value] The value to set the option to, if not passed, the option will be reset to default + * @param {string[]} [tags] tags to add to read preference if key === 'read' + * @see Schema https://mongoosejs.com/docs/api/schema.html#Schema() + * @api public + */ + +Schema.prototype.set = function(key, value, tags) { + if (arguments.length === 1) { + return this.options[key]; + } + + switch (key) { + case 'read': + if (typeof value === 'string') { + this.options[key] = { mode: handleReadPreferenceAliases(value), tags }; + } else if (Array.isArray(value) && typeof value[0] === 'string') { + this.options[key] = { + mode: handleReadPreferenceAliases(value[0]), + tags: value[1] + }; + } else { + this.options[key] = value; + } + this._userProvidedOptions[key] = this.options[key]; + break; + case 'timestamps': + this.setupTimestamp(value); + this.options[key] = value; + this._userProvidedOptions[key] = this.options[key]; + break; + case '_id': + this.options[key] = value; + this._userProvidedOptions[key] = this.options[key]; + + if (value && !this.paths['_id']) { + addAutoId(this); + } else if (!value && this.paths['_id'] != null && this.paths['_id'].auto) { + this.remove('_id'); + } + break; + default: + this.options[key] = value; + this._userProvidedOptions[key] = this.options[key]; + break; + } + + // Propagate `strict` and `strictQuery` changes down to implicitly created schemas + if (key === 'strict') { + _propagateOptionsToImplicitlyCreatedSchemas(this, { strict: value }); + } + if (key === 'strictQuery') { + _propagateOptionsToImplicitlyCreatedSchemas(this, { strictQuery: value }); + } + if (key === 'toObject') { + value = { ...value }; + // Avoid propagating transform to implicitly created schemas re: gh-3279 + delete value.transform; + _propagateOptionsToImplicitlyCreatedSchemas(this, { toObject: value }); + } + if (key === 'toJSON') { + value = { ...value }; + // Avoid propagating transform to implicitly created schemas re: gh-3279 + delete value.transform; + _propagateOptionsToImplicitlyCreatedSchemas(this, { toJSON: value }); + } + + return this; +}; + +/*! + * Recursively set options on implicitly created schemas + */ + +function _propagateOptionsToImplicitlyCreatedSchemas(baseSchema, options) { + for (const { schema } of baseSchema.childSchemas) { + if (!schema.$implicitlyCreated) { + continue; + } + Object.assign(schema.options, options); + _propagateOptionsToImplicitlyCreatedSchemas(schema, options); + } +} + +/** + * Gets a schema option. + * + * #### Example: + * + * schema.get('strict'); // true + * schema.set('strict', false); + * schema.get('strict'); // false + * + * @param {string} key The name of the Option to get the current value for + * @api public + * @return {any} the option's value + */ + +Schema.prototype.get = function(key) { + return this.options[key]; +}; + +const indexTypes = '2d 2dsphere hashed text'.split(' '); + +/** + * The allowed index types + * + * @property {string[]} indexTypes + * @memberOf Schema + * @static + * @api public + */ + +Object.defineProperty(Schema, 'indexTypes', { + get: function() { + return indexTypes; + }, + set: function() { + throw new MongooseError('Cannot overwrite Schema.indexTypes'); + } +}); + +/** + * Returns a list of indexes that this schema declares, via `schema.index()` or by `index: true` in a path's options. + * Indexes are expressed as an array `[spec, options]`. + * + * #### Example: + * + * const userSchema = new Schema({ + * email: { type: String, required: true, unique: true }, + * registeredAt: { type: Date, index: true } + * }); + * + * // [ [ { email: 1 }, { unique: true } ], + * // [ { registeredAt: 1 }, {} ] ] + * userSchema.indexes(); + * + * [Plugins](https://mongoosejs.com/docs/plugins.html) can use the return value of this function to modify a schema's indexes. + * For example, the below plugin makes every index unique by default. + * + * function myPlugin(schema) { + * for (const index of schema.indexes()) { + * if (index[1].unique === undefined) { + * index[1].unique = true; + * } + * } + * } + * + * @api public + * @return {Array} list of indexes defined in the schema + */ + +Schema.prototype.indexes = function() { + return getIndexes(this); +}; + +/** + * Creates a virtual type with the given name. + * + * @param {string} name The name of the Virtual + * @param {object} [options] + * @param {string|Model} [options.ref] model name or model instance. Marks this as a [populate virtual](https://mongoosejs.com/docs/populate.html#populate-virtuals). + * @param {string|Function} [options.localField] Required for populate virtuals. See [populate virtual docs](https://mongoosejs.com/docs/populate.html#populate-virtuals) for more information. + * @param {string|Function} [options.foreignField] Required for populate virtuals. See [populate virtual docs](https://mongoosejs.com/docs/populate.html#populate-virtuals) for more information. + * @param {boolean|Function} [options.justOne=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), will be a single doc or `null`. Otherwise, the populate virtual will be an array. + * @param {boolean} [options.count=false] Only works with populate virtuals. If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), this populate virtual will contain the number of documents rather than the documents themselves when you `populate()`. + * @param {Function|null} [options.get=null] Adds a [getter](https://mongoosejs.com/docs/tutorials/getters-setters.html) to this virtual to transform the populated doc. + * @param {object|Function} [options.match=null] Apply a default [`match` option to populate](https://mongoosejs.com/docs/populate.html#match), adding an additional filter to the populate query. + * @param {boolean} [options.applyToArray=false] If true and the given `name` is a direct child of an array, apply the virtual to the array rather than the elements. + * @return {VirtualType} + */ + +Schema.prototype.virtual = function(name, options) { + if (name instanceof VirtualType || getConstructorName(name) === 'VirtualType') { + return this.virtual(name.path, name.options); + } + options = new VirtualOptions(options); + + if (utils.hasUserDefinedProperty(options, ['ref', 'refPath'])) { + if (options.localField == null) { + throw new MongooseError('Reference virtuals require `localField` option'); + } + + if (options.foreignField == null) { + throw new MongooseError('Reference virtuals require `foreignField` option'); + } + + const virtual = this.virtual(name); + virtual.options = options; + + this.pre('init', function virtualPreInit(obj, opts) { + if (mpath.has(name, obj)) { + const _v = mpath.get(name, obj); + if (!this.$$populatedVirtuals) { + this.$$populatedVirtuals = {}; + } + + if (options.justOne || options.count) { + this.$$populatedVirtuals[name] = Array.isArray(_v) ? + _v[0] : + _v; + } else { + this.$$populatedVirtuals[name] = Array.isArray(_v) ? + _v : + _v == null ? [] : [_v]; + } + + if (opts?.hydratedPopulatedDocs && !options.count) { + const modelNames = virtual._getModelNamesForPopulate(this); + const populatedVal = this.$$populatedVirtuals[name]; + if (!Array.isArray(populatedVal) && !populatedVal.$__ && modelNames?.length === 1) { + const PopulateModel = this.db.model(modelNames[0]); + this.$$populatedVirtuals[name] = PopulateModel.hydrate(populatedVal); + } else if (Array.isArray(populatedVal) && modelNames?.length === 1) { + const PopulateModel = this.db.model(modelNames[0]); + for (let i = 0; i < populatedVal.length; ++i) { + if (!populatedVal[i].$__) { + populatedVal[i] = PopulateModel.hydrate(populatedVal[i], null, { hydratedPopulatedDocs: true }); + } + } + const foreignField = options.foreignField; + this.$populated( + name, + populatedVal.map(doc => doc == null ? doc : doc.get(typeof foreignField === 'function' ? foreignField.call(doc, doc) : foreignField)), + { populateModelSymbol: PopulateModel } + ); + } + } + + mpath.unset(name, obj); + } + }); + + virtual. + set(function(v) { + if (!this.$$populatedVirtuals) { + this.$$populatedVirtuals = {}; + } + + return setPopulatedVirtualValue( + this.$$populatedVirtuals, + name, + v, + options + ); + }); + + if (typeof options.get === 'function') { + virtual.get(options.get); + } + + // Workaround for gh-8198: if virtual is under document array, make a fake + // virtual. See gh-8210, gh-13189 + const parts = name.split('.'); + let cur = parts[0]; + for (let i = 0; i < parts.length - 1; ++i) { + if (this.paths[cur] == null) { + continue; + } + + if (this.paths[cur].$isMongooseDocumentArray || this.paths[cur].$isSingleNested) { + const remnant = parts.slice(i + 1).join('.'); + this.paths[cur].schema.virtual(remnant, options); + break; + } else if (this.paths[cur].$isSchemaMap) { + const remnant = parts.slice(i + 2).join('.'); + this.paths[cur].$__schemaType.schema.virtual(remnant, options); + break; + } + + cur += '.' + parts[i + 1]; + } + + return virtual; + } + + const virtuals = this.virtuals; + const parts = name.split('.'); + + if (this.pathType(name) === 'real') { + throw new MongooseError('Virtual path "' + name + '"' + + ' conflicts with a real path in the schema'); + } + + virtuals[name] = parts.reduce(function(mem, part, i) { + mem[part] || (mem[part] = (i === parts.length - 1) + ? new VirtualType(options, name) + : {}); + return mem[part]; + }, this.tree); + + if (options?.applyToArray && parts.length > 1) { + const path = this.path(parts.slice(0, -1).join('.')); + if (path?.$isMongooseArray) { + return path.virtual(parts[parts.length - 1], options); + } else { + throw new MongooseError(`Path "${path}" is not an array`); + } + } + + return virtuals[name]; +}; + +/** + * Returns the virtual type with the given `name`. + * + * @param {string} name The name of the Virtual to get + * @return {VirtualType|null} + */ + +Schema.prototype.virtualpath = function(name) { + return Object.hasOwn(this.virtuals, name) ? this.virtuals[name] : null; +}; + +/** + * Removes the given `path` (or [`paths`]). + * + * #### Example: + * + * const schema = new Schema({ name: String, age: Number }); + * schema.remove('name'); + * schema.path('name'); // Undefined + * schema.path('age'); // SchemaNumber { ... } + * + * Or as a Array: + * + * schema.remove(['name', 'age']); + * schema.path('name'); // Undefined + * schema.path('age'); // Undefined + * + * @param {string|Array} path The Path(s) to remove + * @return {Schema} the Schema instance + * @api public + */ +Schema.prototype.remove = function(path) { + if (typeof path === 'string') { + path = [path]; + } + if (Array.isArray(path)) { + path.forEach(function(name) { + if (this.path(name) == null && !this.nested[name]) { + return; + } + if (this.nested[name]) { + const allKeys = Object.keys(this.paths). + concat(Object.keys(this.nested)); + for (const path of allKeys) { + if (path.startsWith(name + '.')) { + delete this.paths[path]; + delete this.nested[path]; + _deletePath(this, path); + } + } + + delete this.nested[name]; + _deletePath(this, name); + return; + } + + delete this.paths[name]; + _deletePath(this, name); + + this._removeEncryptedField(name); + }, this); + } + return this; +}; + +/*! + * ignore + */ + +function _deletePath(schema, name) { + const pieces = name.split('.'); + const last = pieces.pop(); + + let branch = schema.tree; + + for (const piece of pieces) { + branch = branch[piece]; + } + + delete branch[last]; +} + +/** + * Removes the given virtual or virtuals from the schema. + * + * @param {string|Array} path The virutal path(s) to remove. + * @returns {Schema} the Schema instance, or a mongoose error if the virtual does not exist. + * @api public + */ + +Schema.prototype.removeVirtual = function(path) { + if (typeof path === 'string') { + path = [path]; + } + if (Array.isArray(path)) { + for (const virtual of path) { + if (this.virtuals[virtual] == null) { + throw new MongooseError(`Attempting to remove virtual "${virtual}" that does not exist.`); + } + } + + for (const virtual of path) { + delete this.paths[virtual]; + delete this.virtuals[virtual]; + if (virtual.indexOf('.') !== -1) { + mpath.unset(virtual, this.tree); + } else { + delete this.tree[virtual]; + } + } + } + return this; +}; + +/** + * Loads an ES6 class into a schema. Maps [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) + [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), [static methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static), + * and [instance methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Class_body_and_method_definitions) + * to schema [virtuals](https://mongoosejs.com/docs/guide.html#virtuals), + * [statics](https://mongoosejs.com/docs/guide.html#statics), and + * [methods](https://mongoosejs.com/docs/guide.html#methods). + * + * #### Example: + * + * ```javascript + * const md5 = require('md5'); + * const userSchema = new Schema({ email: String }); + * class UserClass { + * // `gravatarImage` becomes a virtual + * get gravatarImage() { + * const hash = md5(this.email.toLowerCase()); + * return `https://www.gravatar.com/avatar/${hash}`; + * } + * + * // `getProfileUrl()` becomes a document method + * getProfileUrl() { + * return `https://mysite.com/${this.email}`; + * } + * + * // `findByEmail()` becomes a static + * static findByEmail(email) { + * return this.findOne({ email }); + * } + * } + * + * // `schema` will now have a `gravatarImage` virtual, a `getProfileUrl()` method, + * // and a `findByEmail()` static + * userSchema.loadClass(UserClass); + * ``` + * + * @param {Function} model The Class to load + * @param {boolean} [virtualsOnly] if truthy, only pulls virtuals from the class, not methods or statics + */ +Schema.prototype.loadClass = function(model, virtualsOnly) { + // Stop copying when hit certain base classes + if (model === Object.prototype || + model === Function.prototype || + Object.hasOwn(model.prototype, '$isMongooseModelPrototype') || + Object.hasOwn(model.prototype, '$isMongooseDocumentPrototype')) { + return this; + } + + this.loadClass(Object.getPrototypeOf(model), virtualsOnly); + + // Add static methods + if (!virtualsOnly) { + Object.getOwnPropertyNames(model).forEach(function(name) { + if (name.match(/^(length|name|prototype|constructor|__proto__)$/)) { + return; + } + const prop = Object.getOwnPropertyDescriptor(model, name); + if (Object.hasOwn(prop, 'value')) { + this.static(name, prop.value); + } + }, this); + } + + // Add methods and virtuals + Object.getOwnPropertyNames(model.prototype).forEach(function(name) { + if (name.match(/^(constructor)$/)) { + return; + } + const method = Object.getOwnPropertyDescriptor(model.prototype, name); + if (!virtualsOnly) { + if (typeof method.value === 'function') { + this.method(name, method.value); + } + } + if (typeof method.get === 'function') { + if (this.virtuals[name]) { + this.virtuals[name].getters = []; + } + this.virtual(name).get(method.get); + } + if (typeof method.set === 'function') { + if (this.virtuals[name]) { + this.virtuals[name].setters = []; + } + this.virtual(name).set(method.set); + } + }, this); + + return this; +}; + +/*! + * ignore + */ + +Schema.prototype._getSchema = function(path) { + const _this = this; + const pathschema = _this.path(path); + const resultPath = []; + + if (pathschema) { + pathschema.$fullPath = path; + return pathschema; + } + + function search(parts, schema) { + let p = parts.length + 1; + let foundschema; + let trypath; + + while (p--) { + trypath = parts.slice(0, p).join('.'); + foundschema = schema.path(trypath); + if (foundschema) { + resultPath.push(trypath); + + if (foundschema.embeddedSchemaType || foundschema.Constructor) { + // array of Mixed? + if (foundschema.embeddedSchemaType instanceof MongooseTypes.Mixed) { + foundschema.embeddedSchemaType.$fullPath = resultPath.join('.'); + return foundschema.embeddedSchemaType; + } + + // Now that we found the array, we need to check if there + // are remaining document paths to look up for casting. + // Also we need to handle array.$.path since schema.path + // doesn't work for that. + // If there is no foundschema.schema we are dealing with + // a path like array.$ + if (p !== parts.length) { + if (p + 1 === parts.length && foundschema.embeddedSchemaType && (parts[p] === '$' || isArrayFilter(parts[p]))) { + return foundschema.embeddedSchemaType; + } + + if (foundschema.schema) { + let ret; + if (parts[p] === '$' || isArrayFilter(parts[p])) { + if (p + 1 === parts.length) { + // comments.$ + return foundschema.embeddedSchemaType; + } + // comments.$.comments.$.title + ret = search(parts.slice(p + 1), foundschema.schema); + if (ret) { + ret.$parentSchemaDocArray = ret.$parentSchemaDocArray || + (foundschema.schema.$isSingleNested ? null : foundschema); + } + return ret; + } + // this is the last path of the selector + ret = search(parts.slice(p), foundschema.schema); + if (ret) { + ret.$parentSchemaDocArray = ret.$parentSchemaDocArray || + (foundschema.schema.$isSingleNested ? null : foundschema); + } + return ret; + } + } + } else if (foundschema.$isSchemaMap) { + if (p >= parts.length) { + return foundschema; + } + // Any path in the map will be an instance of the map's embedded schematype + if (p + 1 >= parts.length) { + return foundschema.$__schemaType; + } + + if (foundschema.$__schemaType instanceof MongooseTypes.Mixed) { + return foundschema.$__schemaType; + } + if (foundschema.$__schemaType.schema != null) { + // Map of docs + const ret = search(parts.slice(p + 1), foundschema.$__schemaType.schema); + return ret; + } + } + + foundschema.$fullPath = resultPath.join('.'); + + return foundschema; + } + } + } + + // look for arrays + const parts = path.split('.'); + for (let i = 0; i < parts.length; ++i) { + if (parts[i] === '$' || isArrayFilter(parts[i])) { + // Re: gh-5628, because `schema.path()` doesn't take $ into account. + parts[i] = '0'; + } + if (numberRE.test(parts[i])) { + parts[i] = '$'; + } + } + return search(parts, _this); +}; + +/*! + * ignore + */ + +Schema.prototype._getPathType = function(path) { + const _this = this; + const pathschema = _this.path(path); + + if (pathschema) { + return 'real'; + } + + function search(parts, schema) { + let p = parts.length + 1, + foundschema, + trypath; + + while (p--) { + trypath = parts.slice(0, p).join('.'); + foundschema = schema.path(trypath); + if (foundschema) { + if (foundschema.embeddedSchemaType || foundschema.Constructor) { + // array of Mixed? + if (foundschema.embeddedSchemaType instanceof MongooseTypes.Mixed) { + return { schema: foundschema, pathType: 'mixed' }; + } + + // Now that we found the array, we need to check if there + // are remaining document paths to look up for casting. + // Also we need to handle array.$.path since schema.path + // doesn't work for that. + // If there is no foundschema.schema we are dealing with + // a path like array.$ + if (p !== parts.length && foundschema.schema) { + if (parts[p] === '$' || isArrayFilter(parts[p])) { + if (p === parts.length - 1) { + return { schema: foundschema, pathType: 'nested' }; + } + // comments.$.comments.$.title + return search(parts.slice(p + 1), foundschema.schema); + } + // this is the last path of the selector + return search(parts.slice(p), foundschema.schema); + } + return { + schema: foundschema, + pathType: foundschema.$isSingleNested ? 'nested' : 'array' + }; + } + return { schema: foundschema, pathType: 'real' }; + } else if (p === parts.length && schema.nested[trypath]) { + return { schema: schema, pathType: 'nested' }; + } + } + return { schema: foundschema || schema, pathType: 'undefined' }; + } + + // look for arrays + return search(path.split('.'), _this); +}; + +/** + * Transforms the duplicate key error by checking for duplicate key error messages by path. + * If no duplicate key error messages are found, returns the original error. + * + * @param {Error} error The error to transform + * @returns {Error} The transformed error + * @api private + */ + +Schema.prototype._transformDuplicateKeyError = function _transformDuplicateKeyError(error) { + if (!this._duplicateKeyErrorMessagesByPath) { + return error; + } + if (error.code !== 11000 && error.code !== 11001) { + return error; + } + + if (error.keyPattern != null) { + const keyPattern = error.keyPattern; + const keys = Object.keys(keyPattern); + if (keys.length !== 1) { + return error; + } + const firstKey = keys[0]; + if (!Object.hasOwn(this._duplicateKeyErrorMessagesByPath, firstKey)) { + return error; + } + return new MongooseError(this._duplicateKeyErrorMessagesByPath[firstKey], { cause: error }); + } + + return error; +}; + +/*! + * ignore + */ + +function isArrayFilter(piece) { + return piece.startsWith('$[') && piece.endsWith(']'); +} + +/** + * Called by `compile()` _right before_ compiling. Good for making any changes to + * the schema that should respect options set by plugins, like `id` + * @method _preCompile + * @memberOf Schema + * @instance + * @api private + */ + +Schema.prototype._preCompile = function _preCompile() { + this.plugin(idGetter, { deduplicate: true }); +}; + +/** + * Returns a JSON schema representation of this Schema. + * + * By default, returns normal [JSON schema representation](https://json-schema.org/learn/getting-started-step-by-step), which is not typically what you want to use with + * [MongoDB's `$jsonSchema` collection option](https://www.mongodb.com/docs/manual/core/schema-validation/specify-json-schema/). + * Use the `useBsonType: true` option to return MongoDB `$jsonSchema` syntax instead. + * + * In addition to types, `jsonSchema()` supports the following Mongoose validators: + * - `enum` for strings and numbers + * + * #### Example: + * const schema = new Schema({ name: String }); + * // { required: ['_id'], properties: { name: { type: ['string', 'null'] }, _id: { type: 'string' } } } + * schema.toJSONSchema(); + * + * // { required: ['_id'], properties: { name: { bsonType: ['string', 'null'] }, _id: { bsonType: 'objectId' } } } + * schema.toJSONSchema({ useBsonType: true }); + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] if true, specify each path's type using `bsonType` rather than `type` for MongoDB $jsonSchema support + */ + +Schema.prototype.toJSONSchema = function toJSONSchema(options) { + const useBsonType = options?.useBsonType ?? false; + const result = useBsonType ? { required: [], properties: {} } : { type: 'object', required: [], properties: {} }; + for (const path of Object.keys(this.paths)) { + const schemaType = this.paths[path]; + + // Skip Map embedded paths, maps will be handled separately. + if (schemaType._presplitPath.indexOf('$*') !== -1) { + continue; + } + + // Nested paths are stored as `nested.path` in the schema type, so create nested paths in the json schema + // when necessary. + const isNested = schemaType._presplitPath.length > 1; + let jsonSchemaForPath = result; + if (isNested) { + for (let i = 0; i < schemaType._presplitPath.length - 1; ++i) { + const subpath = schemaType._presplitPath[i]; + if (jsonSchemaForPath.properties[subpath] == null) { + jsonSchemaForPath.properties[subpath] = useBsonType + ? { + bsonType: ['object', 'null'], + properties: {} + } + : { + type: ['object', 'null'], + properties: {} + }; + } + jsonSchemaForPath = jsonSchemaForPath.properties[subpath]; + } + } + + const lastSubpath = schemaType._presplitPath[schemaType._presplitPath.length - 1]; + let isRequired = false; + if (path === '_id') { + if (!jsonSchemaForPath.required) { + jsonSchemaForPath.required = []; + } + jsonSchemaForPath.required.push('_id'); + isRequired = true; + } else if (schemaType.options.required && typeof schemaType.options.required !== 'function') { + if (!jsonSchemaForPath.required) { + jsonSchemaForPath.required = []; + } + // Only `required: true` paths are required, conditional required is not required + jsonSchemaForPath.required.push(lastSubpath); + isRequired = true; + } + jsonSchemaForPath.properties[lastSubpath] = schemaType.toJSONSchema(options); + if (schemaType.options.enum) { + jsonSchemaForPath.properties[lastSubpath].enum = isRequired + ? schemaType.options.enum + : [...schemaType.options.enum, null]; + } + } + + // Otherwise MongoDB errors with "$jsonSchema keyword 'required' cannot be an empty array" + if (result.required.length === 0) { + delete result.required; + } + return result; +}; + +/*! + * Module exports. + */ + +module.exports = exports = Schema; + +// require down here because of reference issues + +/** + * The various built-in Mongoose Schema Types. + * + * #### Example: + * + * const mongoose = require('mongoose'); + * const ObjectId = mongoose.Schema.Types.ObjectId; + * + * #### Types: + * + * - [String](https://mongoosejs.com/docs/schematypes.html#strings) + * - [Number](https://mongoosejs.com/docs/schematypes.html#numbers) + * - [Boolean](https://mongoosejs.com/docs/schematypes.html#booleans) | Bool + * - [Array](https://mongoosejs.com/docs/schematypes.html#arrays) + * - [Buffer](https://mongoosejs.com/docs/schematypes.html#buffers) + * - [Date](https://mongoosejs.com/docs/schematypes.html#dates) + * - [ObjectId](https://mongoosejs.com/docs/schematypes.html#objectids) | Oid + * - [Mixed](https://mongoosejs.com/docs/schematypes.html#mixed) + * - [UUID](https://mongoosejs.com/docs/schematypes.html#uuid) + * - [BigInt](https://mongoosejs.com/docs/schematypes.html#bigint) + * - [Double] (https://mongoosejs.com/docs/schematypes.html#double) + * - [Int32](https://mongoosejs.com/docs/schematypes.html#int32) + * + * Using this exposed access to the `Mixed` SchemaType, we can use them in our schema. + * + * const Mixed = mongoose.Schema.Types.Mixed; + * new mongoose.Schema({ _user: Mixed }) + * + * @api public + */ + +Schema.Types = MongooseTypes = require('./schema/index'); + +/*! + * ignore + */ + +exports.ObjectId = MongooseTypes.ObjectId; diff --git a/backend/node_modules/mongoose/lib/schema/array.js b/backend/node_modules/mongoose/lib/schema/array.js new file mode 100644 index 0000000..6ba94a3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/array.js @@ -0,0 +1,702 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const $exists = require('./operators/exists'); +const $type = require('./operators/type'); +const MongooseError = require('../error/mongooseError'); +const SchemaArrayOptions = require('../options/schemaArrayOptions'); +const SchemaType = require('../schemaType'); +const CastError = SchemaType.CastError; +const Mixed = require('./mixed'); +const VirtualOptions = require('../options/virtualOptions'); +const VirtualType = require('../virtualType'); +const arrayDepth = require('../helpers/arrayDepth'); +const cast = require('../cast'); +const clone = require('../helpers/clone'); +const getConstructorName = require('../helpers/getConstructorName'); +const isOperator = require('../helpers/query/isOperator'); +const util = require('util'); +const utils = require('../utils'); +const castToNumber = require('./operators/helpers').castToNumber; +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const geospatial = require('./operators/geospatial'); +const getDiscriminatorByValue = require('../helpers/discriminator/getDiscriminatorByValue'); + +let MongooseArray; +let EmbeddedDoc; + +const emptyOpts = Object.freeze({}); + +/** + * Array SchemaType constructor + * + * @param {string} key + * @param {SchemaType} cast + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaArray(key, cast, options, schemaOptions, parentSchema) { + // lazy load + EmbeddedDoc || (EmbeddedDoc = require('../types').Embedded); + + let typeKey = 'type'; + if (schemaOptions?.typeKey) { + typeKey = schemaOptions.typeKey; + } + this.schemaOptions = schemaOptions; + + if (cast) { + let castOptions = {}; + + if (utils.isPOJO(cast)) { + if (cast[typeKey]) { + // support { type: Woot } + castOptions = clone(cast); // do not alter user arguments + delete castOptions[typeKey]; + cast = cast[typeKey]; + } else { + cast = Mixed; + } + } + + if (options?.ref != null && castOptions.ref == null) { + castOptions.ref = options.ref; + } + + if (cast === Object) { + cast = Mixed; + } + + // support { type: 'String' } + const name = typeof cast === 'string' + ? cast + : utils.getFunctionName(cast); + + const Types = require('./index.js'); + const schemaTypeDefinition = Object.hasOwn(Types, name) ? Types[name] : cast; + + if (typeof schemaTypeDefinition === 'function') { + if (schemaTypeDefinition === SchemaArray) { + this.embeddedSchemaType = new schemaTypeDefinition(key, castOptions, schemaOptions, null, parentSchema); + } else { + this.embeddedSchemaType = new schemaTypeDefinition(key, castOptions, schemaOptions, parentSchema); + } + } else if (schemaTypeDefinition instanceof SchemaType) { + this.embeddedSchemaType = schemaTypeDefinition; + if (!(this.embeddedSchemaType instanceof EmbeddedDoc)) { + this.embeddedSchemaType.path = key; + } + } + + } + + this.$isMongooseArray = true; + + SchemaType.call(this, key, options, 'Array', parentSchema); + + let defaultArr; + let fn; + + if (this.defaultValue != null) { + defaultArr = this.defaultValue; + fn = typeof defaultArr === 'function'; + } + + if (!('defaultValue' in this) || this.defaultValue != null) { + const defaultFn = function() { + // Leave it up to `cast()` to convert the array + return fn + ? defaultArr.call(this) + : defaultArr != null + ? [].concat(defaultArr) + : []; + }; + defaultFn.$runBeforeSetters = !fn; + this.default(defaultFn); + } +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaArray.schemaName = 'Array'; + + +/** + * Options for all arrays. + * + * - `castNonArrays`: `true` by default. If `false`, Mongoose will throw a CastError when a value isn't an array. If `true`, Mongoose will wrap the provided value in an array before casting. + * + * @static + * @api public + */ + +SchemaArray.options = { castNonArrays: true }; + +/*! + * ignore + */ + +SchemaArray.defaultOptions = {}; + +/** + * Sets a default option for all Array instances. + * + * #### Example: + * + * // Make all Array instances have `required` of true by default. + * mongoose.Schema.Types.Array.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: Array })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @api public + */ +SchemaArray.set = SchemaType.set; + +SchemaArray.setters = []; + +/** + * Attaches a getter for all Array instances + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaArray.get = SchemaType.get; + +/*! + * Inherits from SchemaType. + */ +SchemaArray.prototype = Object.create(SchemaType.prototype); +SchemaArray.prototype.constructor = SchemaArray; +SchemaArray.prototype.OptionsConstructor = SchemaArrayOptions; + +/*! + * ignore + */ + +SchemaArray._checkRequired = SchemaType.prototype.checkRequired; + +/** + * Override the function the required validator uses to check whether an array + * passes the `required` check. + * + * #### Example: + * + * // Require non-empty array to pass `required` check + * mongoose.Schema.Types.Array.checkRequired(v => Array.isArray(v) && v.length); + * + * const M = mongoose.model({ arr: { type: Array, required: true } }); + * new M({ arr: [] }).validateSync(); // `null`, validation fails! + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @api public + */ + +SchemaArray.checkRequired = SchemaType.checkRequired; + +/*! + * Virtuals defined on this array itself. + */ + +SchemaArray.prototype.virtuals = null; + +/** + * Check if the given value satisfies the `required` validator. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaArray.prototype.checkRequired = function checkRequired(value, doc) { + if (typeof value === 'object' && SchemaType._isRef(this, value, doc, true)) { + return !!value; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaArray.checkRequired(); + + return _checkRequired(value); +}; + +/** + * Adds an enum validator if this is an array of strings or numbers. Equivalent to + * `SchemaString.prototype.enum()` or `SchemaNumber.prototype.enum()` + * + * @param {...string|object} [args] enumeration values + * @return {SchemaArray} this + */ + +SchemaArray.prototype.enum = function() { + let arr = this; + while (true) { + const instance = arr?.embeddedSchemaType?.instance; + if (instance === 'Array') { + arr = arr.embeddedSchemaType; + continue; + } + if (instance !== 'String' && instance !== 'Number') { + throw new Error('`enum` can only be set on an array of strings or numbers ' + + ', not ' + instance); + } + break; + } + + let enumArray = arguments; + if (!Array.isArray(arguments) && utils.isObject(arguments)) { + enumArray = utils.object.vals(enumArray); + } + + arr.embeddedSchemaType.enum.apply(arr.embeddedSchemaType, enumArray); + return this; +}; + +/** + * Overrides the getters application for the population special-case + * + * @param {object} value + * @param {object} scope + * @api private + */ + +SchemaArray.prototype.applyGetters = function(value, scope) { + if (scope?.$__ != null && scope.$populated(this.path)) { + // means the object id was populated + return value; + } + + const ret = SchemaType.prototype.applyGetters.call(this, value, scope); + return ret; +}; + +SchemaArray.prototype._applySetters = function(value, scope, init, priorVal) { + if (this.embeddedSchemaType.$isMongooseArray && + SchemaArray.options.castNonArrays) { + // Check nesting levels and wrap in array if necessary + let depth = 0; + let arr = this; + while (arr != null && + arr.$isMongooseArray && + !arr.$isMongooseDocumentArray) { + ++depth; + arr = arr.embeddedSchemaType; + } + + // No need to wrap empty arrays + if (value != null && value.length !== 0) { + const valueDepth = arrayDepth(value); + if (valueDepth.min === valueDepth.max && valueDepth.max < depth && valueDepth.containsNonArrayItem) { + for (let i = valueDepth.max; i < depth; ++i) { + value = [value]; + } + } + } + } + + return SchemaType.prototype._applySetters.call(this, value, scope, init, priorVal); +}; + +/** + * Casts values for set(). + * + * @param {object} value + * @param {Document} doc document that triggers the casting + * @param {boolean} init whether this is an initialization cast + * @api private + */ + +SchemaArray.prototype.cast = function(value, doc, init, prev, options) { + // lazy load + MongooseArray || (MongooseArray = require('../types').Array); + + let i; + let l; + + if (Array.isArray(value)) { + const len = value.length; + if (!len && doc) { + const indexes = doc.schema.indexedPaths(); + + const arrayPath = this.path; + for (i = 0, l = indexes.length; i < l; ++i) { + const pathIndex = indexes[i][0][arrayPath]; + if (pathIndex === '2dsphere' || pathIndex === '2d') { + return; + } + } + + // Special case: if this index is on the parent of what looks like + // GeoJSON, skip setting the default to empty array re: #1668, #3233 + const arrayGeojsonPath = this.path.endsWith('.coordinates') ? + this.path.substring(0, this.path.lastIndexOf('.')) : null; + if (arrayGeojsonPath != null) { + for (i = 0, l = indexes.length; i < l; ++i) { + const pathIndex = indexes[i][0][arrayGeojsonPath]; + if (pathIndex === '2dsphere') { + return; + } + } + } + } + + options = options || emptyOpts; + + let rawValue = utils.isMongooseArray(value) ? value.__array : value; + let path = options.path || this.path; + if (options.arrayPathIndex != null) { + path += '.' + options.arrayPathIndex; + } + value = MongooseArray(rawValue, path, doc, this); + rawValue = value.__array; + + if (init && doc?.$__ != null && doc.$populated(this.path)) { + return value; + } + + const caster = this.embeddedSchemaType; + const isMongooseArray = caster.$isMongooseArray; + if (caster && this.embeddedSchemaType.constructor !== Mixed) { + try { + const len = rawValue.length; + for (i = 0; i < len; i++) { + const opts = {}; + // Perf: creating `arrayPath` is expensive for large arrays. + // We only need `arrayPath` if this is a nested array, so + // skip if possible. + if (isMongooseArray) { + if (options.arrayPath != null) { + opts.arrayPathIndex = i; + } else if (caster._arrayParentPath != null) { + opts.arrayPathIndex = i; + } + } + if (options.hydratedPopulatedDocs) { + opts.hydratedPopulatedDocs = options.hydratedPopulatedDocs; + } + if (options.virtuals) { + opts.virtuals = options.virtuals; + } + rawValue[i] = caster.applySetters(rawValue[i], doc, init, void 0, opts); + } + } catch (e) { + // rethrow + throw new CastError('[' + e.kind + ']', util.inspect(value), this.path + '.' + i, e, this); + } + } + + return value; + } + + const castNonArraysOption = this.options.castNonArrays ?? SchemaArray.options.castNonArrays; + if (init || castNonArraysOption) { + // gh-2442: if we're loading this from the db and its not an array, mark + // the whole array as modified. + if (doc && init) { + doc.markModified(this.path); + } + return this.cast([value], doc, init); + } + + throw new CastError('Array', util.inspect(value), this.path, null, this); +}; + +/*! + * ignore + */ + +SchemaArray.prototype._castForPopulate = function _castForPopulate(value, doc) { + // lazy load + MongooseArray || (MongooseArray = require('../types').Array); + + if (Array.isArray(value)) { + let i; + const rawValue = value.__array ? value.__array : value; + const len = rawValue.length; + + if (this.embeddedSchemaType && this.embeddedSchemaType.constructor !== Mixed) { + try { + for (i = 0; i < len; i++) { + const opts = {}; + // Perf: creating `arrayPath` is expensive for large arrays. + // We only need `arrayPath` if this is a nested array, so + // skip if possible. + if (this.embeddedSchemaType.$isMongooseArray && this.embeddedSchemaType._arrayParentPath != null) { + opts.arrayPathIndex = i; + } + + rawValue[i] = this.embeddedSchemaType.cast(rawValue[i], doc, false, void 0, opts); + } + } catch (e) { + // rethrow + throw new CastError('[' + e.kind + ']', util.inspect(value), this.path + '.' + i, e, this); + } + } + + return value; + } + + throw new CastError('Array', util.inspect(value), this.path, null, this); +}; + +SchemaArray.prototype.$toObject = SchemaArray.prototype.toObject; + +/*! + * ignore + */ + +SchemaArray.prototype.discriminator = function(...args) { + let arr = this; + while (arr.$isMongooseArray && !arr.$isMongooseDocumentArray) { + arr = arr.embeddedSchemaType; + } + if (!arr.$isMongooseDocumentArray) { + throw new MongooseError('You can only add an embedded discriminator on a document array, ' + this.path + ' is a plain array'); + } + return arr.discriminator(...args); +}; + +/*! + * ignore + */ + +SchemaArray.prototype.clone = function() { + const options = Object.assign({}, this.options); + const schematype = new this.constructor(this.path, this.embeddedSchemaType, options, this.schemaOptions, this.parentSchema); + schematype.validators = this.validators.slice(); + if (this.requiredValidator !== undefined) { + schematype.requiredValidator = this.requiredValidator; + } + return schematype; +}; + +SchemaArray.prototype._castForQuery = function(val, context) { + let embeddedSchemaType = this.embeddedSchemaType; + const discriminatorKey = embeddedSchemaType?.schema?.options?.discriminatorKey; + const discriminators = embeddedSchemaType?.discriminators; + + if (val && discriminators && typeof discriminatorKey === 'string') { + if (discriminators[val[discriminatorKey]]) { + embeddedSchemaType = discriminators[val[discriminatorKey]]; + } else { + const constructorByValue = getDiscriminatorByValue(discriminators, val[discriminatorKey]); + if (constructorByValue) { + embeddedSchemaType = constructorByValue; + } + } + } + + if (Array.isArray(val)) { + this.setters.reverse().forEach(setter => { + val = setter.call(this, val, this); + }); + val = val.map(function(v) { + if (utils.isObject(v) && v.$elemMatch) { + return v; + } + return embeddedSchemaType.castForQuery(null, v, context); + }); + } else { + val = embeddedSchemaType.castForQuery(null, val, context); + } + + return val; +}; + +/** + * Casts values for queries. + * + * @param {string} $conditional + * @param {any} [value] + * @api private + */ + +SchemaArray.prototype.castForQuery = function($conditional, val, context) { + let handler; + + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + + if (!handler) { + throw new Error('Can\'t use ' + $conditional + ' with Array.'); + } + + return handler.call(this, val, context); + } else { + return this._castForQuery(val, context); + } +}; + +/** + * Add a virtual to this array. Specifically to this array, not the individual elements. + * + * @param {string} name + * @param {object} [options] + * @api private + */ + +SchemaArray.prototype.virtual = function virtual(name, options) { + if (name instanceof VirtualType || getConstructorName(name) === 'VirtualType') { + return this.virtual(name.path, name.options); + } + options = new VirtualOptions(options); + + if (utils.hasUserDefinedProperty(options, ['ref', 'refPath'])) { + throw new MongooseError('Cannot set populate virtual as a property of an array'); + } + + const virtual = new VirtualType(options, name); + if (this.virtuals === null) { + this.virtuals = {}; + } + this.virtuals[name] = virtual; + return virtual; +}; + +function cast$all(val, context) { + if (!Array.isArray(val)) { + val = [val]; + } + + val = val.map((v) => { + if (!utils.isObject(v)) { + return v; + } + if (v.$elemMatch != null) { + return { $elemMatch: cast(this.embeddedSchemaType.schema, v.$elemMatch, null, this?.$$context) }; + } + + const o = {}; + o[this.path] = v; + return cast(this.embeddedSchemaType.schema, o, null, this?.$$context)[this.path]; + }, this); + + return this.castForQuery(null, val, context); +} + +function cast$elemMatch(val, context) { + const keys = Object.keys(val); + const numKeys = keys.length; + for (let i = 0; i < numKeys; ++i) { + const key = keys[i]; + const value = val[key]; + if (isOperator(key) && value != null) { + val[key] = this.castForQuery(key, value, context); + } + } + + return val; +} + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$all` is the function Mongoose calls to cast `$all` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaArray + * @instance + * @api public + */ + +const handle = SchemaArray.prototype.$conditionalHandlers = {}; + +handle.$all = cast$all; +handle.$options = String; +handle.$elemMatch = cast$elemMatch; +handle.$geoIntersects = geospatial.cast$geoIntersects; +handle.$or = createLogicalQueryOperatorHandler('$or'); +handle.$and = createLogicalQueryOperatorHandler('$and'); +handle.$nor = createLogicalQueryOperatorHandler('$nor'); + +function createLogicalQueryOperatorHandler(op) { + return function logicalQueryOperatorHandler(val, context) { + if (!Array.isArray(val)) { + throw new TypeError('conditional ' + op + ' requires an array'); + } + + const ret = []; + for (const obj of val) { + ret.push(cast(this.embeddedSchemaType.schema ?? context.schema, obj, null, this?.$$context)); + } + + return ret; + }; +} + +handle.$near = +handle.$nearSphere = geospatial.cast$near; + +handle.$within = +handle.$geoWithin = geospatial.cast$within; + +handle.$size = +handle.$minDistance = +handle.$maxDistance = castToNumber; + +handle.$exists = $exists; +handle.$type = $type; + +handle.$eq = +handle.$gt = +handle.$gte = +handle.$lt = +handle.$lte = +handle.$not = +handle.$regex = +handle.$ne = SchemaArray.prototype._castForQuery; + +// `$in` is special because you can also include an empty array in the query +// like `$in: [1, []]`, see gh-5913 +handle.$nin = SchemaType.prototype.$conditionalHandlers.$nin; +handle.$in = SchemaType.prototype.$conditionalHandlers.$in; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaArray.prototype.toJSONSchema = function toJSONSchema(options) { + const embeddedSchemaType = this.getEmbeddedSchemaType(); + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return { + ...createJSONSchemaTypeDefinition('array', 'array', options?.useBsonType, isRequired), + items: embeddedSchemaType.toJSONSchema(options) + }; +}; + +SchemaArray.prototype.autoEncryptionType = function autoEncryptionType() { + return 'array'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaArray; diff --git a/backend/node_modules/mongoose/lib/schema/bigint.js b/backend/node_modules/mongoose/lib/schema/bigint.js new file mode 100644 index 0000000..f7d59bd --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/bigint.js @@ -0,0 +1,282 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const SchemaType = require('../schemaType'); +const castBigInt = require('../cast/bigint'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); + +/** + * BigInt SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaBigInt(path, options, _schemaOptions, parentSchema) { + SchemaType.call(this, path, options, 'BigInt', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaBigInt.schemaName = 'BigInt'; + +SchemaBigInt.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaBigInt.prototype = Object.create(SchemaType.prototype); +SchemaBigInt.prototype.constructor = SchemaBigInt; + +/*! + * ignore + */ + +SchemaBigInt._cast = castBigInt; + +/** + * Sets a default option for all BigInt instances. + * + * #### Example: + * + * // Make all bigints required by default + * mongoose.Schema.Types.BigInt.set('required', true); + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaBigInt.set = SchemaType.set; + +SchemaBigInt.setters = []; + +/** + * Attaches a getter for all BigInt instances + * + * #### Example: + * + * // Convert bigints to numbers + * mongoose.Schema.Types.BigInt.get(v => v == null ? v : Number(v)); + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaBigInt.get = SchemaType.get; + +/** + * Get/set the function used to cast arbitrary values to bigints. + * + * #### Example: + * + * // Make Mongoose cast empty string '' to false. + * const original = mongoose.Schema.Types.BigInt.cast(); + * mongoose.Schema.Types.BigInt.cast(v => { + * if (v === '') { + * return false; + * } + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Schema.Types.BigInt.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaBigInt.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaBigInt._checkRequired = v => v != null; + +/** + * Override the function the required validator uses to check whether a value + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaBigInt.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @return {boolean} + * @api public + */ + +SchemaBigInt.prototype.checkRequired = function(value) { + return this.constructor._checkRequired(value); +}; + +/** + * Casts to bigint + * + * @param {object} value + * @param {object} model this value is optional + * @api private + */ + +SchemaBigInt.prototype.cast = function(value) { + let castBigInt; + if (typeof this._castFunction === 'function') { + castBigInt = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castBigInt = this.constructor.cast(); + } else { + castBigInt = SchemaBigInt.cast(); + } + + try { + return castBigInt(value); + } catch (error) { + throw new CastError('BigInt', value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$in` is the function Mongoose calls to cast `$in` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaBigInt + * @instance + * @api public + */ + +Object.defineProperty(SchemaBigInt.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/*! + * ignore + */ + +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} val + * @api private + */ + +SchemaBigInt.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + + if (handler) { + return handler.call(this, val); + } + + return this.applySetters(val, context); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * + * @api private + */ + +SchemaBigInt.prototype._castNullish = function _castNullish(v) { + if (typeof v === 'undefined') { + return v; + } + const castBigInt = typeof this.constructor.cast === 'function' ? + this.constructor.cast() : + SchemaBigInt.cast(); + if (castBigInt == null) { + return v; + } + return v; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaBigInt.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'long', options?.useBsonType, isRequired); +}; + +SchemaBigInt.prototype.autoEncryptionType = function autoEncryptionType() { + return 'long'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBigInt; diff --git a/backend/node_modules/mongoose/lib/schema/boolean.js b/backend/node_modules/mongoose/lib/schema/boolean.js new file mode 100644 index 0000000..4726ee5 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/boolean.js @@ -0,0 +1,332 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const SchemaType = require('../schemaType'); +const castBoolean = require('../cast/boolean'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); + +/** + * Boolean SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaBoolean(path, options, _schemaOptions, parentSchema) { + SchemaType.call(this, path, options, 'Boolean', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaBoolean.schemaName = 'Boolean'; + +SchemaBoolean.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaBoolean.prototype = Object.create(SchemaType.prototype); +SchemaBoolean.prototype.constructor = SchemaBoolean; + +/*! + * ignore + */ + +SchemaBoolean._cast = castBoolean; + +/** + * Sets a default option for all Boolean instances. + * + * #### Example: + * + * // Make all booleans have `default` of false. + * mongoose.Schema.Types.Boolean.set('default', false); + * + * const Order = mongoose.model('Order', new Schema({ isPaid: Boolean })); + * new Order({ }).isPaid; // false + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaBoolean.set = SchemaType.set; + +SchemaBoolean.setters = []; + +/** + * Attaches a getter for all Boolean instances + * + * #### Example: + * + * mongoose.Schema.Types.Boolean.get(v => v === true ? 'yes' : 'no'); + * + * const Order = mongoose.model('Order', new Schema({ isPaid: Boolean })); + * new Order({ isPaid: false }).isPaid; // 'no' + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaBoolean.get = SchemaType.get; + +/** + * Get/set the function used to cast arbitrary values to booleans. + * + * #### Example: + * + * // Make Mongoose cast empty string '' to false. + * const original = mongoose.Schema.Types.Boolean.cast(); + * mongoose.Schema.Types.Boolean.cast(v => { + * if (v === '') { + * return false; + * } + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Schema.Types.Boolean.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaBoolean.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaBoolean._defaultCaster = v => { + if (v != null && typeof v !== 'boolean') { + throw new Error(); + } + return v; +}; + +/*! + * ignore + */ + +SchemaBoolean._checkRequired = v => v === true || v === false; + +/** + * Override the function the required validator uses to check whether a boolean + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaBoolean.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. For a boolean + * to satisfy a required validator, it must be strictly equal to true or to + * false. + * + * @param {any} value + * @return {boolean} + * @api public + */ + +SchemaBoolean.prototype.checkRequired = function(value) { + return this.constructor._checkRequired(value); +}; + +/** + * Configure which values get casted to `true`. + * + * #### Example: + * + * const M = mongoose.model('Test', new Schema({ b: Boolean })); + * new M({ b: 'affirmative' }).b; // undefined + * mongoose.Schema.Types.Boolean.convertToTrue.add('affirmative'); + * new M({ b: 'affirmative' }).b; // true + * + * @property convertToTrue + * @static + * @memberOf SchemaBoolean + * @type {Set} + * @api public + */ + +Object.defineProperty(SchemaBoolean, 'convertToTrue', { + get: () => castBoolean.convertToTrue, + set: v => { castBoolean.convertToTrue = v; } +}); + +/** + * Configure which values get casted to `false`. + * + * #### Example: + * + * const M = mongoose.model('Test', new Schema({ b: Boolean })); + * new M({ b: 'nay' }).b; // undefined + * mongoose.Schema.Types.Boolean.convertToFalse.add('nay'); + * new M({ b: 'nay' }).b; // false + * + * @property convertToFalse + * @static + * @memberOf SchemaBoolean + * @type {Set} + * @api public + */ + +Object.defineProperty(SchemaBoolean, 'convertToFalse', { + get: () => castBoolean.convertToFalse, + set: v => { castBoolean.convertToFalse = v; } +}); + +/** + * Casts to boolean + * + * @param {object} value + * @param {object} model this value is optional + * @api private + */ + +SchemaBoolean.prototype.cast = function(value) { + let castBoolean; + if (typeof this._castFunction === 'function') { + castBoolean = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castBoolean = this.constructor.cast(); + } else { + castBoolean = SchemaBoolean.cast(); + } + + try { + return castBoolean(value); + } catch (error) { + throw new CastError('Boolean', value, this.path, error, this); + } +}; + +const $conditionalHandlers = { ...SchemaType.prototype.$conditionalHandlers }; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$in` is the function Mongoose calls to cast `$in` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaBoolean + * @instance + * @api public + */ + +Object.defineProperty(SchemaBoolean.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} val + * @api private + */ + +SchemaBoolean.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + + if (handler) { + return handler.call(this, val); + } + + return this.applySetters(val, context); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * + * @api private + */ + +SchemaBoolean.prototype._castNullish = function _castNullish(v) { + if (typeof v === 'undefined') { + return v; + } + const castBoolean = typeof this.constructor.cast === 'function' ? + this.constructor.cast() : + SchemaBoolean.cast(); + if (castBoolean == null) { + return v; + } + if (castBoolean.convertToFalse instanceof Set && castBoolean.convertToFalse.has(v)) { + return false; + } + if (castBoolean.convertToTrue instanceof Set && castBoolean.convertToTrue.has(v)) { + return true; + } + return v; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaBoolean.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('boolean', 'bool', options?.useBsonType, isRequired); +}; + +SchemaBoolean.prototype.autoEncryptionType = function autoEncryptionType() { + return 'bool'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBoolean; diff --git a/backend/node_modules/mongoose/lib/schema/buffer.js b/backend/node_modules/mongoose/lib/schema/buffer.js new file mode 100644 index 0000000..7792870 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/buffer.js @@ -0,0 +1,343 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseBuffer = require('../types/buffer'); +const SchemaBufferOptions = require('../options/schemaBufferOptions'); +const SchemaType = require('../schemaType'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const handleBitwiseOperator = require('./operators/bitwise'); +const utils = require('../utils'); + +const Binary = MongooseBuffer.Binary; +const CastError = SchemaType.CastError; + +/** + * Buffer SchemaType constructor + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaBuffer(key, options, _schemaOptions, parentSchema) { + SchemaType.call(this, key, options, 'Buffer', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaBuffer.schemaName = 'Buffer'; + +SchemaBuffer.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaBuffer.prototype = Object.create(SchemaType.prototype); +SchemaBuffer.prototype.constructor = SchemaBuffer; +SchemaBuffer.prototype.OptionsConstructor = SchemaBufferOptions; + +/*! + * ignore + */ + +SchemaBuffer._checkRequired = v => !!v?.length; + +/** + * Sets a default option for all Buffer instances. + * + * #### Example: + * + * // Make all buffers have `required` of true by default. + * mongoose.Schema.Types.Buffer.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: Buffer })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaBuffer.set = SchemaType.set; + +SchemaBuffer.setters = []; + +/** + * Attaches a getter for all Buffer instances + * + * #### Example: + * + * // Always convert to string when getting an ObjectId + * mongoose.Schema.Types.Buffer.get(v => v.toString('hex')); + * + * const Model = mongoose.model('Test', new Schema({ buf: Buffer } })); + * typeof (new Model({ buf: Buffer.fromString('hello') }).buf); // 'string' + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaBuffer.get = SchemaType.get; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * #### Example: + * + * // Allow empty strings to pass `required` check + * mongoose.Schema.Types.String.checkRequired(v => v != null); + * + * const M = mongoose.model({ buf: { type: Buffer, required: true } }); + * new M({ buf: Buffer.from('') }).validateSync(); // validation passes! + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaBuffer.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. To satisfy a + * required validator, a buffer must not be null or undefined and have + * non-zero length. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaBuffer.prototype.checkRequired = function(value, doc) { + if (SchemaType._isRef(this, value, doc, true)) { + return !!value; + } + return this.constructor._checkRequired(value); +}; + +/** + * Casts contents + * + * @param {object} value + * @param {Document} doc document that triggers the casting + * @param {boolean} init + * @api private + */ + +SchemaBuffer.prototype.cast = function(value, doc, init, prev, options) { + let ret; + if (SchemaType._isRef(this, value, doc, init)) { + if (value?.isMongooseBuffer) { + return value; + } + + if (Buffer.isBuffer(value)) { + if (!value?.isMongooseBuffer) { + value = new MongooseBuffer(value, [this.path, doc]); + if (this.options.subtype != null) { + value._subtype = this.options.subtype; + } + } + return value; + } + + if (value instanceof Binary) { + ret = new MongooseBuffer(value.value(true), [this.path, doc]); + if (typeof value.sub_type !== 'number') { + throw new CastError('Buffer', value, this.path, null, this); + } + ret._subtype = value.sub_type; + return ret; + } + + if (value == null || utils.isNonBuiltinObject(value)) { + return this._castRef(value, doc, init, options); + } + } + + // documents + if (value?._id) { + value = value._id; + } + + if (value?.isMongooseBuffer) { + return value; + } + + if (Buffer.isBuffer(value)) { + if (!value?.isMongooseBuffer) { + value = new MongooseBuffer(value, [this.path, doc]); + if (this.options.subtype != null) { + value._subtype = this.options.subtype; + } + } + return value; + } + + if (value instanceof Binary) { + ret = new MongooseBuffer(value.value(true), [this.path, doc]); + if (typeof value.sub_type !== 'number') { + throw new CastError('Buffer', value, this.path, null, this); + } + ret._subtype = value.sub_type; + return ret; + } + + if (value === null) { + return value; + } + + + const type = typeof value; + if ( + type === 'string' || type === 'number' || Array.isArray(value) || + (type === 'object' && value.type === 'Buffer' && Array.isArray(value.data)) // gh-6863 + ) { + if (type === 'number') { + value = [value]; + } + ret = new MongooseBuffer(value, [this.path, doc]); + if (this.options.subtype != null) { + ret._subtype = this.options.subtype; + } + return ret; + } + + if (utils.isPOJO(value) && (value.$binary instanceof Binary || typeof value.$binary === 'string')) { + const buf = this.cast(Buffer.from(value.$binary, 'base64')); + if (value.$type != null) { + buf._subtype = value.$type; + return buf; + } + } + + throw new CastError('Buffer', value, this.path, null, this); +}; + +/** + * Sets the default [subtype](https://studio3t.com/whats-new/best-practices-uuid-mongodb/) + * for this buffer. You can find a [list of allowed subtypes here](https://www.mongodb.com/docs/manual/reference/bson-types/#binary-data). + * + * #### Example: + * + * const s = new Schema({ uuid: { type: Buffer, subtype: 4 }); + * const M = db.model('M', s); + * const m = new M({ uuid: 'test string' }); + * m.uuid._subtype; // 4 + * + * @param {number} subtype the default subtype + * @return {SchemaType} this + * @api public + */ + +SchemaBuffer.prototype.subtype = function(subtype) { + this.options.subtype = subtype; + return this; +}; + +/*! + * ignore + */ +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $bitsAllClear: handleBitwiseOperator, + $bitsAnyClear: handleBitwiseOperator, + $bitsAllSet: handleBitwiseOperator, + $bitsAnySet: handleBitwiseOperator, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$exists` is the function Mongoose calls to cast `$exists` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaBuffer + * @instance + * @api public + */ + +Object.defineProperty(SchemaBuffer.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} [value] + * @api private + */ + +SchemaBuffer.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new Error('Can\'t use ' + $conditional + ' with Buffer.'); + } + return handler.call(this, val); + } + + let casted; + try { + casted = this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } + return casted ? casted.toObject({ transform: false, virtuals: false }) : casted; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaBuffer.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired); +}; + +SchemaBuffer.prototype.autoEncryptionType = function autoEncryptionType() { + return 'binData'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaBuffer; diff --git a/backend/node_modules/mongoose/lib/schema/date.js b/backend/node_modules/mongoose/lib/schema/date.js new file mode 100644 index 0000000..a147155 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/date.js @@ -0,0 +1,467 @@ +/*! + * Module requirements. + */ + +'use strict'; + +const MongooseError = require('../error/index'); +const SchemaDateOptions = require('../options/schemaDateOptions'); +const SchemaType = require('../schemaType'); +const castDate = require('../cast/date'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const getConstructorName = require('../helpers/getConstructorName'); +const utils = require('../utils'); + +const CastError = SchemaType.CastError; + +/** + * Date SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaDate(key, options, _schemaOptions, parentSchema) { + SchemaType.call(this, key, options, 'Date', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaDate.schemaName = 'Date'; + +SchemaDate.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaDate.prototype = Object.create(SchemaType.prototype); +SchemaDate.prototype.constructor = SchemaDate; +SchemaDate.prototype.OptionsConstructor = SchemaDateOptions; + +/*! + * ignore + */ + +SchemaDate._cast = castDate; + +/** + * Sets a default option for all Date instances. + * + * #### Example: + * + * // Make all dates have `required` of true by default. + * mongoose.Schema.Types.Date.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: Date })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaDate.set = SchemaType.set; + +SchemaDate.setters = []; + +/** + * Attaches a getter for all Date instances + * + * #### Example: + * + * // Always convert Dates to string + * mongoose.Schema.Types.Date.get(v => v.toString()); + * + * const Model = mongoose.model('Test', new Schema({ date: { type: Date, default: () => new Date() } })); + * typeof (new Model({}).date); // 'string' + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaDate.get = SchemaType.get; + +/** + * Get/set the function used to cast arbitrary values to dates. + * + * #### Example: + * + * // Mongoose converts empty string '' into `null` for date types. You + * // can create a custom caster to disable it. + * const original = mongoose.Schema.Types.Date.cast(); + * mongoose.Schema.Types.Date.cast(v => { + * assert.ok(v !== ''); + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Schema.Types.Date.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaDate.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaDate._defaultCaster = v => { + if (v != null && !(v instanceof Date)) { + throw new Error(); + } + return v; +}; + +/** + * Declares a TTL index (rounded to the nearest second) for _Date_ types only. + * + * This sets the `expireAfterSeconds` index option available in MongoDB >= 2.1.2. + * This index type is only compatible with Date types. + * + * #### Example: + * + * // expire in 24 hours + * new Schema({ createdAt: { type: Date, expires: 60*60*24 }}); + * + * `expires` utilizes the `ms` module from [vercel](https://github.com/vercel/ms) allowing us to use a friendlier syntax: + * + * #### Example: + * + * // expire in 24 hours + * new Schema({ createdAt: { type: Date, expires: '24h' }}); + * + * // expire in 1.5 hours + * new Schema({ createdAt: { type: Date, expires: '1.5h' }}); + * + * // expire in 7 days + * const schema = new Schema({ createdAt: Date }); + * schema.path('createdAt').expires('7d'); + * + * @param {number|string} when + * @added 3.0.0 + * @return {SchemaType} this + * @api public + */ + +SchemaDate.prototype.expires = function(when) { + if (getConstructorName(this._index) !== 'Object') { + this._index = {}; + } + + this._index.expires = when; + utils.expires(this._index); + return this; +}; + +/*! + * ignore + */ + +SchemaDate._checkRequired = v => v instanceof Date; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * #### Example: + * + * // Allow empty strings to pass `required` check + * mongoose.Schema.Types.String.checkRequired(v => v != null); + * + * const M = mongoose.model({ str: { type: String, required: true } }); + * new M({ str: '' }).validateSync(); // `null`, validation passes! + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaDate.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. To satisfy + * a required validator, the given value must be an instance of `Date`. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaDate.prototype.checkRequired = function(value, doc) { + if (typeof value === 'object' && SchemaType._isRef(this, value, doc, true)) { + return value != null; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaDate.checkRequired(); + return _checkRequired(value); +}; + +/** + * Sets a minimum date validator. + * + * #### Example: + * + * const s = new Schema({ d: { type: Date, min: Date('1970-01-01') }) + * const M = db.model('M', s) + * const m = new M({ d: Date('1969-12-31') }) + * m.save(function (err) { + * console.error(err) // validator error + * m.d = Date('2014-12-08'); + * m.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MIN} token which will be replaced with the invalid value + * const min = [Date('1970-01-01'), 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).']; + * const schema = new Schema({ d: { type: Date, min: min }) + * const M = mongoose.model('M', schema); + * const s= new M({ d: Date('1969-12-31') }); + * s.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `d` (1969-12-31) is before the limit (1970-01-01). + * }) + * + * @param {Date} value minimum date + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaDate.prototype.min = function(value, message) { + if (this.minValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.minValidator; + }, this); + } + + if (value) { + let msg = message || MongooseError.messages.Date.min; + if (typeof msg === 'string') { + msg = msg.replace(/{MIN}/, (value === Date.now ? 'Date.now()' : value.toString())); + } + const _this = this; + this.validators.push({ + validator: this.minValidator = function(val) { + let _value = value; + if (typeof value === 'function' && value !== Date.now) { + _value = _value.call(this); + } + const min = (_value === Date.now ? _value() : _this.cast(_value)); + return val === null || val.valueOf() >= min.valueOf(); + }, + message: msg, + type: 'min', + min: value + }); + } + + return this; +}; + +/** + * Sets a maximum date validator. + * + * #### Example: + * + * const s = new Schema({ d: { type: Date, max: Date('2014-01-01') }) + * const M = db.model('M', s) + * const m = new M({ d: Date('2014-12-08') }) + * m.save(function (err) { + * console.error(err) // validator error + * m.d = Date('2013-12-31'); + * m.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MAX} token which will be replaced with the invalid value + * const max = [Date('2014-01-01'), 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).']; + * const schema = new Schema({ d: { type: Date, max: max }) + * const M = mongoose.model('M', schema); + * const s= new M({ d: Date('2014-12-08') }); + * s.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `d` (2014-12-08) exceeds the limit (2014-01-01). + * }) + * + * @param {Date} maximum date + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaDate.prototype.max = function(value, message) { + if (this.maxValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.maxValidator; + }, this); + } + + if (value) { + let msg = message || MongooseError.messages.Date.max; + if (typeof msg === 'string') { + msg = msg.replace(/{MAX}/, (value === Date.now ? 'Date.now()' : value.toString())); + } + const _this = this; + this.validators.push({ + validator: this.maxValidator = function(val) { + let _value = value; + if (typeof _value === 'function' && _value !== Date.now) { + _value = _value.call(this); + } + const max = (_value === Date.now ? _value() : _this.cast(_value)); + return val === null || val.valueOf() <= max.valueOf(); + }, + message: msg, + type: 'max', + max: value + }); + } + + return this; +}; + +/** + * Casts to date + * + * @param {object} value to cast + * @api private + */ + +SchemaDate.prototype.cast = function(value) { + let castDate; + if (typeof this._castFunction === 'function') { + castDate = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castDate = this.constructor.cast(); + } else { + castDate = SchemaDate.cast(); + } + + try { + return castDate(value); + } catch (error) { + throw new CastError('date', value, this.path, error, this); + } +}; + +/** + * Date Query casting. + * + * @param {any} val + * @api private + */ + +function handleSingle(val) { + return this.cast(val); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$gte` is the function Mongoose calls to cast `$gte` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaDate + * @instance + * @api public + */ + +Object.defineProperty(SchemaDate.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} [value] + * @api private + */ + +SchemaDate.prototype.castForQuery = function($conditional, val, context) { + if ($conditional == null) { + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } + } + + const handler = this.$conditionalHandlers[$conditional]; + + if (!handler) { + throw new Error('Can\'t use ' + $conditional + ' with Date.'); + } + + return handler.call(this, val); +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaDate.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'date', options?.useBsonType, isRequired); +}; + +SchemaDate.prototype.autoEncryptionType = function autoEncryptionType() { + return 'date'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDate; diff --git a/backend/node_modules/mongoose/lib/schema/decimal128.js b/backend/node_modules/mongoose/lib/schema/decimal128.js new file mode 100644 index 0000000..373c371 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/decimal128.js @@ -0,0 +1,263 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const SchemaType = require('../schemaType'); +const CastError = SchemaType.CastError; +const castDecimal128 = require('../cast/decimal128'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const isBsonType = require('../helpers/isBsonType'); + +/** + * Decimal128 SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaDecimal128(key, options, _schemaOptions, parentSchema) { + SchemaType.call(this, key, options, 'Decimal128', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaDecimal128.schemaName = 'Decimal128'; + +SchemaDecimal128.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaDecimal128.prototype = Object.create(SchemaType.prototype); +SchemaDecimal128.prototype.constructor = SchemaDecimal128; + +/*! + * ignore + */ + +SchemaDecimal128._cast = castDecimal128; + +/** + * Sets a default option for all Decimal128 instances. + * + * #### Example: + * + * // Make all decimal 128s have `required` of true by default. + * mongoose.Schema.Types.Decimal128.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: mongoose.Decimal128 })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaDecimal128.set = SchemaType.set; + +SchemaDecimal128.setters = []; + +/** + * Attaches a getter for all Decimal128 instances + * + * #### Example: + * + * // Automatically convert Decimal128s to Numbers + * mongoose.Schema.Types.Decimal128.get(v => v == null ? v : Number(v)); + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaDecimal128.get = SchemaType.get; + +/** + * Get/set the function used to cast arbitrary values to decimals. + * + * #### Example: + * + * // Make Mongoose only refuse to cast numbers as decimal128 + * const original = mongoose.Schema.Types.Decimal128.cast(); + * mongoose.Decimal128.cast(v => { + * assert.ok(typeof v !== 'number'); + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Decimal128.cast(false); + * + * @param {Function} [caster] + * @return {Function} + * @function get + * @static + * @api public + */ + +SchemaDecimal128.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaDecimal128._defaultCaster = v => { + if (v != null && !isBsonType(v, 'Decimal128')) { + throw new Error(); + } + return v; +}; + +/*! + * ignore + */ + +SchemaDecimal128._checkRequired = v => isBsonType(v, 'Decimal128'); + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaDecimal128.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaDecimal128.prototype.checkRequired = function checkRequired(value, doc) { + if (SchemaType._isRef(this, value, doc, true)) { + return !!value; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaDecimal128.checkRequired(); + + return _checkRequired(value); +}; + +/** + * Casts to Decimal128 + * + * @param {object} value + * @param {object} doc + * @param {boolean} init whether this is an initialization cast + * @api private + */ + +SchemaDecimal128.prototype.cast = function(value, doc, init, prev, options) { + if (SchemaType._isRef(this, value, doc, init)) { + if (isBsonType(value, 'Decimal128')) { + return value; + } + + return this._castRef(value, doc, init, options); + } + + let castDecimal128; + if (typeof this._castFunction === 'function') { + castDecimal128 = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castDecimal128 = this.constructor.cast(); + } else { + castDecimal128 = SchemaDecimal128.cast(); + } + + try { + return castDecimal128(value); + } catch (error) { + throw new CastError('Decimal128', value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val) { + return this.cast(val); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$lte` is the function Mongoose calls to cast `$lte` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaDecimal128 + * @instance + * @api public + */ + +Object.defineProperty(SchemaDecimal128.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaDecimal128.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'decimal', options?.useBsonType, isRequired); +}; + +SchemaDecimal128.prototype.autoEncryptionType = function autoEncryptionType() { + return 'decimal'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDecimal128; diff --git a/backend/node_modules/mongoose/lib/schema/documentArray.js b/backend/node_modules/mongoose/lib/schema/documentArray.js new file mode 100644 index 0000000..31883c9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/documentArray.js @@ -0,0 +1,666 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const DocumentArrayElement = require('./documentArrayElement'); +const EventEmitter = require('events').EventEmitter; +const SchemaArray = require('./array'); +const SchemaDocumentArrayOptions = + require('../options/schemaDocumentArrayOptions'); +const SchemaType = require('../schemaType'); +const cast = require('../cast'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const discriminator = require('../helpers/model/discriminator'); +const handleIdOption = require('../helpers/schema/handleIdOption'); +const handleSpreadDoc = require('../helpers/document/handleSpreadDoc'); +const isOperator = require('../helpers/query/isOperator'); +const utils = require('../utils'); +const getConstructor = require('../helpers/discriminator/getConstructor'); +const InvalidSchemaOptionError = require('../error/invalidSchemaOption'); + +const arrayAtomicsSymbol = require('../helpers/symbols').arrayAtomicsSymbol; +const arrayPathSymbol = require('../helpers/symbols').arrayPathSymbol; +const documentArrayParent = require('../helpers/symbols').documentArrayParent; + +let MongooseDocumentArray; +let Subdocument; + +/** + * SubdocsArray SchemaType constructor + * + * @param {string} key + * @param {Schema} schema + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaArray + * @api public + */ + +function SchemaDocumentArray(key, schema, options, schemaOptions, parentSchema) { + if (schema.options?.timeseries) { + throw new InvalidSchemaOptionError(key, 'timeseries'); + } + const schemaTypeIdOption = SchemaDocumentArray.defaultOptions?._id; + if (schemaTypeIdOption != null) { + schemaOptions = schemaOptions || {}; + schemaOptions._id = schemaTypeIdOption; + } + + if (schemaOptions?._id != null) { + schema = handleIdOption(schema, schemaOptions); + } else if (options?._id != null) { + schema = handleIdOption(schema, options); + } + + const Constructor = _createConstructor(schema, options); + Constructor.prototype.$basePath = key; + Constructor.path = key; + + const $parentSchemaType = this; + const embeddedSchemaType = new DocumentArrayElement(key + '.$', schema, { + ...(schemaOptions || {}), + $parentSchemaType, + Constructor + }); + + SchemaArray.call(this, key, embeddedSchemaType, options, null, parentSchema); + + this.schema = schema; + // EmbeddedDocument schematype options + this.schemaOptions = schemaOptions || {}; + this.$isMongooseDocumentArray = true; + this.Constructor = Constructor; + + Constructor.base = schema.base; + + const fn = this.defaultValue; + + if (!('defaultValue' in this) || fn != null) { + this.default(function() { + let arr = fn.call(this); + if (arr != null && !Array.isArray(arr)) { + arr = [arr]; + } + // Leave it up to `cast()` to convert this to a documentarray + return arr; + }); + } +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaDocumentArray.schemaName = 'DocumentArray'; + +/** + * Options for all document arrays. + * + * - `castNonArrays`: `true` by default. If `false`, Mongoose will throw a CastError when a value isn't an array. If `true`, Mongoose will wrap the provided value in an array before casting. + * + * @api public + */ + +SchemaDocumentArray.options = { castNonArrays: true }; + +/*! + * Inherits from SchemaArray. + */ +SchemaDocumentArray.prototype = Object.create(SchemaArray.prototype); +SchemaDocumentArray.prototype.constructor = SchemaDocumentArray; +SchemaDocumentArray.prototype.OptionsConstructor = SchemaDocumentArrayOptions; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$size` is the function Mongoose calls to cast `$size` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaDocumentArray + * @instance + * @api public + */ + +Object.defineProperty(SchemaDocumentArray.prototype, '$conditionalHandlers', { + enumerable: false, + value: { ...SchemaArray.prototype.$conditionalHandlers } +}); + +/*! + * ignore + */ + +function _createConstructor(schema, options, baseClass) { + Subdocument || (Subdocument = require('../types/arraySubdocument')); + + // compile an embedded document for this schema + function EmbeddedDocument() { + Subdocument.apply(this, arguments); + if (this.__parentArray == null || this.__parentArray.getArrayParent() == null) { + return; + } + this.$session(this.__parentArray.getArrayParent().$session()); + } + + schema._preCompile(); + + const proto = baseClass?.prototype ?? Subdocument.prototype; + EmbeddedDocument.prototype = Object.create(proto); + EmbeddedDocument.prototype.$__setSchema(schema); + EmbeddedDocument.schema = schema; + EmbeddedDocument.prototype.constructor = EmbeddedDocument; + EmbeddedDocument.$isArraySubdocument = true; + EmbeddedDocument.events = new EventEmitter(); + EmbeddedDocument.base = schema.base; + + // apply methods + for (const i in schema.methods) { + EmbeddedDocument.prototype[i] = schema.methods[i]; + } + + // apply statics + for (const i in schema.statics) { + EmbeddedDocument[i] = schema.statics[i]; + } + + for (const i in EventEmitter.prototype) { + EmbeddedDocument[i] = EventEmitter.prototype[i]; + } + + EmbeddedDocument.options = options; + + return EmbeddedDocument; +} + +/** + * Adds a discriminator to this document array. + * + * #### Example: + * + * const shapeSchema = Schema({ name: String }, { discriminatorKey: 'kind' }); + * const schema = Schema({ shapes: [shapeSchema] }); + * + * const docArrayPath = parentSchema.path('shapes'); + * docArrayPath.discriminator('Circle', Schema({ radius: Number })); + * + * @param {string} name + * @param {Schema} schema fields to add to the schema for instances of this sub-class + * @param {object|string} [options] If string, same as `options.value`. + * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter. + * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning. + * @see discriminators https://mongoosejs.com/docs/discriminators.html + * @return {Function} the constructor Mongoose will use for creating instances of this discriminator model + * @api public + */ + +SchemaDocumentArray.prototype.discriminator = function(name, schema, options) { + if (typeof name === 'function') { + name = utils.getFunctionName(name); + } + + options = options || {}; + const tiedValue = utils.isPOJO(options) ? options.value : options; + const clone = typeof options.clone === 'boolean' ? options.clone : true; + + if (schema.instanceOfSchema && clone) { + schema = schema.clone(); + } + + schema = discriminator(this.Constructor, name, schema, tiedValue, null, null, options?.overwriteExisting); + + const EmbeddedDocument = _createConstructor(schema, null, this.Constructor); + EmbeddedDocument.baseCasterConstructor = this.Constructor; + + Object.defineProperty(EmbeddedDocument, 'name', { + value: name + }); + + this.Constructor.discriminators[name] = EmbeddedDocument; + + return this.Constructor.discriminators[name]; +}; + +/** + * Performs local validations first, then validations on each embedded doc + * + * @api public + */ + +SchemaDocumentArray.prototype.doValidate = async function doValidate(array, scope, options) { + // lazy load + MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentArray')); + + await SchemaType.prototype.doValidate.call(this, array, scope); + if (options?.updateValidator) { + return; + } + if (!utils.isMongooseDocumentArray(array)) { + array = new MongooseDocumentArray(array, this.path, scope); + } + + const promises = []; + for (let i = 0; i < array.length; ++i) { + // handle sparse arrays, do not use array.forEach which does not + // iterate over sparse elements yet reports array.length including + // them :( + let doc = array[i]; + if (doc == null) { + continue; + } + // If you set the array index directly, the doc might not yet be + // a full fledged mongoose subdoc, so make it into one. + if (!(doc instanceof Subdocument)) { + const Constructor = getConstructor(this.Constructor, array[i]); + doc = array[i] = new Constructor(doc, array, undefined, undefined, i); + } + + if (options?.validateModifiedOnly && !doc.$isModified()) { + continue; + } + + promises.push(doc.validate(null, options)); + } + + await Promise.all(promises); +}; + +/** + * Performs local validations first, then validations on each embedded doc. + * + * #### Note: + * + * This method ignores the asynchronous validators. + * + * @return {MongooseError|undefined} + * @api private + */ + +SchemaDocumentArray.prototype.doValidateSync = function(array, scope, options) { + const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, array, scope); + if (schemaTypeError != null) { + return schemaTypeError; + } + + const count = array?.length; + let resultError = null; + + if (!count) { + return; + } + + // handle sparse arrays, do not use array.forEach which does not + // iterate over sparse elements yet reports array.length including + // them :( + + for (let i = 0, len = count; i < len; ++i) { + // sidestep sparse entries + let doc = array[i]; + if (!doc) { + continue; + } + + // If you set the array index directly, the doc might not yet be + // a full fledged mongoose subdoc, so make it into one. + if (!(doc instanceof Subdocument)) { + const Constructor = getConstructor(this.Constructor, array[i]); + doc = array[i] = new Constructor(doc, array, undefined, undefined, i); + } + + if (options?.validateModifiedOnly && !doc.$isModified()) { + continue; + } + + const subdocValidateError = doc.validateSync(options); + + if (subdocValidateError && resultError == null) { + resultError = subdocValidateError; + } + } + + return resultError; +}; + +/*! + * ignore + */ + +SchemaDocumentArray.prototype.getDefault = function(scope, init, options) { + let ret = typeof this.defaultValue === 'function' + ? this.defaultValue.call(scope) + : this.defaultValue; + + if (ret == null) { + return ret; + } + + if (options?.skipCast) { + return ret; + } + + // lazy load + MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentArray')); + + if (!Array.isArray(ret)) { + ret = [ret]; + } + + ret = new MongooseDocumentArray(ret, this.path, scope); + + for (let i = 0; i < ret.length; ++i) { + const Constructor = getConstructor(this.Constructor, ret[i]); + const _subdoc = new Constructor({}, ret, undefined, + undefined, i); + _subdoc.$init(ret[i]); + _subdoc.isNew = true; + + // Make sure all paths in the subdoc are set to `default` instead + // of `init` since we used `init`. + Object.assign(_subdoc.$__.activePaths.default, _subdoc.$__.activePaths.init); + _subdoc.$__.activePaths.init = {}; + + ret[i] = _subdoc; + } + + return ret; +}; + +const _toObjectOptions = Object.freeze({ transform: false, virtuals: false }); +const initDocumentOptions = Object.freeze({ skipId: false, willInit: true }); + +/** + * Casts contents + * + * @param {object} value + * @param {Document} document that triggers the casting + * @api private + */ + +SchemaDocumentArray.prototype.cast = function(value, doc, init, prev, options) { + // lazy load + MongooseDocumentArray || (MongooseDocumentArray = require('../types/documentArray')); + + // Skip casting if `value` is the same as the previous value, no need to cast. See gh-9266 + if (value?.[arrayPathSymbol] != null && value === prev) { + return value; + } + + let selected; + let subdoc; + + options = options || {}; + + const path = options.path || this.path; + + if (!Array.isArray(value)) { + if (!init && !SchemaDocumentArray.options.castNonArrays) { + throw new CastError('DocumentArray', value, this.path, null, this); + } + // gh-2442 mark whole array as modified if we're initializing a doc from + // the db and the path isn't an array in the document + if (!!doc && init) { + doc.markModified(path); + } + return this.cast([value], doc, init, prev, options); + } + + // We need to create a new array, otherwise change tracking will + // update the old doc (gh-4449) + if (!options.skipDocumentArrayCast || utils.isMongooseDocumentArray(value)) { + value = new MongooseDocumentArray(value, path, doc, this); + } + + if (prev != null) { + value[arrayAtomicsSymbol] = prev[arrayAtomicsSymbol] || {}; + } + + if (options.arrayPathIndex != null) { + value[arrayPathSymbol] = path + '.' + options.arrayPathIndex; + } + + const rawArray = utils.isMongooseDocumentArray(value) ? value.__array : value; + const len = rawArray.length; + + for (let i = 0; i < len; ++i) { + if (!rawArray[i]) { + continue; + } + + const Constructor = getConstructor(this.Constructor, rawArray[i]); + + const spreadDoc = handleSpreadDoc(rawArray[i], true); + if (rawArray[i] !== spreadDoc) { + rawArray[i] = spreadDoc; + } + + if (rawArray[i] instanceof Subdocument) { + // gh-15973: Clone if subdocument has a different parent, or if it already has a + // different __index (e.g. reordered or duplicated in the array). If __index is null, + // we can safely set it without cloning. + if (rawArray[i][documentArrayParent] !== doc || + (rawArray[i].__index != null && rawArray[i].__index !== i)) { + if (init) { + const subdoc = new Constructor(null, value, initDocumentOptions, selected, i); + rawArray[i] = subdoc.$init(rawArray[i]); + } else { + const subdoc = new Constructor(rawArray[i], value, undefined, undefined, i); + rawArray[i] = subdoc; + } + } + // Might not have the correct index yet, so ensure it does. + if (rawArray[i].__index == null) { + rawArray[i].$setIndex(i); + } + } else if (rawArray[i] != null) { + if (init) { + if (doc) { + selected || (selected = scopePaths(this, doc.$__.selected, init)); + } else { + selected = true; + } + + subdoc = new Constructor(null, value, initDocumentOptions, selected, i); + // Don't pass `path` to $init - it's only for this DocumentArray itself, not its element fields. + // Element subdocuments use relative paths internally for change tracking. + if (options.path != null) { + options = { ...options }; + delete options.path; + } + rawArray[i] = subdoc.$init(rawArray[i], options); + } else { + if (typeof prev?.id === 'function') { + subdoc = prev.id(rawArray[i]._id); + } + + if (prev && subdoc && utils.deepEqual(subdoc.toObject(_toObjectOptions), rawArray[i])) { + // handle resetting doc with existing id and same data + subdoc.set(rawArray[i]); + // if set() is hooked it will have no return value + // see gh-746 + rawArray[i] = subdoc; + } else { + try { + subdoc = new Constructor(rawArray[i], value, undefined, + undefined, i); + // if set() is hooked it will have no return value + // see gh-746 + rawArray[i] = subdoc; + } catch (error) { + throw new CastError('embedded', rawArray[i], + value[arrayPathSymbol], error, this); + } + } + } + } + } + + return value; +}; + +/*! + * ignore + */ + +SchemaDocumentArray.prototype.clone = function() { + const options = Object.assign({}, this.options); + const schematype = new this.constructor( + this.path, + this.schema, + options, + this.schemaOptions, + this.parentSchema + ); + schematype.validators = this.validators.slice(); + if (this.requiredValidator !== undefined) { + schematype.requiredValidator = this.requiredValidator; + } + schematype.Constructor.discriminators = Object.assign({}, + this.Constructor.discriminators); + schematype._appliedDiscriminators = this._appliedDiscriminators; + return schematype; +}; + +/*! + * ignore + */ + +SchemaDocumentArray.prototype.applyGetters = function(value, scope) { + return SchemaType.prototype.applyGetters.call(this, value, scope); +}; + +/** + * Scopes paths selected in a query to this array. + * Necessary for proper default application of subdocument values. + * + * @param {DocumentArrayPath} array the array to scope `fields` paths + * @param {object|undefined} fields the root fields selected in the query + * @param {boolean|undefined} init if we are being created part of a query result + * @api private + */ + +function scopePaths(array, fields, init) { + if (!(init && fields)) { + return undefined; + } + + const path = array.path + '.'; + const keys = Object.keys(fields); + let i = keys.length; + const selected = {}; + let hasKeys; + let key; + let sub; + + while (i--) { + key = keys[i]; + if (key.startsWith(path)) { + sub = key.substring(path.length); + if (sub === '$') { + continue; + } + if (sub.startsWith('$.')) { + sub = sub.substring(2); + } + hasKeys || (hasKeys = true); + selected[sub] = fields[key]; + } + } + + return hasKeys && selected || undefined; +} + +/*! + * ignore + */ + +SchemaDocumentArray.defaultOptions = {}; + +/** + * Sets a default option for all DocumentArray instances. + * + * #### Example: + * + * // Make all document arrays not have `_id` by default. + * mongoose.Schema.Types.DocumentArray.set('_id', false); + * + * @param {string} option The name of the option you'd like to set (e.g. trim, lowercase, etc...) + * @param {any} value The value of the option you'd like to set. + * @return {void} + * @function set + * @static + * @api public + */ + +SchemaDocumentArray.set = SchemaType.set; + +SchemaDocumentArray.setters = []; + +/** + * Attaches a getter for all DocumentArrayPath instances + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaDocumentArray.get = SchemaType.get; + +/*! + * Handle casting $elemMatch operators + */ + +SchemaDocumentArray.prototype.$conditionalHandlers.$elemMatch = cast$elemMatch; + +function cast$elemMatch(val, context) { + const keys = Object.keys(val); + const numKeys = keys.length; + for (let i = 0; i < numKeys; ++i) { + const key = keys[i]; + const value = val[key]; + if (isOperator(key) && value != null) { + val[key] = this.castForQuery(key, value, context); + } + } + + // Is this an embedded discriminator and is the discriminator key set? + // If so, use the discriminator schema. See gh-7449 + const discriminatorKey = this?.Constructor?.schema?.options?.discriminatorKey; + const discriminators = this?.Constructor?.schema?.discriminators || {}; + if (discriminatorKey != null && + val[discriminatorKey] != null && + discriminators[val[discriminatorKey]] != null) { + return cast(discriminators[val[discriminatorKey]], val, null, this?.$$context); + } + + const schema = this.Constructor.schema ?? context.schema; + return cast(schema, val, null, this?.$$context); +} + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaDocumentArray.prototype.toJSONSchema = function toJSONSchema(options) { + const itemsTypeDefinition = createJSONSchemaTypeDefinition('object', 'object', options?.useBsonType, false); + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return { + ...createJSONSchemaTypeDefinition('array', 'array', options?.useBsonType, isRequired), + items: { ...itemsTypeDefinition, ...this.schema.toJSONSchema(options) } + }; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDocumentArray; diff --git a/backend/node_modules/mongoose/lib/schema/documentArrayElement.js b/backend/node_modules/mongoose/lib/schema/documentArrayElement.js new file mode 100644 index 0000000..eeda344 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/documentArrayElement.js @@ -0,0 +1,136 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const MongooseError = require('../error/mongooseError'); +const SchemaType = require('../schemaType'); +const SchemaSubdocument = require('./subdocument'); +const getConstructor = require('../helpers/discriminator/getConstructor'); + +/** + * DocumentArrayElement SchemaType constructor. Mongoose calls this internally when you define a new document array in your schema. + * + * #### Example: + * const schema = new Schema({ users: [{ name: String }] }); + * schema.path('users.$'); // SchemaDocumentArrayElement with schema `new Schema({ name: String })` + * + * @param {string} path + * @param {Schema} schema + * @param {object} options + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaDocumentArrayElement(path, schema, options, parentSchema) { + this.$parentSchemaType = options?.$parentSchemaType; + if (!this.$parentSchemaType) { + throw new MongooseError('Cannot create DocumentArrayElement schematype without a parent'); + } + delete options.$parentSchemaType; + + SchemaType.call(this, path, options, 'DocumentArrayElement', parentSchema); + + this.$isMongooseDocumentArrayElement = true; + this.Constructor = options?.Constructor; + this.schema = schema; +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaDocumentArrayElement.schemaName = 'DocumentArrayElement'; + +SchemaDocumentArrayElement.defaultOptions = {}; + +/** + * Sets a default option for all SchemaDocumentArrayElement instances. + * + * #### Example: + * + * // Make all document array elements have option `_id` equal to false. + * mongoose.Schema.Types.DocumentArrayElement.set('_id', false); + * + * @param {string} option The name of the option you'd like to set + * @param {any} value The value of the option you'd like to set. + * @return {void} + * @function set + * @static + * @api public + */ + +SchemaDocumentArrayElement.set = SchemaType.set; + +/** + * Attaches a getter for all DocumentArrayElement instances + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaDocumentArrayElement.get = SchemaType.get; + +/*! + * Inherits from SchemaType. + */ +SchemaDocumentArrayElement.prototype = Object.create(SchemaType.prototype); +SchemaDocumentArrayElement.prototype.constructor = SchemaDocumentArrayElement; + +/** + * Casts `val` for DocumentArrayElement. + * + * @param {object} value to cast + * @api private + */ + +SchemaDocumentArrayElement.prototype.cast = function(...args) { + return this.$parentSchemaType.cast(...args)[0]; +}; + +/** + * Async validation on this individual array element + * + * @api public + */ + +SchemaDocumentArrayElement.prototype.doValidate = async function doValidate(value, scope, options) { + const Constructor = getConstructor(this.Constructor, value); + + if (value && !(value instanceof Constructor)) { + value = new Constructor(value, scope, null, null, options?.index ?? null); + } + + return SchemaSubdocument.prototype.doValidate.call(this, value, scope, options); +}; + +/** + * Clone the current SchemaType + * + * @return {DocumentArrayElement} The cloned instance + * @api private + */ + +SchemaDocumentArrayElement.prototype.clone = function() { + this.options.$parentSchemaType = this.$parentSchemaType; + const ret = SchemaType.prototype.clone.apply(this, arguments); + delete this.options.$parentSchemaType; + + ret.Constructor = this.Constructor; + ret.schema = this.schema; + + return ret; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDocumentArrayElement; diff --git a/backend/node_modules/mongoose/lib/schema/double.js b/backend/node_modules/mongoose/lib/schema/double.js new file mode 100644 index 0000000..9674214 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/double.js @@ -0,0 +1,246 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const SchemaType = require('../schemaType'); +const castDouble = require('../cast/double'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); + +/** + * Double SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaDouble(path, options, _schemaOptions, parentSchema) { + SchemaType.call(this, path, options, 'Double', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaDouble.schemaName = 'Double'; + +SchemaDouble.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaDouble.prototype = Object.create(SchemaType.prototype); +SchemaDouble.prototype.constructor = SchemaDouble; + +/*! + * ignore + */ + +SchemaDouble._cast = castDouble; + +/** + * Sets a default option for all Double instances. + * + * #### Example: + * + * // Make all Double fields required by default + * mongoose.Schema.Types.Double.set('required', true); + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaDouble.set = SchemaType.set; + +SchemaDouble.setters = []; + +/** + * Attaches a getter for all Double instances + * + * #### Example: + * + * // Converts Double to be a represent milliseconds upon access + * mongoose.Schema.Types.Double.get(v => v == null ? '0.000 ms' : v.toString() + ' ms'); + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaDouble.get = SchemaType.get; + +/*! + * ignore + */ + +SchemaDouble._defaultCaster = v => { + if (v != null) { + if (v._bsontype !== 'Double') { + throw new Error(); + } + } + + return v; +}; + +/** + * Get/set the function used to cast arbitrary values to IEEE 754-2008 floating points + * + * #### Example: + * + * // Make Mongoose cast any NaNs to 0 + * const defaultCast = mongoose.Schema.Types.Double.cast(); + * mongoose.Schema.Types.Double.cast(v => { + * if (isNaN(v)) { + * return 0; + * } + * return defaultCast(v); + * }); + * + * // Or disable casting for Doubles entirely (only JS numbers are permitted) + * mongoose.Schema.Types.Double.cast(false); + * + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaDouble.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + + this._cast = caster; + + return this._cast; +}; + + +/*! + * ignore + */ + +SchemaDouble._checkRequired = v => v != null; +/** + * Override the function the required validator uses to check whether a value + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaDouble.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @return {boolean} + * @api public + */ + +SchemaDouble.prototype.checkRequired = function(value) { + return this.constructor._checkRequired(value); +}; + +/** + * Casts to Double + * + * @param {object} value + * @param {object} model this value is optional + * @api private + */ + +SchemaDouble.prototype.cast = function(value) { + let castDouble; + if (typeof this._castFunction === 'function') { + castDouble = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castDouble = this.constructor.cast(); + } else { + castDouble = SchemaDouble.cast(); + } + + try { + return castDouble(value); + } catch (error) { + throw new CastError('Double', value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val) { + return this.cast(val); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$lt` is the function Mongoose calls to cast `$lt` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaDouble + * @instance + * @api public + */ + +Object.defineProperty(SchemaDouble.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaDouble.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('number', 'double', options?.useBsonType, isRequired); +}; + +SchemaDouble.prototype.autoEncryptionType = function autoEncryptionType() { + return 'double'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaDouble; diff --git a/backend/node_modules/mongoose/lib/schema/index.js b/backend/node_modules/mongoose/lib/schema/index.js new file mode 100644 index 0000000..23aee05 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/index.js @@ -0,0 +1,32 @@ + +/*! + * Module exports. + */ + +'use strict'; + +exports.Array = require('./array'); +exports.BigInt = require('./bigint'); +exports.Boolean = require('./boolean'); +exports.Buffer = require('./buffer'); +exports.Date = require('./date'); +exports.Decimal128 = exports.Decimal = require('./decimal128'); +exports.DocumentArray = require('./documentArray'); +exports.DocumentArrayElement = require('./documentArrayElement'); +exports.Double = require('./double'); +exports.Int32 = require('./int32'); +exports.Map = require('./map'); +exports.Mixed = require('./mixed'); +exports.Number = require('./number'); +exports.ObjectId = require('./objectId'); +exports.String = require('./string'); +exports.Subdocument = require('./subdocument'); +exports.UUID = require('./uuid'); +exports.Union = require('./union'); + +// alias + +exports.Oid = exports.ObjectId; +exports.Object = exports.Mixed; +exports.Bool = exports.Boolean; +exports.ObjectID = exports.ObjectId; diff --git a/backend/node_modules/mongoose/lib/schema/int32.js b/backend/node_modules/mongoose/lib/schema/int32.js new file mode 100644 index 0000000..06ade66 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/int32.js @@ -0,0 +1,289 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const SchemaType = require('../schemaType'); +const castInt32 = require('../cast/int32'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const handleBitwiseOperator = require('./operators/bitwise'); + +/** + * Int32 SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaInt32(path, options, _schemaOptions, parentSchema) { + SchemaType.call(this, path, options, 'Int32', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaInt32.schemaName = 'Int32'; + +SchemaInt32.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaInt32.prototype = Object.create(SchemaType.prototype); +SchemaInt32.prototype.constructor = SchemaInt32; + +/*! + * ignore + */ + +SchemaInt32._cast = castInt32; + +/** + * Sets a default option for all Int32 instances. + * + * #### Example: + * + * // Make all Int32 fields required by default + * mongoose.Schema.Types.Int32.set('required', true); + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaInt32.set = SchemaType.set; + +SchemaInt32.setters = []; + +/** + * Attaches a getter for all Int32 instances + * + * #### Example: + * + * // Converts int32 to be a represent milliseconds upon access + * mongoose.Schema.Types.Int32.get(v => v == null ? '0 ms' : v.toString() + ' ms'); + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaInt32.get = SchemaType.get; + +/*! + * ignore + */ + +SchemaInt32._defaultCaster = v => { + const INT32_MAX = 0x7FFFFFFF; + const INT32_MIN = -0x80000000; + + if (v != null) { + if (typeof v !== 'number' || v !== (v | 0) || v < INT32_MIN || v > INT32_MAX) { + throw new Error(); + } + } + + return v; +}; + +/** + * Get/set the function used to cast arbitrary values to 32-bit integers + * + * #### Example: + * + * // Make Mongoose cast NaN to 0 + * const defaultCast = mongoose.Schema.Types.Int32.cast(); + * mongoose.Schema.Types.Int32.cast(v => { + * if (isNaN(v)) { + * return 0; + * } + * return defaultCast(v); + * }); + * + * // Or disable casting for Int32s entirely (only JS numbers within 32-bit integer bounds and null-ish values are permitted) + * mongoose.Schema.Types.Int32.cast(false); + * + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaInt32.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + + this._cast = caster; + + return this._cast; +}; + + +/*! + * ignore + */ + +SchemaInt32._checkRequired = v => v != null; +/** + * Override the function the required validator uses to check whether a value + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaInt32.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @return {boolean} + * @api public + */ + +SchemaInt32.prototype.checkRequired = function(value) { + return this.constructor._checkRequired(value); +}; + +/** + * Casts to Int32 + * + * @param {object} value + * @param {object} model this value is optional + * @api private + */ + +SchemaInt32.prototype.cast = function(value) { + let castInt32; + if (typeof this._castFunction === 'function') { + castInt32 = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castInt32 = this.constructor.cast(); + } else { + castInt32 = SchemaInt32.cast(); + } + + try { + return castInt32(value); + } catch (error) { + throw new CastError('Int32', value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle, + $bitsAllClear: handleBitwiseOperator, + $bitsAnyClear: handleBitwiseOperator, + $bitsAllSet: handleBitwiseOperator, + $bitsAnySet: handleBitwiseOperator +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$gt` is the function Mongoose calls to cast `$gt` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaInt32 + * @instance + * @api public + */ + +Object.defineProperty(SchemaInt32.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/*! + * ignore + */ + +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} val + * @api private + */ + +SchemaInt32.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + + if (handler) { + return handler.call(this, val); + } + + return this.applySetters(val, context); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaInt32.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('number', 'int', options?.useBsonType, isRequired); +}; + +SchemaInt32.prototype.autoEncryptionType = function autoEncryptionType() { + return 'int'; +}; + + +/*! + * Module exports. + */ + +module.exports = SchemaInt32; diff --git a/backend/node_modules/mongoose/lib/schema/map.js b/backend/node_modules/mongoose/lib/schema/map.js new file mode 100644 index 0000000..7fff0be --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/map.js @@ -0,0 +1,197 @@ +'use strict'; + +/*! + * ignore + */ + +const MongooseMap = require('../types/map'); +const SchemaMapOptions = require('../options/schemaMapOptions'); +const SchemaType = require('../schemaType'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const MongooseError = require('../error/mongooseError'); +const Schema = require('../schema'); +const utils = require('../utils'); + +class SchemaMap extends SchemaType { + /** + * Map SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + + constructor(key, options, schemaOptions, parentSchema) { + super(key, options, 'Map', parentSchema); + this.$isSchemaMap = true; + // Create the nested schema type for the map values + this._createNestedSchemaType(parentSchema, key, options, schemaOptions); + } + + /** + * Sets a default option for all Map instances. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @api public + */ + + set(option, value) { + return SchemaType.set(option, value); + } + + /** + * Casts to Map + * + * @param {object} value + * @param {object} model this value is optional + * @api private + */ + + cast(val, doc, init, prev, options) { + if (val instanceof MongooseMap) { + return val; + } + + const path = options?.path ?? this.path; + + if (init) { + const map = new MongooseMap({}, path, doc, this.$__schemaType); + + if (val instanceof global.Map) { + for (const key of val.keys()) { + let _val = val.get(key); + if (_val == null) { + _val = map.$__schemaType._castNullish(_val); + } else { + _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: path + '.' + key }); + } + map.$init(key, _val); + } + } else { + for (const key of Object.keys(val)) { + let _val = val[key]; + if (_val == null) { + _val = map.$__schemaType._castNullish(_val); + } else { + _val = map.$__schemaType.cast(_val, doc, true, null, { ...options, path: path + '.' + key }); + } + map.$init(key, _val); + } + } + + return map; + } + + return new MongooseMap(val, path, doc, this.$__schemaType); + } + + /** + * Creates a copy of this map schema type. + * + * @api private + */ + + clone() { + const schematype = super.clone(); + + if (this.$__schemaType != null) { + schematype.$__schemaType = this.$__schemaType.clone(); + } + return schematype; + } + + /** + * Returns the embedded schema type (i.e. the `.$*` path) + * + * @api public + */ + + getEmbeddedSchemaType() { + return this.$__schemaType; + } + + /** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + + toJSONSchema(options) { + const useBsonType = options?.useBsonType; + const embeddedSchemaType = this.getEmbeddedSchemaType(); + + const isRequired = this.options.required && typeof this.options.required !== 'function'; + const result = createJSONSchemaTypeDefinition('object', 'object', useBsonType, isRequired); + result.additionalProperties = embeddedSchemaType.toJSONSchema(options); + + return result; + } + + /** + * Returns the auto encryption type for this schema type. + * + * @api public + */ + + autoEncryptionType() { + return 'object'; + } +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ + +SchemaMap.schemaName = 'Map'; + +SchemaMap.prototype.OptionsConstructor = SchemaMapOptions; + +SchemaMap.defaultOptions = {}; + +/*! + * ignore + */ + +SchemaMap.prototype._createNestedSchemaType = function _createNestedSchemaType(schema, path, obj, options) { + const mapPath = path + '.$*'; + let _mapType = { type: {} }; + if (utils.hasUserDefinedProperty(obj, 'of')) { + const isInlineSchema = utils.isPOJO(obj.of) && + utils.hasOwnKeys(obj.of) && + !utils.hasUserDefinedProperty(obj.of, schema.options.typeKey); + if (isInlineSchema) { + _mapType = { [schema.options.typeKey]: new Schema(obj.of) }; + } else if (utils.isPOJO(obj.of)) { + _mapType = Object.assign({}, obj.of); + } else { + _mapType = { [schema.options.typeKey]: obj.of }; + } + + if (_mapType[schema.options.typeKey] && _mapType[schema.options.typeKey].instanceOfSchema) { + const subdocumentSchema = _mapType[schema.options.typeKey]; + subdocumentSchema.eachPath((subpath, type) => { + if (type.options.select === true || type.options.select === false) { + throw new MongooseError('Cannot use schema-level projections (`select: true` or `select: false`) within maps at path "' + path + '.' + subpath + '"'); + } + }); + } + + if (utils.hasUserDefinedProperty(obj, 'ref')) { + _mapType.ref = obj.ref; + } + } + this.$__schemaType = schema.interpretAsType(mapPath, _mapType, options); +}; + +module.exports = SchemaMap; diff --git a/backend/node_modules/mongoose/lib/schema/mixed.js b/backend/node_modules/mongoose/lib/schema/mixed.js new file mode 100644 index 0000000..7445e64 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/mixed.js @@ -0,0 +1,146 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const SchemaType = require('../schemaType'); +const symbols = require('./symbols'); +const isObject = require('../helpers/isObject'); +const utils = require('../utils'); + +/** + * Mixed SchemaType constructor. + * + * @param {string} path + * @param {object} options + * @param {object} _schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaMixed(path, options, _schemaOptions, parentSchema) { + if (options?.default) { + const def = options.default; + if (Array.isArray(def) && def.length === 0) { + // make sure empty array defaults are handled + options.default = Array; + } else if (!options.shared && isObject(def) && utils.hasOwnKeys(def) === false) { + // prevent odd "shared" objects between documents + options.default = function() { + return {}; + }; + } + } + + SchemaType.call(this, path, options, 'Mixed', parentSchema); + + this[symbols.schemaMixedSymbol] = true; +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaMixed.schemaName = 'Mixed'; + +SchemaMixed.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaMixed.prototype = Object.create(SchemaType.prototype); +SchemaMixed.prototype.constructor = SchemaMixed; + +/** + * Attaches a getter for all Mixed paths. + * + * #### Example: + * + * // Hide the 'hidden' path + * mongoose.Schema.Types.Mixed.get(v => Object.assign({}, v, { hidden: null })); + * + * const Model = mongoose.model('Test', new Schema({ test: {} })); + * new Model({ test: { hidden: 'Secret!' } }).test.hidden; // null + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaMixed.get = SchemaType.get; + +/** + * Sets a default option for all Mixed instances. + * + * #### Example: + * + * // Make all mixed instances have `required` of true by default. + * mongoose.Schema.Types.Mixed.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: mongoose.Mixed })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaMixed.set = SchemaType.set; + +SchemaMixed.setters = []; + +/** + * Casts `val` for Mixed. + * + * _this is a no-op_ + * + * @param {object} value to cast + * @api private + */ + +SchemaMixed.prototype.cast = function(val) { + if (val instanceof Error) { + return utils.errorToPOJO(val); + } + return val; +}; + +/** + * Casts contents for queries. + * + * @param {string} $cond + * @param {any} [val] + * @api private + */ + +SchemaMixed.prototype.castForQuery = function($cond, val) { + return val; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +// eslint-disable-next-line no-unused-vars +SchemaMixed.prototype.toJSONSchema = function toJSONSchema(_options) { + return {}; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaMixed; diff --git a/backend/node_modules/mongoose/lib/schema/number.js b/backend/node_modules/mongoose/lib/schema/number.js new file mode 100644 index 0000000..1f4d231 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/number.js @@ -0,0 +1,510 @@ +'use strict'; + +/*! + * Module requirements. + */ + +const MongooseError = require('../error/index'); +const SchemaNumberOptions = require('../options/schemaNumberOptions'); +const SchemaType = require('../schemaType'); +const castNumber = require('../cast/number'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const handleBitwiseOperator = require('./operators/bitwise'); +const utils = require('../utils'); + +const CastError = SchemaType.CastError; + +/** + * Number SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaNumber(key, options, _schemaOptions, parentSchema) { + this.enumValues = []; + SchemaType.call(this, key, options, 'Number', parentSchema); +} + +/** + * Attaches a getter for all Number instances. + * + * #### Example: + * + * // Make all numbers round down + * mongoose.Number.get(function(v) { return Math.floor(v); }); + * + * const Model = mongoose.model('Test', new Schema({ test: Number })); + * new Model({ test: 3.14 }).test; // 3 + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaNumber.get = SchemaType.get; + +/** + * Sets a default option for all Number instances. + * + * #### Example: + * + * // Make all numbers have option `min` equal to 0. + * mongoose.Schema.Types.Number.set('min', 0); + * + * const Order = mongoose.model('Order', new Schema({ amount: Number })); + * new Order({ amount: -10 }).validateSync().errors.amount.message; // Path `amount` must be larger than 0. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaNumber.set = SchemaType.set; + +SchemaNumber.setters = []; + +/*! + * ignore + */ + +SchemaNumber._cast = castNumber; + +/** + * Get/set the function used to cast arbitrary values to numbers. + * + * #### Example: + * + * // Make Mongoose cast empty strings '' to 0 for paths declared as numbers + * const original = mongoose.Number.cast(); + * mongoose.Number.cast(v => { + * if (v === '') { return 0; } + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Number.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaNumber.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaNumber._defaultCaster = v => { + if (typeof v !== 'number') { + throw new Error(); + } + return v; +}; + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaNumber.schemaName = 'Number'; + +SchemaNumber.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaNumber.prototype = Object.create(SchemaType.prototype); +SchemaNumber.prototype.constructor = SchemaNumber; +SchemaNumber.prototype.OptionsConstructor = SchemaNumberOptions; + +/*! + * ignore + */ + +SchemaNumber._checkRequired = v => typeof v === 'number' || v instanceof Number; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaNumber.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaNumber.prototype.checkRequired = function checkRequired(value, doc) { + if (typeof value === 'object' && SchemaType._isRef(this, value, doc, true)) { + return value != null; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaNumber.checkRequired(); + + return _checkRequired(value); +}; + +/** + * Sets a minimum number validator. + * + * #### Example: + * + * const s = new Schema({ n: { type: Number, min: 10 }) + * const M = db.model('M', s) + * const m = new M({ n: 9 }) + * m.save(function (err) { + * console.error(err) // validator error + * m.n = 10; + * m.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MIN} token which will be replaced with the invalid value + * const min = [10, 'The value of path `{PATH}` ({VALUE}) is beneath the limit ({MIN}).']; + * const schema = new Schema({ n: { type: Number, min: min }) + * const M = mongoose.model('Measurement', schema); + * const s= new M({ n: 4 }); + * s.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `n` (4) is beneath the limit (10). + * }) + * + * @param {number} value minimum number + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaNumber.prototype.min = function(value, message) { + if (this.minValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.minValidator; + }, this); + } + + if (value != null) { + let msg = message || MongooseError.messages.Number.min; + msg = msg.replace(/{MIN}/, value); + this.validators.push({ + validator: this.minValidator = function(v) { + return v == null || v >= value; + }, + message: msg, + type: 'min', + min: value + }); + } + + return this; +}; + +/** + * Sets a maximum number validator. + * + * #### Example: + * + * const s = new Schema({ n: { type: Number, max: 10 }) + * const M = db.model('M', s) + * const m = new M({ n: 11 }) + * m.save(function (err) { + * console.error(err) // validator error + * m.n = 10; + * m.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MAX} token which will be replaced with the invalid value + * const max = [10, 'The value of path `{PATH}` ({VALUE}) exceeds the limit ({MAX}).']; + * const schema = new Schema({ n: { type: Number, max: max }) + * const M = mongoose.model('Measurement', schema); + * const s= new M({ n: 4 }); + * s.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `n` (4) exceeds the limit (10). + * }) + * + * @param {number} maximum number + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaNumber.prototype.max = function(value, message) { + if (this.maxValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.maxValidator; + }, this); + } + + if (value != null) { + let msg = message || MongooseError.messages.Number.max; + msg = msg.replace(/{MAX}/, value); + this.validators.push({ + validator: this.maxValidator = function(v) { + return v == null || v <= value; + }, + message: msg, + type: 'max', + max: value + }); + } + + return this; +}; + +/** + * Sets a enum validator + * + * #### Example: + * + * const s = new Schema({ n: { type: Number, enum: [1, 2, 3] }); + * const M = db.model('M', s); + * + * const m = new M({ n: 4 }); + * await m.save(); // throws validation error + * + * m.n = 3; + * await m.save(); // succeeds + * + * @param {Array} values allowed values + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaNumber.prototype.enum = function(values, message) { + if (this.enumValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.enumValidator; + }, this); + this.enumValidator = false; + } + + if (values === void 0 || values === false) { + return this; + } + + if (!Array.isArray(values)) { + const isObjectSyntax = utils.isPOJO(values) && values.values != null; + if (isObjectSyntax) { + message = values.message; + values = values.values; + } else if (typeof values === 'number') { + values = Array.prototype.slice.call(arguments); + message = null; + } + + if (utils.isPOJO(values)) { + // TypeScript numeric enums produce objects with reverse + // mappings, e.g. { 0: 'Zero', 1: 'One', Zero: 0, One: 1 }. + // Object.values on that will yield ['Zero','One',0,1]. + // For Number schema enums we only want the numeric values, + // otherwise casting the name strings to Number will throw. + const keys = Object.keys(values).sort(); + const objVals = Object.values(values).sort(); + // If keys and values are equal and half of values are numbers, + // this is likely a TS enum with reverse mapping, so use only the numbers. + if ( + keys.length === objVals.length && + keys.every((k, i) => k === String(objVals[i])) && + objVals.filter(v => typeof v === 'number').length === Math.floor(objVals.length / 2) + ) { + values = objVals.filter(v => typeof v === 'number'); + } else { + // Avoid sorting the values to preserve user-specified order + values = Object.values(values); + } + } + message = message || MongooseError.messages.Number.enum; + } + + message = message == null ? MongooseError.messages.Number.enum : message; + + for (const value of values) { + if (value !== undefined) { + this.enumValues.push(this.cast(value)); + } + } + + const vals = this.enumValues; + this.enumValidator = v => v == null || vals.indexOf(v) !== -1; + this.validators.push({ + validator: this.enumValidator, + message: message, + type: 'enum', + enumValues: vals + }); + + return this; +}; + +/** + * Casts to number + * + * @param {object} value value to cast + * @param {Document} doc document that triggers the casting + * @param {boolean} init + * @api private + */ + +SchemaNumber.prototype.cast = function(value, doc, init, prev, options) { + if (typeof value !== 'number' && SchemaType._isRef(this, value, doc, init)) { + if (value == null || utils.isNonBuiltinObject(value)) { + return this._castRef(value, doc, init, options); + } + } + + const val = value?._id !== undefined ? + value._id : // documents + value; + + let castNumber; + if (typeof this._castFunction === 'function') { + castNumber = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castNumber = this.constructor.cast(); + } else { + castNumber = SchemaNumber.cast(); + } + + try { + return castNumber(val); + } catch (err) { + throw new CastError('Number', val, this.path, err, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val) { + return this.cast(val); +} + +function handleArray(val) { + const _this = this; + if (!Array.isArray(val)) { + return [this.cast(val)]; + } + return val.map(function(m) { + return _this.cast(m); + }); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $bitsAllClear: handleBitwiseOperator, + $bitsAnyClear: handleBitwiseOperator, + $bitsAllSet: handleBitwiseOperator, + $bitsAnySet: handleBitwiseOperator, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle, + $mod: handleArray +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$gte` is the function Mongoose calls to cast `$gte` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaNumber + * @instance + * @api public + */ + +Object.defineProperty(SchemaNumber.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} [value] + * @api private + */ + +SchemaNumber.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new CastError('number', val, this.path, null, this); + } + return handler.call(this, val, context); + } + + try { + val = this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } + + return val; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaNumber.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = (this.options.required && typeof this.options.required !== 'function') || this.path === '_id'; + return createJSONSchemaTypeDefinition('number', 'number', options?.useBsonType, isRequired); +}; + +/*! + * Module exports. + */ + +module.exports = SchemaNumber; diff --git a/backend/node_modules/mongoose/lib/schema/objectId.js b/backend/node_modules/mongoose/lib/schema/objectId.js new file mode 100644 index 0000000..6dea2ac --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/objectId.js @@ -0,0 +1,333 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const SchemaObjectIdOptions = require('../options/schemaObjectIdOptions'); +const SchemaType = require('../schemaType'); +const castObjectId = require('../cast/objectid'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const getConstructorName = require('../helpers/getConstructorName'); +const oid = require('../types/objectid'); +const isBsonType = require('../helpers/isBsonType'); +const utils = require('../utils'); + +const CastError = SchemaType.CastError; +let Document; + +/** + * ObjectId SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaObjectId(key, options, _schemaOptions, parentSchema) { + const isKeyHexStr = typeof key === 'string' && key.length === 24 && /^[a-f0-9]+$/i.test(key); + const suppressWarning = options?.suppressWarning; + if ((isKeyHexStr || typeof key === 'undefined') && !suppressWarning) { + utils.warn('mongoose: To create a new ObjectId please try ' + + '`Mongoose.Types.ObjectId` instead of using ' + + '`Mongoose.Schema.Types.ObjectId`. Set the `suppressWarning` option if ' + + 'you\'re trying to create a hex char path in your schema.'); + } + SchemaType.call(this, key, options, 'ObjectId', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaObjectId.schemaName = 'ObjectId'; + +SchemaObjectId.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaObjectId.prototype = Object.create(SchemaType.prototype); +SchemaObjectId.prototype.constructor = SchemaObjectId; +SchemaObjectId.prototype.OptionsConstructor = SchemaObjectIdOptions; + +/** + * Attaches a getter for all ObjectId instances + * + * #### Example: + * + * // Always convert to string when getting an ObjectId + * mongoose.ObjectId.get(v => v.toString()); + * + * const Model = mongoose.model('Test', new Schema({})); + * typeof (new Model({})._id); // 'string' + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaObjectId.get = SchemaType.get; + +/** + * Sets a default option for all ObjectId instances. + * + * #### Example: + * + * // Make all object ids have option `required` equal to true. + * mongoose.Schema.Types.ObjectId.set('required', true); + * + * const Order = mongoose.model('Order', new Schema({ userId: ObjectId })); + * new Order({ }).validateSync().errors.userId.message; // Path `userId` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaObjectId.set = SchemaType.set; + +SchemaObjectId.setters = []; + +/** + * Adds an auto-generated ObjectId default if turnOn is true. + * @param {boolean} turnOn auto generated ObjectId defaults + * @api public + * @return {SchemaType} this + */ + +SchemaObjectId.prototype.auto = function(turnOn) { + if (turnOn) { + this.default(defaultId); + this.set(resetId); + } + + return this; +}; + +/*! + * ignore + */ + +SchemaObjectId._checkRequired = v => isBsonType(v, 'ObjectId'); + +/*! + * ignore + */ + +SchemaObjectId._cast = castObjectId; + +/** + * Get/set the function used to cast arbitrary values to objectids. + * + * #### Example: + * + * // Make Mongoose only try to cast length 24 strings. By default, any 12 + * // char string is a valid ObjectId. + * const original = mongoose.ObjectId.cast(); + * mongoose.ObjectId.cast(v => { + * assert.ok(typeof v !== 'string' || v.length === 24); + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.ObjectId.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaObjectId.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaObjectId._defaultCaster = v => { + if (!(isBsonType(v, 'ObjectId'))) { + throw new Error(v + ' is not an instance of ObjectId'); + } + return v; +}; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * #### Example: + * + * // Allow empty strings to pass `required` check + * mongoose.Schema.Types.String.checkRequired(v => v != null); + * + * const M = mongoose.model({ str: { type: String, required: true } }); + * new M({ str: '' }).validateSync(); // `null`, validation passes! + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaObjectId.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaObjectId.prototype.checkRequired = function checkRequired(value, doc) { + if (SchemaType._isRef(this, value, doc, true)) { + return !!value; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaObjectId.checkRequired(); + + return _checkRequired(value); +}; + +/** + * Casts to ObjectId + * + * @param {object} value + * @param {object} doc + * @param {boolean} init whether this is an initialization cast + * @api private + */ + +SchemaObjectId.prototype.cast = function(value, doc, init, prev, options) { + if (!(isBsonType(value, 'ObjectId')) && SchemaType._isRef(this, value, doc, init)) { + // wait! we may need to cast this to a document + if ((getConstructorName(value) || '').toLowerCase() === 'objectid') { + return new oid(value.toHexString()); + } + + if (value == null || utils.isNonBuiltinObject(value)) { + return this._castRef(value, doc, init, options); + } + } + + let castObjectId; + if (typeof this._castFunction === 'function') { + castObjectId = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castObjectId = this.constructor.cast(); + } else { + castObjectId = SchemaObjectId.cast(); + } + + try { + return castObjectId(value); + } catch (error) { + throw new CastError('ObjectId', value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val) { + return this.cast(val); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$in` is the function Mongoose calls to cast `$in` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaObjectId + * @instance + * @api public + */ + +Object.defineProperty(SchemaObjectId.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/*! + * ignore + */ + +function defaultId() { + return new oid(); +} + +defaultId.$runBeforeSetters = true; + +function resetId(v) { + Document || (Document = require('../document')); + + if (this instanceof Document) { + if (v === void 0) { + const _v = new oid(); + return _v; + } + } + + return v; +} + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + * @api public + */ + +SchemaObjectId.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = (this.options.required && typeof this.options.required !== 'function') || this.path === '_id'; + return createJSONSchemaTypeDefinition('string', 'objectId', options?.useBsonType, isRequired); +}; + +SchemaObjectId.prototype.autoEncryptionType = function autoEncryptionType() { + return 'objectId'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaObjectId; diff --git a/backend/node_modules/mongoose/lib/schema/operators/bitwise.js b/backend/node_modules/mongoose/lib/schema/operators/bitwise.js new file mode 100644 index 0000000..07e18cd --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/bitwise.js @@ -0,0 +1,38 @@ +/*! + * Module requirements. + */ + +'use strict'; + +const CastError = require('../../error/cast'); + +/*! + * ignore + */ + +function handleBitwiseOperator(val) { + const _this = this; + if (Array.isArray(val)) { + return val.map(function(v) { + return _castNumber(_this.path, v); + }); + } else if (Buffer.isBuffer(val)) { + return val; + } + // Assume trying to cast to number + return _castNumber(_this.path, val); +} + +/*! + * ignore + */ + +function _castNumber(path, num) { + const v = Number(num); + if (isNaN(v)) { + throw new CastError('number', num, path); + } + return v; +} + +module.exports = handleBitwiseOperator; diff --git a/backend/node_modules/mongoose/lib/schema/operators/exists.js b/backend/node_modules/mongoose/lib/schema/operators/exists.js new file mode 100644 index 0000000..a1ddddd --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/exists.js @@ -0,0 +1,12 @@ +'use strict'; + +const castBoolean = require('../../cast/boolean'); + +/*! + * ignore + */ + +module.exports = function(val) { + const path = this?.path ?? null; + return castBoolean(val, path); +}; diff --git a/backend/node_modules/mongoose/lib/schema/operators/geospatial.js b/backend/node_modules/mongoose/lib/schema/operators/geospatial.js new file mode 100644 index 0000000..b3207e8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/geospatial.js @@ -0,0 +1,107 @@ +/*! + * Module requirements. + */ + +'use strict'; + +const castArraysOfNumbers = require('./helpers').castArraysOfNumbers; +const castToNumber = require('./helpers').castToNumber; + +/*! + * ignore + */ + +exports.cast$geoIntersects = cast$geoIntersects; +exports.cast$near = cast$near; +exports.cast$within = cast$within; + +function cast$near(val) { + const SchemaArray = require('../array'); + + if (Array.isArray(val)) { + castArraysOfNumbers(val, this); + return val; + } + + _castMinMaxDistance(this, val); + + if (val?.$geometry) { + return cast$geometry(val, this); + } + + if (!Array.isArray(val)) { + throw new TypeError('$near must be either an array or an object ' + + 'with a $geometry property'); + } + + return SchemaArray.prototype.castForQuery.call(this, null, val); +} + +function cast$geometry(val, self) { + switch (val.$geometry.type) { + case 'Polygon': + case 'LineString': + case 'Point': + castArraysOfNumbers(val.$geometry.coordinates, self); + break; + default: + // ignore unknowns + break; + } + + _castMinMaxDistance(self, val); + + return val; +} + +function cast$within(val) { + _castMinMaxDistance(this, val); + + if (val.$box || val.$polygon) { + const type = val.$box ? '$box' : '$polygon'; + val[type].forEach(arr => { + if (!Array.isArray(arr)) { + const msg = 'Invalid $within $box argument. ' + + 'Expected an array, received ' + arr; + throw new TypeError(msg); + } + arr.forEach((v, i) => { + arr[i] = castToNumber.call(this, v); + }); + }); + } else if (val.$center || val.$centerSphere) { + const type = val.$center ? '$center' : '$centerSphere'; + val[type].forEach((item, i) => { + if (Array.isArray(item)) { + item.forEach((v, j) => { + item[j] = castToNumber.call(this, v); + }); + } else { + val[type][i] = castToNumber.call(this, item); + } + }); + } else if (val.$geometry) { + cast$geometry(val, this); + } + + return val; +} + +function cast$geoIntersects(val) { + const geo = val.$geometry; + if (!geo) { + return; + } + + cast$geometry(val, this); + return val; +} + +function _castMinMaxDistance(self, val) { + if (val.$maxDistance) { + val.$maxDistance = castToNumber.call(self, val.$maxDistance); + } + if (val.$minDistance) { + val.$minDistance = castToNumber.call(self, val.$minDistance); + } +} diff --git a/backend/node_modules/mongoose/lib/schema/operators/helpers.js b/backend/node_modules/mongoose/lib/schema/operators/helpers.js new file mode 100644 index 0000000..4690ca3 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/helpers.js @@ -0,0 +1,32 @@ +'use strict'; + +/*! + * Module requirements. + */ + +const SchemaNumber = require('../number'); + +/*! + * ignore + */ + +exports.castToNumber = castToNumber; +exports.castArraysOfNumbers = castArraysOfNumbers; + +/*! + * ignore + */ + +function castToNumber(val) { + return SchemaNumber.cast()(val); +} + +function castArraysOfNumbers(arr, self) { + arr.forEach(function(v, i) { + if (Array.isArray(v)) { + castArraysOfNumbers(v, self); + } else { + arr[i] = castToNumber.call(self, v); + } + }); +} diff --git a/backend/node_modules/mongoose/lib/schema/operators/text.js b/backend/node_modules/mongoose/lib/schema/operators/text.js new file mode 100644 index 0000000..5059ca8 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/text.js @@ -0,0 +1,39 @@ +'use strict'; + +const CastError = require('../../error/cast'); +const castBoolean = require('../../cast/boolean'); +const castString = require('../../cast/string'); + +/** + * Casts val to an object suitable for `$text`. Throws an error if the object + * can't be casted. + * + * @param {any} val value to cast + * @param {string} [path] path to associate with any errors that occurred + * @return {object} casted object + * @see https://www.mongodb.com/docs/manual/reference/operator/query/text/ + * @api private + */ + +module.exports = function castTextSearch(val, path) { + if (val == null || typeof val !== 'object') { + throw new CastError('$text', val, path); + } + + if (val.$search != null) { + val.$search = castString(val.$search, path + '.$search'); + } + if (val.$language != null) { + val.$language = castString(val.$language, path + '.$language'); + } + if (val.$caseSensitive != null) { + val.$caseSensitive = castBoolean(val.$caseSensitive, + path + '.$caseSensitive'); + } + if (val.$diacriticSensitive != null) { + val.$diacriticSensitive = castBoolean(val.$diacriticSensitive, + path + '.$diacriticSensitive'); + } + + return val; +}; diff --git a/backend/node_modules/mongoose/lib/schema/operators/type.js b/backend/node_modules/mongoose/lib/schema/operators/type.js new file mode 100644 index 0000000..952c790 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/operators/type.js @@ -0,0 +1,20 @@ +'use strict'; + +/*! + * ignore + */ + +module.exports = function(val) { + if (Array.isArray(val)) { + if (!val.every(v => typeof v === 'number' || typeof v === 'string')) { + throw new Error('$type array values must be strings or numbers'); + } + return val; + } + + if (typeof val !== 'number' && typeof val !== 'string') { + throw new Error('$type parameter must be number, string, or array of numbers and strings'); + } + + return val; +}; diff --git a/backend/node_modules/mongoose/lib/schema/string.js b/backend/node_modules/mongoose/lib/schema/string.js new file mode 100644 index 0000000..9e9431d --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/string.js @@ -0,0 +1,733 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const SchemaType = require('../schemaType'); +const MongooseError = require('../error/index'); +const SchemaStringOptions = require('../options/schemaStringOptions'); +const castString = require('../cast/string'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const utils = require('../utils'); +const isBsonType = require('../helpers/isBsonType'); + +const CastError = SchemaType.CastError; + +/** + * String SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaString(key, options, _schemaOptions, parentSchema) { + this.enumValues = []; + this.regExp = null; + SchemaType.call(this, key, options, 'String', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaString.schemaName = 'String'; + +SchemaString.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaString.prototype = Object.create(SchemaType.prototype); +SchemaString.prototype.constructor = SchemaString; +Object.defineProperty(SchemaString.prototype, 'OptionsConstructor', { + configurable: false, + enumerable: false, + writable: false, + value: SchemaStringOptions +}); + +/*! + * ignore + */ + +SchemaString._cast = castString; + +/** + * Get/set the function used to cast arbitrary values to strings. + * + * #### Example: + * + * // Throw an error if you pass in an object. Normally, Mongoose allows + * // objects with custom `toString()` functions. + * const original = mongoose.Schema.Types.String.cast(); + * mongoose.Schema.Types.String.cast(v => { + * assert.ok(v == null || typeof v !== 'object'); + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.Schema.Types.String.cast(false); + * + * @param {Function} caster + * @return {Function} + * @function cast + * @static + * @api public + */ + +SchemaString.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaString._defaultCaster = v => { + if (v != null && typeof v !== 'string') { + throw new Error(); + } + return v; +}; + +/** + * Attaches a getter for all String instances. + * + * #### Example: + * + * // Make all numbers round down + * mongoose.Schema.Types.String.get(v => v.toLowerCase()); + * + * const Model = mongoose.model('Test', new Schema({ test: String })); + * new Model({ test: 'FOO' }).test; // 'foo' + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaString.get = SchemaType.get; + +/** + * Sets a default option for all String instances. + * + * #### Example: + * + * // Make all strings have option `trim` equal to true. + * mongoose.Schema.Types.String.set('trim', true); + * + * const User = mongoose.model('User', new Schema({ name: String })); + * new User({ name: ' John Doe ' }).name; // 'John Doe' + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaString.set = SchemaType.set; + +SchemaString.setters = []; + +/*! + * ignore + */ + +SchemaString._checkRequired = v => (v instanceof String || typeof v === 'string') && v.length; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * #### Example: + * + * // Allow empty strings to pass `required` check + * mongoose.Schema.Types.String.checkRequired(v => v != null); + * + * const M = mongoose.model({ str: { type: String, required: true } }); + * new M({ str: '' }).validateSync(); // `null`, validation passes! + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaString.checkRequired = SchemaType.checkRequired; + +/** + * Adds an enum validator + * + * #### Example: + * + * const states = ['opening', 'open', 'closing', 'closed'] + * const s = new Schema({ state: { type: String, enum: states }}) + * const M = db.model('M', s) + * const m = new M({ state: 'invalid' }) + * await m.save() + * .catch((err) => console.error(err)); // ValidationError: `invalid` is not a valid enum value for path `state`. + * m.state = 'open'; + * await m.save(); + * // success + * + * // or with custom error messages + * const enum = { + * values: ['opening', 'open', 'closing', 'closed'], + * message: 'enum validator failed for path `{PATH}` with value `{VALUE}`' + * } + * const s = new Schema({ state: { type: String, enum: enum }) + * const M = db.model('M', s) + * const m = new M({ state: 'invalid' }) + * await m.save() + * .catch((err) => console.error(err)); // ValidationError: enum validator failed for path `state` with value `invalid` + * m.state = 'open'; + * await m.save(); + * // success + * + * @param {...string|object} [args] enumeration values + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @see Enums in JavaScript https://masteringjs.io/tutorials/fundamentals/enum + * @api public + */ + +SchemaString.prototype.enum = function() { + if (this.enumValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.enumValidator; + }, this); + this.enumValidator = false; + } + + if (arguments[0] === void 0 || arguments[0] === false) { + return this; + } + + let values; + let errorMessage; + + if (utils.isObject(arguments[0])) { + if (Array.isArray(arguments[0].values)) { + values = arguments[0].values; + errorMessage = arguments[0].message; + } else { + values = utils.object.vals(arguments[0]); + errorMessage = MongooseError.messages.String.enum; + } + } else { + values = arguments; + errorMessage = MongooseError.messages.String.enum; + } + + for (const value of values) { + if (value !== undefined) { + this.enumValues.push(this.cast(value)); + } + } + + const vals = this.enumValues; + this.enumValidator = function(v) { + return null == v || ~vals.indexOf(v); + }; + this.validators.push({ + validator: this.enumValidator, + message: errorMessage, + type: 'enum', + enumValues: vals + }); + + return this; +}; + +/** + * Adds a lowercase [setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()). + * + * #### Example: + * + * const s = new Schema({ email: { type: String, lowercase: true }}) + * const M = db.model('M', s); + * const m = new M({ email: 'SomeEmail@example.COM' }); + * console.log(m.email) // someemail@example.com + * M.find({ email: 'SomeEmail@example.com' }); // Queries by 'someemail@example.com' + * + * Note that `lowercase` does **not** affect regular expression queries: + * + * #### Example: + * + * // Still queries for documents whose `email` matches the regular + * // expression /SomeEmail/. Mongoose does **not** convert the RegExp + * // to lowercase. + * M.find({ email: /SomeEmail/ }); + * + * @api public + * @return {SchemaType} this + */ + +SchemaString.prototype.lowercase = function(shouldApply) { + if (arguments.length > 0 && !shouldApply) { + return this; + } + return this.set(v => { + if (typeof v !== 'string') { + v = this.cast(v); + } + if (v) { + return v.toLowerCase(); + } + return v; + }); +}; + +/** + * Adds an uppercase [setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()). + * + * #### Example: + * + * const s = new Schema({ caps: { type: String, uppercase: true }}) + * const M = db.model('M', s); + * const m = new M({ caps: 'an example' }); + * console.log(m.caps) // AN EXAMPLE + * M.find({ caps: 'an example' }) // Matches documents where caps = 'AN EXAMPLE' + * + * Note that `uppercase` does **not** affect regular expression queries: + * + * #### Example: + * + * // Mongoose does **not** convert the RegExp to uppercase. + * M.find({ email: /an example/ }); + * + * @api public + * @return {SchemaType} this + */ + +SchemaString.prototype.uppercase = function(shouldApply) { + if (arguments.length > 0 && !shouldApply) { + return this; + } + return this.set(v => { + if (typeof v !== 'string') { + v = this.cast(v); + } + if (v) { + return v.toUpperCase(); + } + return v; + }); +}; + +/** + * Adds a trim [setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()). + * + * The string value will be [trimmed](https://masteringjs.io/tutorials/fundamentals/trim-string) when set. + * + * #### Example: + * + * const s = new Schema({ name: { type: String, trim: true }}); + * const M = db.model('M', s); + * const string = ' some name '; + * console.log(string.length); // 11 + * const m = new M({ name: string }); + * console.log(m.name.length); // 9 + * + * // Equivalent to `findOne({ name: string.trim() })` + * M.findOne({ name: string }); + * + * Note that `trim` does **not** affect regular expression queries: + * + * #### Example: + * + * // Mongoose does **not** trim whitespace from the RegExp. + * M.find({ name: / some name / }); + * + * @api public + * @return {SchemaType} this + */ + +SchemaString.prototype.trim = function(shouldTrim) { + if (arguments.length > 0 && !shouldTrim) { + return this; + } + return this.set(v => { + if (typeof v !== 'string') { + v = this.cast(v); + } + if (v) { + return v.trim(); + } + return v; + }); +}; + +/** + * Sets a minimum length validator. + * + * #### Example: + * + * const schema = new Schema({ postalCode: { type: String, minLength: 5 }) + * const Address = db.model('Address', schema) + * const address = new Address({ postalCode: '9512' }) + * address.save(function (err) { + * console.error(err) // validator error + * address.postalCode = '95125'; + * address.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MINLENGTH} token which will be replaced with the minimum allowed length + * const minLength = [5, 'The value of path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).']; + * const schema = new Schema({ postalCode: { type: String, minLength: minLength }) + * const Address = mongoose.model('Address', schema); + * const address = new Address({ postalCode: '9512' }); + * address.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512`) is shorter than the minimum length (5). + * }) + * + * @param {number} value minimum string length + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaString.prototype.minlength = function(value, message) { + if (this.minlengthValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.minlengthValidator; + }, this); + } + + if (value != null) { + let msg = message || MongooseError.messages.String.minlength; + msg = msg.replace(/{MINLENGTH}/, value); + this.validators.push({ + validator: this.minlengthValidator = function(v) { + return v === null || v.length >= value; + }, + message: msg, + type: 'minlength', + minlength: value + }); + } + + return this; +}; + +SchemaString.prototype.minLength = SchemaString.prototype.minlength; + +/** + * Sets a maximum length validator. + * + * #### Example: + * + * const schema = new Schema({ postalCode: { type: String, maxlength: 9 }) + * const Address = db.model('Address', schema) + * const address = new Address({ postalCode: '9512512345' }) + * address.save(function (err) { + * console.error(err) // validator error + * address.postalCode = '95125'; + * address.save() // success + * }) + * + * // custom error messages + * // We can also use the special {MAXLENGTH} token which will be replaced with the maximum allowed length + * const maxlength = [9, 'The value of path `{PATH}` (`{VALUE}`) exceeds the maximum allowed length ({MAXLENGTH}).']; + * const schema = new Schema({ postalCode: { type: String, maxlength: maxlength }) + * const Address = mongoose.model('Address', schema); + * const address = new Address({ postalCode: '9512512345' }); + * address.validate(function (err) { + * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512512345`) exceeds the maximum allowed length (9). + * }) + * + * @param {number} value maximum string length + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaString.prototype.maxlength = function(value, message) { + if (this.maxlengthValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.maxlengthValidator; + }, this); + } + + if (value != null) { + let msg = message || MongooseError.messages.String.maxlength; + msg = msg.replace(/{MAXLENGTH}/, value); + this.validators.push({ + validator: this.maxlengthValidator = function(v) { + return v === null || v.length <= value; + }, + message: msg, + type: 'maxlength', + maxlength: value + }); + } + + return this; +}; + +SchemaString.prototype.maxLength = SchemaString.prototype.maxlength; + +/** + * Sets a regexp validator. + * + * Any value that does not pass `regExp`.test(val) will fail validation. + * + * #### Example: + * + * const s = new Schema({ name: { type: String, match: /^a/ }}) + * const M = db.model('M', s) + * const m = new M({ name: 'I am invalid' }) + * m.validate(function (err) { + * console.error(String(err)) // "ValidationError: Path `name` is invalid (I am invalid)." + * m.name = 'apples' + * m.validate(function (err) { + * assert.ok(err) // success + * }) + * }) + * + * // using a custom error message + * const match = [ /\.html$/, "That file doesn't end in .html ({VALUE})" ]; + * const s = new Schema({ file: { type: String, match: match }}) + * const M = db.model('M', s); + * const m = new M({ file: 'invalid' }); + * m.validate(function (err) { + * console.log(String(err)) // "ValidationError: That file doesn't end in .html (invalid)" + * }) + * + * Empty strings, `undefined`, and `null` values always pass the match validator. If you require these values, enable the `required` validator also. + * + * const s = new Schema({ name: { type: String, match: /^a/, required: true }}) + * + * @param {RegExp} regExp regular expression to test against + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @api public + */ + +SchemaString.prototype.match = function match(regExp, message) { + // yes, we allow multiple match validators + + const msg = message || MongooseError.messages.String.match; + + const matchValidator = function(v) { + if (!regExp) { + return false; + } + + // In case RegExp happens to have `/g` flag set, we need to reset the + // `lastIndex`, otherwise `match` will intermittently fail. + regExp.lastIndex = 0; + + const ret = ((v != null && v !== '') + ? regExp.test(v) + : true); + return ret; + }; + + this.validators.push({ + validator: matchValidator, + message: msg, + type: 'regexp', + regexp: regExp + }); + return this; +}; + +/** + * Check if the given value satisfies the `required` validator. The value is + * considered valid if it is a string (that is, not `null` or `undefined`) and + * has positive length. The `required` validator **will** fail for empty + * strings. + * + * @param {any} value + * @param {Document} doc + * @return {boolean} + * @api public + */ + +SchemaString.prototype.checkRequired = function checkRequired(value, doc) { + if (typeof value === 'object' && SchemaType._isRef(this, value, doc, true)) { + return value != null; + } + + // `require('util').inherits()` does **not** copy static properties, and + // plugins like mongoose-float use `inherits()` for pre-ES6. + const _checkRequired = typeof this.constructor.checkRequired === 'function' ? + this.constructor.checkRequired() : + SchemaString.checkRequired(); + + return _checkRequired(value); +}; + +/** + * Casts to String + * + * @api private + */ + +SchemaString.prototype.cast = function(value, doc, init, prev, options) { + if (typeof value !== 'string' && SchemaType._isRef(this, value, doc, init)) { + return this._castRef(value, doc, init, options); + } + + let castString; + if (typeof this._castFunction === 'function') { + castString = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castString = this.constructor.cast(); + } else { + castString = SchemaString.cast(); + } + + try { + return castString(value); + } catch { + throw new CastError('string', value, this.path, null, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} + +/*! + * ignore + */ + +function handleArray(val, context) { + const _this = this; + if (!Array.isArray(val)) { + return [this.castForQuery(null, val, context)]; + } + return val.map(function(m) { + return _this.castForQuery(null, m, context); + }); +} + +/*! + * ignore + */ + +function handleSingleNoSetters(val) { + if (val == null) { + return this._castNullish(val); + } + + return this.cast(val, this); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $all: handleArray, + $gt: handleSingle, + $gte: handleSingle, + $lt: handleSingle, + $lte: handleSingle, + $options: handleSingleNoSetters, + $regex: function handle$regex(val) { + if (Object.prototype.toString.call(val) === '[object RegExp]') { + return val; + } + + return handleSingleNoSetters.call(this, val); + }, + $not: handleSingle +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$exists` is the function Mongoose calls to cast `$exists` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaString + * @instance + * @api public + */ + +Object.defineProperty(SchemaString.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} [val] + * @api private + */ + +SchemaString.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new Error('Can\'t use ' + $conditional + ' with String.'); + } + return handler.call(this, val, context); + } + + if (Object.prototype.toString.call(val) === '[object RegExp]' || isBsonType(val, 'BSONRegExp')) { + return val; + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaString.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'string', options?.useBsonType, isRequired); +}; + +SchemaString.prototype.autoEncryptionType = function autoEncryptionType() { + return 'string'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaString; diff --git a/backend/node_modules/mongoose/lib/schema/subdocument.js b/backend/node_modules/mongoose/lib/schema/subdocument.js new file mode 100644 index 0000000..b95210b --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/subdocument.js @@ -0,0 +1,436 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const CastError = require('../error/cast'); +const EventEmitter = require('events').EventEmitter; +const ObjectExpectedError = require('../error/objectExpected'); +const SchemaSubdocumentOptions = require('../options/schemaSubdocumentOptions'); +const SchemaType = require('../schemaType'); +const applyDefaults = require('../helpers/document/applyDefaults'); +const $exists = require('./operators/exists'); +const castToNumber = require('./operators/helpers').castToNumber; +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const discriminator = require('../helpers/model/discriminator'); +const geospatial = require('./operators/geospatial'); +const getConstructor = require('../helpers/discriminator/getConstructor'); +const handleIdOption = require('../helpers/schema/handleIdOption'); +const internalToObjectOptions = require('../options').internalToObjectOptions; +const isExclusive = require('../helpers/projection/isExclusive'); +const utils = require('../utils'); +const InvalidSchemaOptionError = require('../error/invalidSchemaOption'); + +let SubdocumentType; + +module.exports = SchemaSubdocument; + +/** + * Single nested subdocument SchemaType constructor. + * + * @param {Schema} schema + * @param {string} path + * @param {object} options + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaSubdocument(schema, path, options, parentSchema) { + if (schema.options.timeseries) { + throw new InvalidSchemaOptionError(path, 'timeseries'); + } + const schemaTypeIdOption = SchemaSubdocument.defaultOptions?._id; + if (schemaTypeIdOption != null) { + options = options || {}; + options._id = schemaTypeIdOption; + } + + schema = handleIdOption(schema, options); + + this.Constructor = _createConstructor(schema, null, options); + this.Constructor.path = path; + this.Constructor.prototype.$basePath = path; + this.schema = schema; + this.$isSingleNested = true; + this.base = schema.base; + SchemaType.call(this, path, options, 'Embedded', parentSchema); +} + +/*! + * ignore + */ + +SchemaSubdocument.prototype = Object.create(SchemaType.prototype); +SchemaSubdocument.prototype.constructor = SchemaSubdocument; +SchemaSubdocument.prototype.OptionsConstructor = SchemaSubdocumentOptions; + +/*! + * ignore + */ + +function _createConstructor(schema, baseClass, options) { + // lazy load + SubdocumentType || (SubdocumentType = require('../types/subdocument')); + + const _embedded = function SingleNested(value, path, parent) { + this.$__parent = parent; + SubdocumentType.apply(this, arguments); + + if (parent == null) { + return; + } + this.$session(parent.$session()); + }; + + schema._preCompile(); + + const proto = baseClass?.prototype ?? SubdocumentType.prototype; + _embedded.prototype = Object.create(proto); + _embedded.prototype.$__setSchema(schema); + _embedded.prototype.constructor = _embedded; + _embedded.prototype.$__schemaTypeOptions = options; + _embedded.$__required = options?.required; + _embedded.base = schema.base; + _embedded.schema = schema; + _embedded.$isSingleNested = true; + _embedded.events = new EventEmitter(); + _embedded.prototype.toBSON = function() { + return this.toObject(internalToObjectOptions); + }; + + // apply methods + for (const i in schema.methods) { + _embedded.prototype[i] = schema.methods[i]; + } + + // apply statics + for (const i in schema.statics) { + _embedded[i] = schema.statics[i]; + } + + for (const i in EventEmitter.prototype) { + _embedded[i] = EventEmitter.prototype[i]; + } + + return _embedded; +} + +/*! + * ignore + */ +const $conditionalHandlers = { ...SchemaType.prototype.$conditionalHandlers }; + +/** + * Special case for when users use a common location schema to represent + * locations for use with $geoWithin. + * https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/ + * + * @param {object} val + * @api private + */ + +$conditionalHandlers.$geoWithin = function handle$geoWithin(val, context) { + return { $geometry: this.castForQuery(null, val.$geometry, context) }; +}; + +/*! + * ignore + */ + +$conditionalHandlers.$near = +$conditionalHandlers.$nearSphere = geospatial.cast$near; + +$conditionalHandlers.$within = +$conditionalHandlers.$geoWithin = geospatial.cast$within; + +$conditionalHandlers.$geoIntersects = + geospatial.cast$geoIntersects; + +$conditionalHandlers.$minDistance = castToNumber; +$conditionalHandlers.$maxDistance = castToNumber; + +$conditionalHandlers.$exists = $exists; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$exists` is the function Mongoose calls to cast `$exists` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaSubdocument + * @instance + * @api public + */ + +Object.defineProperty(SchemaSubdocument.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents + * + * @param {object} value + * @api private + */ + +SchemaSubdocument.prototype.cast = function(val, doc, init, priorVal, options) { + if (val?.$isSingleNested && val.parent === doc) { + return val; + } + + if (!init && val != null && (typeof val !== 'object' || Array.isArray(val))) { + throw new ObjectExpectedError(this.path, val); + } + + const discriminatorKeyPath = this.schema.path(this.schema.options.discriminatorKey); + const defaultDiscriminatorValue = discriminatorKeyPath == null ? null : discriminatorKeyPath.getDefault(doc); + const Constructor = getConstructor(this.Constructor, val, defaultDiscriminatorValue); + + let subdoc; + + // Only pull relevant selected paths and pull out the base path + const parentSelected = doc?.$__?.selected; + const path = this.path; + const selected = parentSelected == null ? null : Object.keys(parentSelected).reduce((obj, key) => { + if (key.startsWith(path + '.')) { + obj = obj || {}; + obj[key.substring(path.length + 1)] = parentSelected[key]; + } + return obj; + }, null); + if (init) { + subdoc = new Constructor(void 0, selected, doc, { defaults: false }); + delete subdoc.$__.defaults; + // Don't pass `path` to $init - it's only for the subdocument itself, not its fields. + // For change tracking, subdocuments use relative paths internally. + // Here, `options.path` contains the absolute path and is only used by the subdocument constructor, not by $init. + if (options.path != null) { + options = { ...options }; + delete options.path; + } + subdoc.$init(val, options); + const exclude = isExclusive(selected); + applyDefaults(subdoc, selected, exclude); + } else { + options = Object.assign({}, options, { priorDoc: priorVal }); + if (utils.hasOwnKeys(val) === false) { + return new Constructor({}, selected, doc, options); + } + + return new Constructor(val, selected, doc, options); + } + + return subdoc; +}; + +/** + * Casts contents for query + * + * @param {string} [$conditional] optional query operator (like `$eq` or `$in`) + * @param {any} value + * @api private + */ + +SchemaSubdocument.prototype.castForQuery = function($conditional, val, context, options) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new Error('Can\'t use ' + $conditional); + } + return handler.call(this, val); + } + if (val == null) { + return val; + } + + const Constructor = getConstructor(this.Constructor, val); + if (val instanceof Constructor) { + return val; + } + + if (this.options.runSetters) { + val = this._applySetters(val, context); + } + + const overrideStrict = options?.strict ?? void 0; + + try { + val = new Constructor(val, overrideStrict); + } catch (error) { + // Make sure we always wrap in a CastError (gh-6803) + if (!(error instanceof CastError)) { + throw new CastError('Embedded', val, this.path, error, this); + } + throw error; + } + return val; +}; + +/** + * Async validation on this single nested doc. + * + * @api public + */ + +SchemaSubdocument.prototype.doValidate = async function doValidate(value, scope, options) { + const Constructor = getConstructor(this.Constructor, value); + + if (value && !(value instanceof Constructor)) { + value = new Constructor(value, null, scope?.$__ != null ? scope : null); + } + + if (options?.skipSchemaValidators) { + if (!value) { + return; + } + return value.validate(); + } + + await SchemaType.prototype.doValidate.call(this, value, scope, options); + if (value != null) { + await value.validate(); + } +}; + +/** + * Synchronously validate this single nested doc + * + * @api private + */ + +SchemaSubdocument.prototype.doValidateSync = function(value, scope, options) { + if (!options?.skipSchemaValidators) { + const schemaTypeError = SchemaType.prototype.doValidateSync.call(this, value, scope); + if (schemaTypeError) { + return schemaTypeError; + } + } + if (!value) { + return; + } + return value.validateSync(); +}; + +/** + * Adds a discriminator to this single nested subdocument. + * + * #### Example: + * + * const shapeSchema = Schema({ name: String }, { discriminatorKey: 'kind' }); + * const schema = Schema({ shape: shapeSchema }); + * + * const singleNestedPath = parentSchema.path('shape'); + * singleNestedPath.discriminator('Circle', Schema({ radius: Number })); + * + * @param {string} name + * @param {Schema} schema fields to add to the schema for instances of this sub-class + * @param {object|string} [options] If string, same as `options.value`. + * @param {string} [options.value] the string stored in the `discriminatorKey` property. If not specified, Mongoose uses the `name` parameter. + * @param {boolean} [options.clone=true] By default, `discriminator()` clones the given `schema`. Set to `false` to skip cloning. + * @return {Function} the constructor Mongoose will use for creating instances of this discriminator model + * @see discriminators https://mongoosejs.com/docs/discriminators.html + * @api public + */ + +SchemaSubdocument.prototype.discriminator = function(name, schema, options) { + options = options || {}; + const value = utils.isPOJO(options) ? options.value : options; + const clone = typeof options.clone === 'boolean' + ? options.clone + : true; + + if (schema.instanceOfSchema && clone) { + schema = schema.clone(); + } + + schema = discriminator(this.Constructor, name, schema, value, null, null, options.overwriteExisting); + + this.Constructor.discriminators[name] = _createConstructor(schema, this.Constructor); + + return this.Constructor.discriminators[name]; +}; + +/*! + * ignore + */ + +SchemaSubdocument.defaultOptions = {}; + +/** + * Sets a default option for all Subdocument instances. + * + * #### Example: + * + * // Make all subdocuments required by default. + * mongoose.Schema.Types.Subdocument.set('required', true); + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {void} + * @function set + * @static + * @api public + */ + +SchemaSubdocument.set = SchemaType.set; + +SchemaSubdocument.setters = []; + +/** + * Attaches a getter for all Subdocument instances + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaSubdocument.get = SchemaType.get; + +/*! + * ignore + */ + +SchemaSubdocument.prototype.toJSON = function toJSON() { + return { path: this.path, options: this.options }; +}; + +/*! + * ignore + */ + +SchemaSubdocument.prototype.clone = function() { + const schematype = new this.constructor( + this.schema, + this.path, + { ...this.options, _skipApplyDiscriminators: true }, + this.parentSchema + ); + schematype.validators = this.validators.slice(); + if (this.requiredValidator !== undefined) { + schematype.requiredValidator = this.requiredValidator; + } + schematype.Constructor.discriminators = Object.assign({}, this.Constructor.discriminators); + schematype._appliedDiscriminators = this._appliedDiscriminators; + return schematype; +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaSubdocument.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return { + ...this.schema.toJSONSchema(options), + ...createJSONSchemaTypeDefinition('object', 'object', options?.useBsonType, isRequired) + }; +}; diff --git a/backend/node_modules/mongoose/lib/schema/symbols.js b/backend/node_modules/mongoose/lib/schema/symbols.js new file mode 100644 index 0000000..7ae0e9e --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/symbols.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.schemaMixedSymbol = Symbol.for('mongoose:schema_mixed'); + +exports.builtInMiddleware = Symbol.for('mongoose:built-in-middleware'); diff --git a/backend/node_modules/mongoose/lib/schema/union.js b/backend/node_modules/mongoose/lib/schema/union.js new file mode 100644 index 0000000..7615afb --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/union.js @@ -0,0 +1,113 @@ +'use strict'; + +/*! + * ignore + */ + +const SchemaUnionOptions = require('../options/schemaUnionOptions'); +const SchemaType = require('../schemaType'); + +const firstValueSymbol = Symbol('firstValue'); + +/*! + * ignore + */ + +class Union extends SchemaType { + /** + * Create a Union schema type. + * + * @param {string} key the path in the schema for this schema type + * @param {object} options SchemaType-specific options (must have 'of' as array) + * @param {object} schemaOptions additional options from the schema this schematype belongs to + * @param {Schema} parentSchema the schema this schematype belongs to + */ + constructor(key, options, schemaOptions, parentSchema) { + super(key, options, 'Union', parentSchema); + if (!Array.isArray(options?.of) || options.of.length === 0) { + throw new Error('Union schema type requires an array of types'); + } + this.schemaTypes = options.of.map(obj => parentSchema.interpretAsType(key, obj, schemaOptions)); + } + + cast(val, doc, init, prev, options) { + let firstValue = firstValueSymbol; + let lastError; + // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then + // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value. + // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union. + // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types, + // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number. + for (let i = 0; i < this.schemaTypes.length; ++i) { + try { + const casted = this.schemaTypes[i].cast(val, doc, init, prev, options); + if (casted === val) { + return casted; + } + if (firstValue === firstValueSymbol) { + firstValue = casted; + } + } catch (error) { + lastError = error; + } + } + if (firstValue !== firstValueSymbol) { + return firstValue; + } + throw lastError; + } + + // Setters also need to be aware of casting - we need to apply the setters of the entry in the union we choose. + applySetters(val, doc, init, prev, options) { + let firstValue = firstValueSymbol; + let lastError; + // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then + // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value. + // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union. + // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types, + // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number. + for (let i = 0; i < this.schemaTypes.length; ++i) { + try { + let castedVal = this.schemaTypes[i]._applySetters(val, doc, init, prev, options); + if (castedVal == null) { + castedVal = this.schemaTypes[i]._castNullish(castedVal); + } else { + castedVal = this.schemaTypes[i].cast(castedVal, doc, init, prev, options); + } + if (castedVal === val) { + return castedVal; + } + if (firstValue === firstValueSymbol) { + firstValue = castedVal; + } + } catch (error) { + lastError = error; + } + } + if (firstValue !== firstValueSymbol) { + return firstValue; + } + throw lastError; + } + + clone() { + const schematype = super.clone(); + + schematype.schemaTypes = this.schemaTypes.map(schemaType => schemaType.clone()); + return schematype; + } +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +Union.schemaName = 'Union'; + +Union.defaultOptions = {}; + +Union.prototype.OptionsConstructor = SchemaUnionOptions; + +module.exports = Union; diff --git a/backend/node_modules/mongoose/lib/schema/uuid.js b/backend/node_modules/mongoose/lib/schema/uuid.js new file mode 100644 index 0000000..f136139 --- /dev/null +++ b/backend/node_modules/mongoose/lib/schema/uuid.js @@ -0,0 +1,305 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const SchemaType = require('../schemaType'); +const CastError = SchemaType.CastError; +const castUUID = require('../cast/uuid'); +const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeDefinition'); +const utils = require('../utils'); +const handleBitwiseOperator = require('./operators/bitwise'); + +const UUID_FORMAT = castUUID.UUID_FORMAT; + +/** + * Convert binary to a uuid string + * @param {Buffer|Binary|string} uuidBin The value to process + * @returns {string} The completed uuid-string + * @api private + */ +function binaryToString(uuidBin) { + // i(hasezoey) dont quite know why, but "uuidBin" may sometimes also be the already processed string + let hex; + if (typeof uuidBin !== 'string' && uuidBin != null) { + hex = uuidBin.toString('hex'); + const uuidStr = hex.substring(0, 8) + '-' + hex.substring(8, 8 + 4) + '-' + hex.substring(12, 12 + 4) + '-' + hex.substring(16, 16 + 4) + '-' + hex.substring(20, 20 + 12); + return uuidStr; + } + return uuidBin; +} + +/** + * UUIDv1 SchemaType constructor. + * + * @param {string} key + * @param {object} options + * @param {object} _schemaOptions + * @param {Schema} parentSchema + * @inherits SchemaType + * @api public + */ + +function SchemaUUID(key, options, _schemaOptions, parentSchema) { + SchemaType.call(this, key, options, 'UUID', parentSchema); +} + +/** + * This schema type's name, to defend against minifiers that mangle + * function names. + * + * @api public + */ +SchemaUUID.schemaName = 'UUID'; + +SchemaUUID.defaultOptions = {}; + +/*! + * Inherits from SchemaType. + */ +SchemaUUID.prototype = Object.create(SchemaType.prototype); +SchemaUUID.prototype.constructor = SchemaUUID; + +/*! + * ignore + */ + +SchemaUUID._cast = castUUID; + +/** + * Attaches a getter for all UUID instances. + * + * #### Example: + * + * // Note that `v` is a string by default + * mongoose.Schema.UUID.get(v => v.toUpperCase()); + * + * const Model = mongoose.model('Test', new Schema({ test: 'UUID' })); + * new Model({ test: uuid.v4() }).test; // UUID with all uppercase + * + * @param {Function} getter + * @return {this} + * @function get + * @static + * @api public + */ + +SchemaUUID.get = SchemaType.get; + +/** + * Sets a default option for all UUID instances. + * + * #### Example: + * + * // Make all UUIDs have `required` of true by default. + * mongoose.Schema.UUID.set('required', true); + * + * const User = mongoose.model('User', new Schema({ test: mongoose.UUID })); + * new User({ }).validateSync().errors.test.message; // Path `test` is required. + * + * @param {string} option The option you'd like to set the value for + * @param {any} value value for option + * @return {undefined} + * @function set + * @static + * @api public + */ + +SchemaUUID.set = SchemaType.set; + +SchemaUUID.setters = []; + +/** + * Get/set the function used to cast arbitrary values to UUIDs. + * + * #### Example: + * + * // Make Mongoose refuse to cast UUIDs with 0 length + * const original = mongoose.Schema.Types.UUID.cast(); + * mongoose.UUID.cast(v => { + * assert.ok(typeof v === "string" && v.length > 0); + * return original(v); + * }); + * + * // Or disable casting entirely + * mongoose.UUID.cast(false); + * + * @param {Function} [caster] + * @return {Function} + * @function get + * @static + * @api public + */ + +SchemaUUID.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = this._defaultCaster; + } + this._cast = caster; + + return this._cast; +}; + +/*! + * ignore + */ + +SchemaUUID._checkRequired = v => v != null; + +/** + * Override the function the required validator uses to check whether a string + * passes the `required` check. + * + * @param {Function} fn + * @return {Function} + * @function checkRequired + * @static + * @api public + */ + +SchemaUUID.checkRequired = SchemaType.checkRequired; + +/** + * Check if the given value satisfies a required validator. + * + * @param {any} value + * @return {boolean} + * @api public + */ + +SchemaUUID.prototype.checkRequired = function checkRequired(value) { + if (Buffer.isBuffer(value)) { + value = binaryToString(value); + } + return value != null && UUID_FORMAT.test(value); +}; + +/** + * Casts to UUID + * + * @param {object} value + * @param {object} doc + * @param {boolean} init whether this is an initialization cast + * @api private + */ + +SchemaUUID.prototype.cast = function(value, doc, init, prev, options) { + if (utils.isNonBuiltinObject(value) && + SchemaType._isRef(this, value, doc, init)) { + return this._castRef(value, doc, init, options); + } + + let castFn; + if (typeof this._castFunction === 'function') { + castFn = this._castFunction; + } else if (typeof this.constructor.cast === 'function') { + castFn = this.constructor.cast(); + } else { + castFn = SchemaUUID.cast(); + } + + try { + return castFn(value); + } catch (error) { + throw new CastError(SchemaUUID.schemaName, value, this.path, error, this); + } +}; + +/*! + * ignore + */ + +function handleSingle(val) { + return this.cast(val); +} + +/*! + * ignore + */ + +function handleArray(val) { + return val.map((m) => { + return this.cast(m); + }); +} + +const $conditionalHandlers = { + ...SchemaType.prototype.$conditionalHandlers, + $bitsAllClear: handleBitwiseOperator, + $bitsAnyClear: handleBitwiseOperator, + $bitsAllSet: handleBitwiseOperator, + $bitsAnySet: handleBitwiseOperator, + $all: handleArray, + $in: handleArray, + $ne: handleSingle, + $nin: handleArray +}; + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$exists` is the function Mongoose calls to cast `$exists` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaUUID + * @instance + * @api public + */ + +Object.defineProperty(SchemaUUID.prototype, '$conditionalHandlers', { + enumerable: false, + value: $conditionalHandlers +}); + +/** + * Casts contents for queries. + * + * @param {string} $conditional + * @param {any} val + * @api private + */ + +SchemaUUID.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) + throw new Error('Can\'t use ' + $conditional + ' with UUID.'); + return handler.call(this, val, context); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaUUID.prototype.toJSONSchema = function toJSONSchema(options) { + const isRequired = this.options.required && typeof this.options.required !== 'function'; + return createJSONSchemaTypeDefinition('string', 'binData', options?.useBsonType, isRequired); +}; + +SchemaUUID.prototype.autoEncryptionType = function autoEncryptionType() { + return 'binData'; +}; + +/*! + * Module exports. + */ + +module.exports = SchemaUUID; diff --git a/backend/node_modules/mongoose/lib/schemaType.js b/backend/node_modules/mongoose/lib/schemaType.js new file mode 100644 index 0000000..dbbd59b --- /dev/null +++ b/backend/node_modules/mongoose/lib/schemaType.js @@ -0,0 +1,1842 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const MongooseError = require('./error/index'); +const SchemaTypeOptions = require('./options/schemaTypeOptions'); +const $exists = require('./schema/operators/exists'); +const $type = require('./schema/operators/type'); +const clone = require('./helpers/clone'); +const handleImmutable = require('./helpers/schematype/handleImmutable'); +const isAsyncFunction = require('./helpers/isAsyncFunction'); +const isSimpleValidator = require('./helpers/isSimpleValidator'); +const schemaTypeSymbol = require('./helpers/symbols').schemaTypeSymbol; +const utils = require('./utils'); +const validatorErrorSymbol = require('./helpers/symbols').validatorErrorSymbol; +const documentIsModified = require('./helpers/symbols').documentIsModified; + +const populateModelSymbol = require('./helpers/symbols').populateModelSymbol; + +const CastError = MongooseError.CastError; +const ValidatorError = MongooseError.ValidatorError; + +const setOptionsForDefaults = { _skipMarkModified: true }; + +/** + * SchemaType constructor. Do **not** instantiate `SchemaType` directly. + * Mongoose converts your schema paths into SchemaTypes automatically. + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * schema.path('name') instanceof SchemaType; // true + * + * @param {string} path + * @param {SchemaTypeOptions} [options] See [SchemaTypeOptions docs](https://mongoosejs.com/docs/api/schematypeoptions.html) + * @param {string} [instance] + * @param {Schema} [parentSchema] + * @api public + */ + +function SchemaType(path, options, instance, parentSchema) { + this[schemaTypeSymbol] = true; + this.path = path; + this.instance = instance; + this.schemaName = this.constructor.schemaName; + this.validators = []; + this.getters = Object.hasOwn(this.constructor, 'getters') ? + this.constructor.getters.slice() : + []; + this.setters = Object.hasOwn(this.constructor, 'setters') ? + this.constructor.setters.slice() : + []; + + this.splitPath(); + + options = options || {}; + const defaultOptions = this.constructor.defaultOptions || {}; + const defaultOptionsKeys = Object.keys(defaultOptions); + + for (const option of defaultOptionsKeys) { + if (option === 'validate') { + this.validate(defaultOptions.validate); + } else if (Object.hasOwn(defaultOptions, option) && !Object.hasOwn(options, option)) { + options[option] = defaultOptions[option]; + } + } + + if (options.select == null) { + delete options.select; + } + + const Options = this.OptionsConstructor || SchemaTypeOptions; + this.options = new Options(options); + this.parentSchema = parentSchema; + this._index = null; + + if (utils.hasUserDefinedProperty(this.options, 'immutable')) { + this.$immutable = this.options.immutable; + + handleImmutable(this); + } + + const keys = Object.keys(this.options); + for (const prop of keys) { + if (prop === 'cast') { + if (Array.isArray(this.options[prop])) { + this.castFunction.apply(this, this.options[prop]); + } else { + this.castFunction(this.options[prop]); + } + continue; + } + if (utils.hasUserDefinedProperty(this.options, prop) && typeof this[prop] === 'function') { + // { unique: true, index: true } + if (prop === 'index' && this._index) { + if (options.index === false) { + const index = this._index; + if (typeof index === 'object' && index != null) { + if (index.unique) { + throw new MongooseError('Path "' + this.path + '" may not have `index` ' + + 'set to false and `unique` set to true'); + } + if (index.sparse) { + throw new MongooseError('Path "' + this.path + '" may not have `index` ' + + 'set to false and `sparse` set to true'); + } + } + + this._index = false; + } + continue; + } + + const val = options[prop]; + // Special case so we don't screw up array defaults, see gh-5780 + if (prop === 'default') { + this.default(val); + continue; + } + + const opts = Array.isArray(val) ? val : [val]; + + this[prop].apply(this, opts); + } + } + + Object.defineProperty(this, '$$context', { + enumerable: false, + configurable: false, + writable: true, + value: null + }); +} + +/** + * The class that Mongoose uses internally to instantiate this SchemaType's `options` property. + * @memberOf SchemaType + * @instance + * @api private + */ + +SchemaType.prototype.OptionsConstructor = SchemaTypeOptions; + +/** + * The path to this SchemaType in a Schema. + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * schema.path('name').path; // 'name' + * + * @property path + * @api public + * @memberOf SchemaType + */ + +SchemaType.prototype.path; + +/** + * Returns a plain JavaScript object representing this SchemaType. + * + * Typically used by `JSON.stringify()` or when calling `.toJSON()` on a SchemaType instance. + * Omits certain internal properties such as `parentSchema` that can cause circular references. + * + * #### Example: + * + * const schema = new Schema({ name: String }); + * const schematype = schema.path('name'); + * console.log(schematype.toJSON()); + * + * @memberOf SchemaType + * @instance + * @api public + */ + +SchemaType.prototype.toJSON = function toJSON() { + const res = { ...this }; + delete res.parentSchema; + return res; +}; + +/** + * The validators that Mongoose should run to validate properties at this SchemaType's path. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, required: true } }); + * schema.path('name').validators.length; // 1, the `required` validator + * + * @property validators + * @api public + * @memberOf SchemaType + */ + +SchemaType.prototype.validators; + +/** + * True if this SchemaType has a required validator. False otherwise. + * + * #### Example: + * + * const schema = new Schema({ name: { type: String, required: true } }); + * schema.path('name').isRequired; // true + * + * schema.path('name').required(false); + * schema.path('name').isRequired; // false + * + * @property isRequired + * @api public + * @memberOf SchemaType + */ + +SchemaType.prototype.isRequired; + +/** + * Split the current dotted path into segments + * + * @return {string[]|undefined} + * @api private + */ + +SchemaType.prototype.splitPath = function() { + if (this._presplitPath != null) { + return this._presplitPath; + } + if (this.path == null) { + return undefined; + } + + this._presplitPath = this.path.indexOf('.') === -1 ? [this.path] : this.path.split('.'); + return this._presplitPath; +}; + +/** + * Get/set the function used to cast arbitrary values to this type. + * + * #### Example: + * + * // Disallow `null` for numbers, and don't try to cast any values to + * // numbers, so even strings like '123' will cause a CastError. + * mongoose.Number.cast(function(v) { + * assert.ok(v === undefined || typeof v === 'number'); + * return v; + * }); + * + * @param {Function|false} caster Function that casts arbitrary values to this type, or throws an error if casting failed + * @return {Function} + * @static + * @memberOf SchemaType + * @function cast + * @api public + */ + +SchemaType.cast = function cast(caster) { + if (arguments.length === 0) { + return this._cast; + } + if (caster === false) { + caster = v => v; + } + this._cast = caster; + + return this._cast; +}; + +/** + * Get/set the function used to cast arbitrary values to this particular schematype instance. + * Overrides `SchemaType.cast()`. + * + * #### Example: + * + * // Disallow `null` for numbers, and don't try to cast any values to + * // numbers, so even strings like '123' will cause a CastError. + * const number = new mongoose.Number('mypath', {}); + * number.cast(function(v) { + * assert.ok(v === undefined || typeof v === 'number'); + * return v; + * }); + * + * @param {Function|false} caster Function that casts arbitrary values to this type, or throws an error if casting failed + * @return {Function} + * @memberOf SchemaType + * @api public + */ + +SchemaType.prototype.castFunction = function castFunction(caster, message) { + if (arguments.length === 0) { + return this._castFunction; + } + + if (caster === false) { + caster = this.constructor._defaultCaster || (v => v); + } + if (typeof caster === 'string') { + this._castErrorMessage = caster; + return this._castFunction; + } + if (caster != null) { + this._castFunction = caster; + } + if (message != null) { + this._castErrorMessage = message; + } + + return this._castFunction; +}; + +/** + * The function that Mongoose calls to cast arbitrary values to this SchemaType. + * + * @param {object} value value to cast + * @param {Document} doc document that triggers the casting + * @param {boolean} init + * @api public + */ + +SchemaType.prototype.cast = function cast() { + throw new MongooseError('Base SchemaType class does not implement a `cast()` function'); +}; + +/** + * Sets a default option for this schema type. + * + * #### Example: + * + * // Make all strings be trimmed by default + * mongoose.SchemaTypes.String.set('trim', true); + * + * @param {string} option The name of the option you'd like to set (e.g. trim, lowercase, etc...) + * @param {any} value The value of the option you'd like to set. + * @return {void} + * @static + * @memberOf SchemaType + * @function set + * @api public + */ + +SchemaType.set = function set(option, value) { + if (!Object.hasOwn(this, 'defaultOptions')) { + this.defaultOptions = Object.assign({}, this.defaultOptions); + } + this.defaultOptions[option] = value; +}; + +/** + * Attaches a getter for all instances of this schema type. + * + * #### Example: + * + * // Make all numbers round down + * mongoose.Number.get(function(v) { return Math.floor(v); }); + * + * @param {Function} getter + * @return {this} + * @static + * @memberOf SchemaType + * @function get + * @api public + */ + +SchemaType.get = function(getter) { + this.getters = Object.hasOwn(this, 'getters') ? this.getters : []; + this.getters.push(getter); +}; + +/** + * Sets a default value for this SchemaType. + * + * #### Example: + * + * const schema = new Schema({ n: { type: Number, default: 10 }) + * const M = db.model('M', schema) + * const m = new M; + * console.log(m.n) // 10 + * + * Defaults can be either `functions` which return the value to use as the default or the literal value itself. Either way, the value will be cast based on its schema type before being set during document creation. + * + * #### Example: + * + * // values are cast: + * const schema = new Schema({ aNumber: { type: Number, default: 4.815162342 }}) + * const M = db.model('M', schema) + * const m = new M; + * console.log(m.aNumber) // 4.815162342 + * + * // default unique objects for Mixed types: + * const schema = new Schema({ mixed: Schema.Types.Mixed }); + * schema.path('mixed').default(function () { + * return {}; + * }); + * + * // if we don't use a function to return object literals for Mixed defaults, + * // each document will receive a reference to the same object literal creating + * // a "shared" object instance: + * const schema = new Schema({ mixed: Schema.Types.Mixed }); + * schema.path('mixed').default({}); + * const M = db.model('M', schema); + * const m1 = new M; + * m1.mixed.added = 1; + * console.log(m1.mixed); // { added: 1 } + * const m2 = new M; + * console.log(m2.mixed); // { added: 1 } + * + * @param {Function|any} val The default value to set + * @return {any|undefined} Returns the set default value. + * @api public + */ + +SchemaType.prototype.default = function(val) { + if (arguments.length === 1) { + if (val === void 0) { + this.defaultValue = void 0; + return void 0; + } + + if (val?.instanceOfSchema) { + throw new MongooseError('Cannot set default value of path `' + this.path + + '` to a mongoose Schema instance.'); + } + + this.defaultValue = val; + return this.defaultValue; + } else if (arguments.length > 1) { + this.defaultValue = [...arguments]; + } + return this.defaultValue; +}; + +/** + * Declares the index options for this schematype. + * + * #### Example: + * + * const s = new Schema({ name: { type: String, index: true }) + * const s = new Schema({ name: { type: String, index: -1 }) + * const s = new Schema({ loc: { type: [Number], index: 'hashed' }) + * const s = new Schema({ loc: { type: [Number], index: '2d', sparse: true }) + * const s = new Schema({ loc: { type: [Number], index: { type: '2dsphere', sparse: true }}) + * const s = new Schema({ date: { type: Date, index: { unique: true, expires: '1d' }}) + * s.path('my.path').index(true); + * s.path('my.date').index({ expires: 60 }); + * s.path('my.path').index({ unique: true, sparse: true }); + * + * @param {object|boolean|string|number} options + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.index = function(options) { + this._index = options; + utils.expires(this._index); + return this; +}; + +/** + * Declares an unique index. + * + * #### Example: + * + * const s = new Schema({ name: { type: String, unique: true } }); + * s.path('name').index({ unique: true }); + * + * _NOTE: violating the constraint returns an `E11000` error from MongoDB when saving, not a Mongoose validation error._ + * + * You can optionally specify an error message to replace MongoDB's default `E11000 duplicate key error` message. + * The following will throw a "Email must be unique" error if `save()`, `updateOne()`, `updateMany()`, `replaceOne()`, + * `findOneAndUpdate()`, or `findOneAndReplace()` throws a duplicate key error: + * + * ```javascript + * new Schema({ + * email: { + * type: String, + * unique: [true, 'Email must be unique'] + * } + * }); + * ``` + * + * Note that the above syntax does **not** work for `bulkWrite()` or `insertMany()`. `bulkWrite()` and `insertMany()` + * will still throw MongoDB's default `E11000 duplicate key error` message. + * + * @param {boolean} value + * @param {string} [message] + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.unique = function unique(value, message) { + if (this._index === false) { + if (!value) { + return; + } + throw new MongooseError('Path "' + this.path + '" may not have `index` set to ' + + 'false and `unique` set to true'); + } + + if (!Object.hasOwn(this.options, 'index') && value === false) { + return this; + } + + if (this._index == null || this._index === true) { + this._index = {}; + } else if (typeof this._index === 'string') { + this._index = { type: this._index }; + } + + this._index.unique = !!value; + if (typeof message === 'string') { + this._duplicateKeyErrorMessage = message; + } + return this; +}; + +/** + * Declares a full text index. + * + * ### Example: + * + * const s = new Schema({ name : { type: String, text : true } }) + * s.path('name').index({ text : true }); + * + * @param {boolean} bool + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.text = function(bool) { + if (this._index === false) { + if (!bool) { + return this; + } + throw new MongooseError('Path "' + this.path + '" may not have `index` set to ' + + 'false and `text` set to true'); + } + + if (!Object.hasOwn(this.options, 'index') && bool === false) { + return this; + } + + if (this._index === null || this._index === undefined || + typeof this._index === 'boolean') { + this._index = {}; + } else if (typeof this._index === 'string') { + this._index = { type: this._index }; + } + + this._index.text = bool; + return this; +}; + +/** + * Declares a sparse index. + * + * #### Example: + * + * const s = new Schema({ name: { type: String, sparse: true } }); + * s.path('name').index({ sparse: true }); + * + * @param {boolean} bool + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.sparse = function(bool) { + if (this._index === false) { + if (!bool) { + return this; + } + throw new MongooseError('Path "' + this.path + '" may not have `index` set to ' + + 'false and `sparse` set to true'); + } + + if (!Object.hasOwn(this.options, 'index') && bool === false) { + return this; + } + + if (this._index == null || typeof this._index === 'boolean') { + this._index = {}; + } else if (typeof this._index === 'string') { + this._index = { type: this._index }; + } + + this._index.sparse = bool; + return this; +}; + +/** + * Defines this path as immutable. Mongoose prevents you from changing + * immutable paths unless the parent document has [`isNew: true`](https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew()). + * + * #### Example: + * + * const schema = new Schema({ + * name: { type: String, immutable: true }, + * age: Number + * }); + * const Model = mongoose.model('Test', schema); + * + * await Model.create({ name: 'test' }); + * const doc = await Model.findOne(); + * + * doc.isNew; // false + * doc.name = 'new name'; + * doc.name; // 'test', because `name` is immutable + * + * Mongoose also prevents changing immutable properties using `updateOne()` + * and `updateMany()` based on [strict mode](https://mongoosejs.com/docs/guide.html#strict). + * + * #### Example: + * + * // Mongoose will strip out the `name` update, because `name` is immutable + * Model.updateOne({}, { $set: { name: 'test2' }, $inc: { age: 1 } }); + * + * // If `strict` is set to 'throw', Mongoose will throw an error if you + * // update `name` + * const err = await Model.updateOne({}, { name: 'test2' }, { strict: 'throw' }). + * then(() => null, err => err); + * err.name; // StrictModeError + * + * // If `strict` is `false`, Mongoose allows updating `name` even though + * // the property is immutable. + * Model.updateOne({}, { name: 'test2' }, { strict: false }); + * + * @param {boolean} bool + * @return {SchemaType} this + * @see isNew https://mongoosejs.com/docs/api/document.html#Document.prototype.isNew() + * @api public + */ + +SchemaType.prototype.immutable = function(bool) { + this.$immutable = bool; + handleImmutable(this); + + return this; +}; + +/** + * Defines a custom function for transforming this path when converting a document to JSON. + * + * Mongoose calls this function with one parameter: the current `value` of the path. Mongoose + * then uses the return value in the JSON output. + * + * #### Example: + * + * const schema = new Schema({ + * date: { type: Date, transform: v => v.getFullYear() } + * }); + * const Model = mongoose.model('Test', schema); + * + * await Model.create({ date: new Date('2016-06-01') }); + * const doc = await Model.findOne(); + * + * doc.date instanceof Date; // true + * + * doc.toJSON().date; // 2016 as a number + * JSON.stringify(doc); // '{"_id":...,"date":2016}' + * + * @param {Function} fn + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.transform = function(fn) { + this.options.transform = fn; + + return this; +}; + +/** + * Adds a setter to this schematype. + * + * #### Example: + * + * function capitalize (val) { + * if (typeof val !== 'string') val = ''; + * return val.charAt(0).toUpperCase() + val.substring(1); + * } + * + * // defining within the schema + * const s = new Schema({ name: { type: String, set: capitalize }}); + * + * // or with the SchemaType + * const s = new Schema({ name: String }) + * s.path('name').set(capitalize); + * + * Setters allow you to transform the data before it gets to the raw mongodb + * document or query. + * + * Suppose you are implementing user registration for a website. Users provide + * an email and password, which gets saved to mongodb. The email is a string + * that you will want to normalize to lower case, in order to avoid one email + * having more than one account -- e.g., otherwise, avenue@q.com can be registered for 2 accounts via avenue@q.com and AvEnUe@Q.CoM. + * + * You can set up email lower case normalization easily via a Mongoose setter. + * + * function toLower(v) { + * return v.toLowerCase(); + * } + * + * const UserSchema = new Schema({ + * email: { type: String, set: toLower } + * }); + * + * const User = db.model('User', UserSchema); + * + * const user = new User({email: 'AVENUE@Q.COM'}); + * console.log(user.email); // 'avenue@q.com' + * + * // or + * const user = new User(); + * user.email = 'Avenue@Q.com'; + * console.log(user.email); // 'avenue@q.com' + * User.updateOne({ _id: _id }, { $set: { email: 'AVENUE@Q.COM' } }); // update to 'avenue@q.com' + * + * // Setters also transform query filters + * const user = await User.find({ email: 'AVENUE@Q.COM' }); // query for 'avenue@q.com' + * + * As you can see above, setters allow you to transform the data before it + * stored in MongoDB, or before executing a query. + * + * _NOTE: we could have also just used the built-in [`lowercase: true` SchemaType option](https://mongoosejs.com/docs/api/schemastringoptions.html#SchemaStringOptions.prototype.lowercase) instead of defining our own function._ + * + * new Schema({ email: { type: String, lowercase: true }}) + * + * Setters are also passed a second argument, the schematype on which the setter was defined. This allows for tailored behavior based on options passed in the schema. + * + * function inspector (val, priorValue, schematype) { + * if (schematype.options.required) { + * return schematype.path + ' is required'; + * } else { + * return val; + * } + * } + * + * const VirusSchema = new Schema({ + * name: { type: String, required: true, set: inspector }, + * taxonomy: { type: String, set: inspector } + * }) + * + * const Virus = db.model('Virus', VirusSchema); + * const v = new Virus({ name: 'Parvoviridae', taxonomy: 'Parvovirinae' }); + * + * console.log(v.name); // name is required + * console.log(v.taxonomy); // Parvovirinae + * + * You can also use setters to modify other properties on the document. If + * you're setting a property `name` on a document, the setter will run with + * `this` as the document. Be careful, in mongoose 5 setters will also run + * when querying by `name` with `this` as the query. + * + * const nameSchema = new Schema({ name: String, keywords: [String] }); + * nameSchema.path('name').set(function(v) { + * // Need to check if `this` is a document, because in mongoose 5 + * // setters will also run on queries, in which case `this` will be a + * // mongoose query object. + * if (this instanceof Document && v != null) { + * this.keywords = v.split(' '); + * } + * return v; + * }); + * + * @param {Function} fn + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.set = function(fn) { + if (typeof fn !== 'function') { + throw new TypeError('A setter must be a function.'); + } + this.setters.push(fn); + return this; +}; + +/** + * Adds a getter to this schematype. + * + * #### Example: + * + * function dob (val) { + * if (!val) return val; + * return (val.getMonth() + 1) + "/" + val.getDate() + "/" + val.getFullYear(); + * } + * + * // defining within the schema + * const s = new Schema({ born: { type: Date, get: dob }) + * + * // or by retreiving its SchemaType + * const s = new Schema({ born: Date }) + * s.path('born').get(dob) + * + * Getters allow you to transform the representation of the data as it travels from the raw mongodb document to the value that you see. + * + * Suppose you are storing credit card numbers and you want to hide everything except the last 4 digits to the mongoose user. You can do so by defining a getter in the following way: + * + * function obfuscate (cc) { + * return '****-****-****-' + cc.slice(cc.length-4, cc.length); + * } + * + * const AccountSchema = new Schema({ + * creditCardNumber: { type: String, get: obfuscate } + * }); + * + * const Account = db.model('Account', AccountSchema); + * + * Account.findById(id, function (err, found) { + * console.log(found.creditCardNumber); // '****-****-****-1234' + * }); + * + * Getters are also passed a second argument, the schematype on which the getter was defined. This allows for tailored behavior based on options passed in the schema. + * + * function inspector (val, priorValue, schematype) { + * if (schematype.options.required) { + * return schematype.path + ' is required'; + * } else { + * return schematype.path + ' is not'; + * } + * } + * + * const VirusSchema = new Schema({ + * name: { type: String, required: true, get: inspector }, + * taxonomy: { type: String, get: inspector } + * }) + * + * const Virus = db.model('Virus', VirusSchema); + * + * Virus.findById(id, function (err, virus) { + * console.log(virus.name); // name is required + * console.log(virus.taxonomy); // taxonomy is not + * }) + * + * @param {Function} fn + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.get = function(fn) { + if (typeof fn !== 'function') { + throw new TypeError('A getter must be a function.'); + } + this.getters.push(fn); + return this; +}; + +/** + * Adds multiple validators for this document path. + * Calls `validate()` for every element in validators. + * + * @param {(RegExp|Function|object)[]} validators + * @returns {SchemaType} + */ + +SchemaType.prototype.validateAll = function(validators) { + for (let i = 0; i < validators.length; i++) { + this.validate(validators[i]); + } + return this; +}; + +/** + * Adds validator(s) for this document path. + * + * Validators always receive the value to validate as their first argument and + * must return `Boolean`. Returning `false` or throwing an error means + * validation failed. + * + * The error message argument is optional. If not passed, the [default generic error message template](https://mongoosejs.com/docs/api/error.html#Error.messages) will be used. + * + * #### Example: + * + * // make sure every value is equal to "something" + * function validator (val) { + * return val === 'something'; + * } + * new Schema({ name: { type: String, validate: validator }}); + * + * // with a custom error message + * + * const custom = [validator, 'Uh oh, {PATH} does not equal "something".'] + * new Schema({ name: { type: String, validate: custom }}); + * + * // adding many validators at a time + * + * const many = [ + * { validator: validator, message: 'uh oh' } + * , { validator: anotherValidator, message: 'failed' } + * ] + * new Schema({ name: { type: String, validate: many }}); + * + * // or utilizing SchemaType methods directly: + * + * const schema = new Schema({ name: 'string' }); + * schema.path('name').validate(validator, 'validation of `{PATH}` failed with value `{VALUE}`'); + * + * #### Error message templates: + * + * Below is a list of supported template keywords: + * + * - PATH: The schema path where the error is being triggered. + * - VALUE: The value assigned to the PATH that is triggering the error. + * - KIND: The validation property that triggered the error i.e. required. + * - REASON: The error object that caused this error if there was one. + * + * If Mongoose's built-in error message templating isn't enough, Mongoose + * supports setting the `message` property to a function. + * + * schema.path('name').validate({ + * validator: function(v) { return v.length > 5; }, + * // `errors['name']` will be "name must have length 5, got 'foo'" + * message: function(props) { + * return `${props.path} must have length 5, got '${props.value}'`; + * } + * }); + * + * To bypass Mongoose's error messages and just copy the error message that + * the validator throws, do this: + * + * schema.path('name').validate({ + * validator: function() { throw new Error('Oops!'); }, + * // `errors['name'].message` will be "Oops!" + * message: function(props) { return props.reason.message; } + * }); + * + * #### Asynchronous validation: + * + * Mongoose supports validators that return a promise. A validator that returns + * a promise is called an _async validator_. Async validators run in + * parallel, and `validate()` will wait until all async validators have settled. + * + * schema.path('name').validate({ + * validator: function (value) { + * return new Promise(function (resolve, reject) { + * resolve(false); // validation failed + * }); + * } + * }); + * + * You might use asynchronous validators to retreive other documents from the database to validate against or to meet other I/O bound validation needs. + * + * Validation occurs `pre('save')` or whenever you manually execute [document#validate](https://mongoosejs.com/docs/api/document.html#Document.prototype.validate()). + * + * If validation fails during `pre('save')` and no callback was passed to receive the error, an `error` event will be emitted on your Models associated db [connection](https://mongoosejs.com/docs/api/connection.html#Connection()), passing the validation error object along. + * + * const conn = mongoose.createConnection(..); + * conn.on('error', handleError); + * + * const Product = conn.model('Product', yourSchema); + * const dvd = new Product(..); + * dvd.save(); // emits error on the `conn` above + * + * If you want to handle these errors at the Model level, add an `error` + * listener to your Model as shown below. + * + * // registering an error listener on the Model lets us handle errors more locally + * Product.on('error', handleError); + * + * @param {RegExp|Function|object} obj validator function, or hash describing options + * @param {Function} [obj.validator] validator function. If the validator function returns `undefined` or a truthy value, validation succeeds. If it returns [falsy](https://masteringjs.io/tutorials/fundamentals/falsy) (except `undefined`) or throws an error, validation fails. + * @param {string|Function} [obj.message] optional error message. If function, should return the error message as a string + * @param {boolean} [obj.propsParameter=false] If true, Mongoose will pass the validator properties object (with the `validator` function, `message`, etc.) as the 2nd arg to the validator function. This is disabled by default because many validators [rely on positional args](https://github.com/chriso/validator.js#validators), so turning this on may cause unpredictable behavior in external validators. + * @param {string|Function} [errorMsg] optional error message. If function, should return the error message as a string + * @param {string} [type] optional validator type + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.validate = function(obj, message, type) { + if (typeof obj === 'function' || obj && utils.getFunctionName(obj.constructor) === 'RegExp') { + let properties; + if (typeof message === 'function') { + properties = { validator: obj, message: message }; + properties.type = type || 'user defined'; + } else if (message instanceof Object && !type) { + properties = isSimpleValidator(message) ? Object.assign({}, message) : clone(message); + if (!properties.message) { + properties.message = properties.msg; + } + properties.validator = obj; + properties.type = properties.type || 'user defined'; + } else { + if (message == null) { + message = MongooseError.messages.general.default; + } + if (!type) { + type = 'user defined'; + } + properties = { message: message, type: type, validator: obj }; + } + + this.validators.push(properties); + return this; + } + + let i; + let length; + let arg; + + for (i = 0, length = arguments.length; i < length; i++) { + arg = arguments[i]; + if (!utils.isPOJO(arg)) { + const msg = 'Invalid validator. Received (' + typeof arg + ') ' + + arg + + '. See https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.validate()'; + + throw new MongooseError(msg); + } + this.validate(arg.validator, arg); + } + + return this; +}; + +/** + * Adds a required validator to this SchemaType. The validator gets added + * to the front of this SchemaType's validators array using `unshift()`. + * + * #### Example: + * + * const s = new Schema({ born: { type: Date, required: true }) + * + * // or with custom error message + * + * const s = new Schema({ born: { type: Date, required: '{PATH} is required!' }) + * + * // or with a function + * + * const s = new Schema({ + * userId: ObjectId, + * username: { + * type: String, + * required: function() { return this.userId != null; } + * } + * }) + * + * // or with a function and a custom message + * const s = new Schema({ + * userId: ObjectId, + * username: { + * type: String, + * required: [ + * function() { return this.userId != null; }, + * 'username is required if id is specified' + * ] + * } + * }) + * + * // or through the path API + * + * s.path('name').required(true); + * + * // with custom error messaging + * + * s.path('name').required(true, 'grrr :( '); + * + * // or make a path conditionally required based on a function + * const isOver18 = function() { return this.age >= 18; }; + * s.path('voterRegistrationId').required(isOver18); + * + * The required validator uses the SchemaType's `checkRequired` function to + * determine whether a given value satisfies the required validator. By default, + * a value satisfies the required validator if `val != null` (that is, if + * the value is not null nor undefined). However, most built-in mongoose schema + * types override the default `checkRequired` function: + * + * @param {boolean|Function|object} required enable/disable the validator, or function that returns required boolean, or options object + * @param {boolean|Function} [options.isRequired] enable/disable the validator, or function that returns required boolean + * @param {Function} [options.ErrorConstructor] custom error constructor. The constructor receives 1 parameter, an object containing the validator properties. + * @param {string} [message] optional custom error message + * @return {SchemaType} this + * @see Customized Error Messages https://mongoosejs.com/docs/api/error.html#Error.messages + * @see SchemaArray#checkRequired https://mongoosejs.com/docs/api/schemaarray.html#SchemaArray.prototype.checkRequired() + * @see SchemaBoolean#checkRequired https://mongoosejs.com/docs/api/schemaboolean.html#SchemaBoolean.prototype.checkRequired() + * @see SchemaBuffer#checkRequired https://mongoosejs.com/docs/api/schemabuffer.html#SchemaBuffer.prototype.checkRequired() + * @see SchemaNumber#checkRequired https://mongoosejs.com/docs/api/schemanumber.html#SchemaNumber.prototype.checkRequired() + * @see SchemaObjectId#checkRequired https://mongoosejs.com/docs/api/schemaobjectid.html#ObjectId.prototype.checkRequired() + * @see SchemaString#checkRequired https://mongoosejs.com/docs/api/schemastring.html#SchemaString.prototype.checkRequired() + * @api public + */ + +SchemaType.prototype.required = function(required, message) { + let customOptions = {}; + + if (arguments.length > 0 && required == null) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.requiredValidator; + }, this); + + this.isRequired = false; + delete this.originalRequiredValue; + return this; + } + + if (typeof required === 'object') { + customOptions = required; + message = customOptions.message || message; + required = required.isRequired; + } + + if (required === false) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.requiredValidator; + }, this); + + this.isRequired = false; + delete this.originalRequiredValue; + return this; + } + + const _this = this; + this.isRequired = true; + + this.requiredValidator = function(v) { + const cachedRequired = this?.$__?.cachedRequired; + + // no validation when this path wasn't selected in the query. + if (cachedRequired != null && !this.$__isSelected(_this.path) && !this[documentIsModified](_this.path)) { + return true; + } + + // `$cachedRequired` gets set in `_evaluateRequiredFunctions()` so we + // don't call required functions multiple times in one validate call + // See gh-6801 + if (cachedRequired != null && _this.path in cachedRequired) { + const res = cachedRequired[_this.path] ? + _this.checkRequired(v, this) : + true; + delete cachedRequired[_this.path]; + return res; + } else if (typeof required === 'function') { + return required.apply(this) ? _this.checkRequired(v, this) : true; + } + + return _this.checkRequired(v, this); + }; + this.originalRequiredValue = required; + + if (typeof required === 'string') { + message = required; + required = undefined; + } + + const msg = message || MongooseError.messages.general.required; + this.validators.unshift(Object.assign({}, customOptions, { + validator: this.requiredValidator, + message: msg, + type: 'required' + })); + + return this; +}; + +/** + * Set the model that this path refers to. This is the option that [populate](https://mongoosejs.com/docs/populate.html) + * looks at to determine the foreign collection it should query. + * + * #### Example: + * + * const userSchema = new Schema({ name: String }); + * const User = mongoose.model('User', userSchema); + * + * const postSchema = new Schema({ user: mongoose.ObjectId }); + * postSchema.path('user').ref('User'); // Can set ref to a model name + * postSchema.path('user').ref(User); // Or a model class + * postSchema.path('user').ref(() => 'User'); // Or a function that returns the model name + * postSchema.path('user').ref(() => User); // Or a function that returns the model class + * + * // Or you can just declare the `ref` inline in your schema + * const postSchema2 = new Schema({ + * user: { type: mongoose.ObjectId, ref: User } + * }); + * + * @param {string|Model|Function} ref either a model name, a [Model](https://mongoosejs.com/docs/models.html), or a function that returns a model name or model. + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.ref = function(ref) { + this.options.ref = ref; + return this; +}; + +/** + * Gets the default value + * + * @param {object} parentDoc + * @param {boolean} init + * @param {object} options + * @param {object} [options.context] + * @param {boolean} [options.skipCast] + * @return {any} The Stored default value. + * @api private + */ + +SchemaType.prototype.getDefault = function getDefault(parentDoc, init, options) { + let ret; + if (this.defaultValue == null) { + return this.defaultValue; + } + if (typeof this.defaultValue === 'function') { + // In setDefaultsOnInsert, we pass the Query as `context` option. This is used to + // avoid passing the query as the parent doc to `applySetters()` below, because + // applySetters assumes parentDoc is a document or null. Queries run setters + // separately anyway. + const context = options?.context ?? parentDoc; + if ( + this.defaultValue === Date.now || + this.defaultValue === Array || + this.defaultValue.name.toLowerCase() === 'objectid' + ) { + ret = this.defaultValue.call(context); + } else { + ret = this.defaultValue.call(context, context); + } + } else { + ret = this.defaultValue; + } + + if (ret != null) { + if (typeof ret === 'object' && !this.options?.shared) { + ret = clone(ret); + } + + if (options?.skipCast) { + return this._applySetters(ret, parentDoc); + } + + const casted = this.applySetters(ret, parentDoc, init, undefined, setOptionsForDefaults); + if (casted && !Array.isArray(casted) && casted.$isSingleNested) { + casted.$__parent = parentDoc; + } + return casted; + } + return ret; +}; + +/** + * Applies setters without casting + * + * @param {any} value + * @param {any} scope + * @param {boolean} init + * @param {any} priorVal + * @param {object} [options] + * @instance + * @api private + */ + +SchemaType.prototype._applySetters = function(value, scope, init, priorVal, options) { + let v = value; + if (init) { + return v; + } + const setters = this.setters; + + for (let i = setters.length - 1; i >= 0; i--) { + v = setters[i].call(scope, v, priorVal, this, options); + } + + return v; +}; + +/*! + * ignore + */ + +SchemaType.prototype._castNullish = function _castNullish(v) { + return v; +}; + +/** + * Applies setters + * + * @param {any} value + * @param {object} scope + * @param {boolean} init + * @return {any} + * @api private + */ + +SchemaType.prototype.applySetters = function(value, scope, init, priorVal, options) { + let v = this._applySetters(value, scope, init, priorVal, options); + if (v == null) { + return this._castNullish(v); + } + // do not cast until all setters are applied #665 + v = this.cast(v, scope, init, priorVal, options); + + return v; +}; + +/** + * Applies getters to a value + * + * @param {object} value + * @param {object} scope + * @return {any} + * @api private + */ + +SchemaType.prototype.applyGetters = function(value, scope) { + let v = value; + const getters = this.getters; + const len = getters.length; + + if (len === 0) { + return v; + } + + for (let i = 0; i < len; ++i) { + v = getters[i].call(scope, v, this); + } + + return v; +}; + +/** + * Sets default `select()` behavior for this path. + * + * Set to `true` if this path should always be included in the results, `false` if it should be excluded by default. This setting can be overridden at the query level. + * + * #### Example: + * + * T = db.model('T', new Schema({ x: { type: String, select: true }})); + * T.find(..); // field x will always be selected .. + * // .. unless overridden; + * T.find().select('-x').exec(callback); + * + * @param {boolean} val + * @return {SchemaType} this + * @api public + */ + +SchemaType.prototype.select = function select(val) { + this.selected = !!val; + return this; +}; + +/** + * Performs a validation of `value` using the validators declared for this SchemaType. + * + * @param {any} value + * @param {object} scope + * @param {object} [options] + * @param {string} [options.path] + * @return {any} If no validators, returns the output from calling `fn`, otherwise no return + * @api public + */ + +SchemaType.prototype.doValidate = async function doValidate(value, scope, options) { + let err = false; + const path = this.path; + + // Avoid non-object `validators` + const validators = this.validators. + filter(v => typeof v === 'object' && v !== null); + + if (!validators.length) { + return; + } + + const promises = []; + for (let i = 0, len = validators.length; i < len; ++i) { + const v = validators[i]; + const validator = v.validator; + let ok; + + const validatorProperties = isSimpleValidator(v) ? Object.assign({}, v) : clone(v); + validatorProperties.path = options?.path || path; + validatorProperties.fullPath = this.$fullPath; + validatorProperties.value = value; + if (typeof value === 'string') { + validatorProperties.length = value.length; + if (validatorProperties.value.length > 30) { + validatorProperties.value = validatorProperties.value.slice(0, 30) + '...'; + } + } + + if (value === undefined && validator !== this.requiredValidator) { + continue; + } + if (validator instanceof RegExp) { + ok = validator.test(value); + if (ok === false) { + const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError; + err = new ErrorConstructor(validatorProperties, scope); + err[validatorErrorSymbol] = true; + throw err; + } + continue; + } else if (typeof validator !== 'function') { + continue; + } + + try { + if (validatorProperties.propsParameter) { + ok = validator.call(scope, value, validatorProperties); + } else { + ok = validator.call(scope, value); + } + } catch (error) { + ok = false; + validatorProperties.reason = error; + if (error.message) { + validatorProperties.message = error.message; + } + } + + if (typeof ok?.then === 'function') { + promises.push( + ok.then( + function(ok) { + if (ok === false) { + const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError; + err = new ErrorConstructor(validatorProperties, scope); + err[validatorErrorSymbol] = true; + throw err; + } + }, + function(error) { + validatorProperties.reason = error; + validatorProperties.message = error.message; + ok = false; + const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError; + err = new ErrorConstructor(validatorProperties, scope); + err[validatorErrorSymbol] = true; + throw err; + }) + ); + } else if (ok !== undefined && !ok) { + const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError; + err = new ErrorConstructor(validatorProperties, scope); + err[validatorErrorSymbol] = true; + throw err; + } + } + + await Promise.all(promises); +}; + + +function _validate(ok, validatorProperties) { + if (ok !== undefined && !ok) { + const ErrorConstructor = validatorProperties.ErrorConstructor || ValidatorError; + const err = new ErrorConstructor(validatorProperties); + err[validatorErrorSymbol] = true; + return err; + } +} + +/** + * Performs a validation of `value` using the validators declared for this SchemaType. + * + * #### Note: + * + * This method ignores the asynchronous validators. + * + * @param {any} value + * @param {object} scope + * @param {object} [options] + * @param {string} [options.path] + * @return {MongooseError|null} + * @api private + */ + +SchemaType.prototype.doValidateSync = function(value, scope, options) { + const path = this.path; + const count = this.validators.length; + + if (!count) { + return null; + } + + let validators = this.validators; + if (value === void 0) { + if (this.validators.length !== 0 && this.validators[0].type === 'required') { + validators = [this.validators[0]]; + } else { + return null; + } + } + + let err = null; + let i = 0; + const len = validators.length; + for (i = 0; i < len; ++i) { + const v = validators[i]; + + if (v === null || typeof v !== 'object') { + continue; + } + + const validator = v.validator; + const validatorProperties = isSimpleValidator(v) ? Object.assign({}, v) : clone(v); + validatorProperties.path = options?.path || path; + validatorProperties.fullPath = this.$fullPath; + validatorProperties.value = value; + if (typeof value === 'string') { + validatorProperties.length = value.length; + if (validatorProperties.value.length > 30) { + validatorProperties.value = validatorProperties.value.slice(0, 30) + '...'; + } + } + let ok = false; + + // Skip any explicit async validators. Validators that return a promise + // will still run, but won't trigger any errors. + if (isAsyncFunction(validator)) { + continue; + } + + if (validator instanceof RegExp) { + err = _validate(validator.test(value), validatorProperties); + continue; + } + + if (typeof validator !== 'function') { + continue; + } + + try { + if (validatorProperties.propsParameter) { + ok = validator.call(scope, value, validatorProperties); + } else { + ok = validator.call(scope, value); + } + } catch (error) { + ok = false; + validatorProperties.reason = error; + } + + // Skip any validators that return a promise, we can't handle those + // synchronously + if (typeof ok?.then === 'function') { + continue; + } + err = _validate(ok, validatorProperties); + if (err) { + break; + } + } + + return err; +}; + +/** + * Determines if value is a valid Reference. + * + * @param {SchemaType} self + * @param {object} value + * @param {Document} doc + * @param {boolean} init + * @return {boolean} + * @api private + */ + +SchemaType._isRef = function(self, value, doc, init) { + // fast path + let ref = init && (self.options?.ref || self.options?.refPath); + + if (!ref && doc?.$__ != null) { + // checks for + // - this populated with adhoc model and no ref was set in schema OR + // - setting / pushing values after population + const path = doc.$__fullPath(self.path, true); + + const owner = doc.ownerDocument(); + ref = (path != null && owner.$populated(path)) || doc.$populated(self.path); + } + + if (ref) { + if (value == null) { + return true; + } + if (!Buffer.isBuffer(value) && // buffers are objects too + value._bsontype !== 'Binary' // raw binary value from the db + && utils.isObject(value) // might have deselected _id in population query + ) { + return true; + } + + return init; + } + + return false; +}; + +/*! + * ignore + */ + +SchemaType.prototype._castRef = function _castRef(value, doc, init, options) { + if (value == null) { + return value; + } + + if (value.$__ != null) { + value.$__.wasPopulated = value.$__.wasPopulated || { value: value._doc._id }; + return value; + } + + // setting a populated path + if (Buffer.isBuffer(value) || !utils.isObject(value)) { + if (init) { + return value; + } + throw new CastError(this.instance, value, this.path, null, this); + } + + // Handle the case where user directly sets a populated + // path to a plain object; cast to the Model used in + // the population query. + const path = doc.$__fullPath(this.path, true); + const owner = doc.ownerDocument(); + const pop = owner.$populated(path, true); + + let ret = value; + if (!doc.$__.populated || + !doc.$__.populated[path] || + !doc.$__.populated[path].options || + !doc.$__.populated[path].options.options || + !doc.$__.populated[path].options.options.lean) { + const PopulatedModel = pop ? pop.options[populateModelSymbol] : doc.constructor.db.model(this.options.ref); + ret = PopulatedModel.hydrate(value, null, options); + ret.$__.wasPopulated = { value: ret._doc._id, options: { [populateModelSymbol]: PopulatedModel } }; + } + + return ret; +}; + +/*! + * ignore + */ + +function handleSingle(val, context) { + return this.castForQuery(null, val, context); +} + +/*! + * ignore + */ + +function handleArray(val, context) { + const _this = this; + if (!Array.isArray(val)) { + return [this.castForQuery(null, val, context)]; + } + return val.map(function(m) { + return _this.castForQuery(null, m, context); + }); +} + +/** + * Just like handleArray, except also allows `[]` because surprisingly + * `$in: [1, []]` works fine + * @api private + */ + +function handle$in(val, context) { + const _this = this; + if (!Array.isArray(val)) { + return [this.castForQuery(null, val, context)]; + } + return val.map(function(m) { + if (Array.isArray(m) && m.length === 0) { + return m; + } + return _this.castForQuery(null, m, context); + }); +} + +/** + * Contains the handlers for different query operators for this schema type. + * For example, `$conditionalHandlers.$exists` is the function Mongoose calls to cast `$exists` filter operators. + * + * @property $conditionalHandlers + * @memberOf SchemaType + * @instance + * @api public + */ + +SchemaType.prototype.$conditionalHandlers = { + $all: handleArray, + $eq: handleSingle, + $in: handle$in, + $ne: handleSingle, + $nin: handle$in, + $exists: $exists, + $type: $type +}; + +/** + * Cast the given value with the given optional query operator. + * + * @param {string} [$conditional] query operator, like `$eq` or `$in` + * @param {any} val + * @param {Query} context + * @return {any} + * @api private + */ + +SchemaType.prototype.castForQuery = function($conditional, val, context) { + let handler; + if ($conditional != null) { + handler = this.$conditionalHandlers[$conditional]; + if (!handler) { + throw new MongooseError('Can\'t use ' + $conditional); + } + return handler.call(this, val, context); + } + + try { + return this.applySetters(val, context); + } catch (err) { + if (err instanceof CastError && err.path === this.path && this.$fullPath != null) { + err.path = this.$fullPath; + } + throw err; + } +}; + +/** + * Set & Get the `checkRequired` function + * Override the function the required validator uses to check whether a value + * passes the `required` check. Override this on the individual SchemaType. + * + * #### Example: + * + * // Use this to allow empty strings to pass the `required` validator + * mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string'); + * + * @param {Function} [fn] If set, will overwrite the current set function + * @return {Function} The input `fn` or the already set function + * @static + * @memberOf SchemaType + * @function checkRequired + * @api public + */ + +SchemaType.checkRequired = function(fn) { + if (arguments.length !== 0) { + this._checkRequired = fn; + } + + return this._checkRequired; +}; + +/** + * Default check for if this path satisfies the `required` validator. + * + * @param {any} val + * @return {boolean} `true` when the value is not `null`, `false` otherwise + * @api private + */ + +SchemaType.prototype.checkRequired = function(val) { + return val != null; +}; + +/** + * Clone the current SchemaType + * + * @return {SchemaType} The cloned SchemaType instance + * @api private + */ + +SchemaType.prototype.clone = function() { + const options = Object.assign({}, this.options); + const schematype = new this.constructor(this.path, options, this.instance, this.parentSchema); + schematype.validators = this.validators.slice(); + if (this.requiredValidator !== undefined) schematype.requiredValidator = this.requiredValidator; + if (this.defaultValue !== undefined) schematype.defaultValue = this.defaultValue; + if (this.$immutable !== undefined && this.options.immutable === undefined) { + schematype.$immutable = this.$immutable; + + handleImmutable(schematype); + } + if (this._index !== undefined) schematype._index = this._index; + if (this.selected !== undefined) schematype.selected = this.selected; + if (this.isRequired !== undefined) schematype.isRequired = this.isRequired; + if (this.originalRequiredValue !== undefined) schematype.originalRequiredValue = this.originalRequiredValue; + schematype.getters = this.getters.slice(); + schematype.setters = this.setters.slice(); + return schematype; +}; + +/** + * Returns the embedded schema type, if any. For arrays, document arrays, and maps, `getEmbeddedSchemaType()` + * returns the schema type of the array's elements (or map's elements). For other types, `getEmbeddedSchemaType()` + * returns `undefined`. + * + * #### Example: + * + * const schema = new Schema({ name: String, tags: [String] }); + * schema.path('name').getEmbeddedSchemaType(); // undefined + * schema.path('tags').getEmbeddedSchemaType(); // SchemaString { path: 'tags', ... } + * + * @returns {SchemaType} embedded schematype + * @api public + */ + +SchemaType.prototype.getEmbeddedSchemaType = function getEmbeddedSchemaType() { + return this.embeddedSchemaType; +}; + +/*! + * If _duplicateKeyErrorMessage is a string, replace unique index errors "E11000 duplicate key error" with this string. + * + * @api private + */ + +SchemaType.prototype._duplicateKeyErrorMessage = null; + +/** + * Returns this schema type's representation in a JSON schema. + * + * @param {object} [options] + * @param {boolean} [options.useBsonType=false] If true, return a representation with `bsonType` for use with MongoDB's `$jsonSchema`. + * @returns {object} JSON schema properties + */ + +SchemaType.prototype.toJSONSchema = function toJSONSchema(_options) { // eslint-disable-line no-unused-vars + throw new MongooseError(`Converting unsupported SchemaType to JSON Schema: ${this.instance} at path "${this.path}"`); +}; + +/** + * Returns the BSON type that the schema corresponds to, for automatic encryption. + * @api private + */ +SchemaType.prototype.autoEncryptionType = function autoEncryptionType() { + return null; +}; + +/*! + * Module exports. + */ + +module.exports = exports = SchemaType; + +exports.CastError = CastError; + +exports.ValidatorError = ValidatorError; diff --git a/backend/node_modules/mongoose/lib/stateMachine.js b/backend/node_modules/mongoose/lib/stateMachine.js new file mode 100644 index 0000000..ddd223b --- /dev/null +++ b/backend/node_modules/mongoose/lib/stateMachine.js @@ -0,0 +1,229 @@ + +/*! + * Module dependencies. + */ + +'use strict'; + +const utils = require('./utils'); // eslint-disable-line no-unused-vars + +/** + * StateMachine represents a minimal `interface` for the + * constructors it builds via StateMachine.ctor(...). + * + * @api private + */ + +const StateMachine = module.exports = exports = function StateMachine() { +}; + +/** + * StateMachine.ctor('state1', 'state2', ...) + * A factory method for subclassing StateMachine. + * The arguments are a list of states. For each state, + * the constructor's prototype gets state transition + * methods named after each state. These transition methods + * place their path argument into the given state. + * + * @param {...string} state one or more state names + * @return {Function} subclass constructor + * @api private + */ + +StateMachine.ctor = function() { + const states = [...arguments]; + + const ctor = function() { + StateMachine.apply(this, arguments); + this.paths = {}; + this.states = {}; + }; + + ctor.prototype = new StateMachine(); + ctor.prototype.constructor = ctor; + + ctor.prototype.stateNames = states; + + states.forEach(function(state) { + // Changes the `path`'s state to `state`. + ctor.prototype[state] = function(path) { + this._changeState(path, state); + }; + }); + + return ctor; +}; + +/** + * This function is wrapped by the state change functions: + * + * - `require(path)` + * - `modify(path)` + * - `init(path)` + * + * @api private + */ + +StateMachine.prototype._changeState = function _changeState(path, nextState) { + const prevState = this.paths[path]; + if (prevState === nextState) { + return; + } + const prevBucket = this.states[prevState]; + if (prevBucket) delete prevBucket[path]; + + this.paths[path] = nextState; + this.states[nextState] = this.states[nextState] || {}; + this.states[nextState][path] = true; +}; + +/*! + * ignore + */ + +StateMachine.prototype.clear = function clear(state) { + if (this.states[state] == null) { + return; + } + const keys = Object.keys(this.states[state]); + let i = keys.length; + let path; + + while (i--) { + path = keys[i]; + delete this.states[state][path]; + delete this.paths[path]; + } +}; + +/*! + * ignore + */ + +StateMachine.prototype.clearPath = function clearPath(path) { + const state = this.paths[path]; + if (!state) { + return; + } + delete this.paths[path]; + delete this.states[state][path]; +}; + +/** + * Gets the paths for the given state, or empty object `{}` if none. + * @api private + */ + +StateMachine.prototype.getStatePaths = function getStatePaths(state) { + if (this.states[state] != null) { + return this.states[state]; + } + return {}; +}; + +/** + * Checks to see if at least one path is in the states passed in via `arguments` + * e.g., this.some('required', 'inited') + * + * @param {string} state that we want to check for. + * @api private + */ + +StateMachine.prototype.some = function some() { + const _this = this; + const what = arguments.length ? arguments : this.stateNames; + return Array.prototype.some.call(what, function(state) { + if (_this.states[state] == null) { + return false; + } + return Object.keys(_this.states[state]).length; + }); +}; + +/** + * This function builds the functions that get assigned to `forEach` and `map`, + * since both of those methods share a lot of the same logic. + * + * @param {string} iterMethod is either 'forEach' or 'map' + * @return {Function} + * @api private + */ + +StateMachine.prototype._iter = function _iter(iterMethod) { + return function() { + let states = [...arguments]; + const callback = states.pop(); + + if (!states.length) states = this.stateNames; + + const _this = this; + + const paths = states.reduce(function(paths, state) { + if (_this.states[state] == null) { + return paths; + } + return paths.concat(Object.keys(_this.states[state])); + }, []); + + return paths[iterMethod](function(path, i, paths) { + return callback(path, i, paths); + }); + }; +}; + +/** + * Iterates over the paths that belong to one of the parameter states. + * + * The function profile can look like: + * this.forEach(state1, fn); // iterates over all paths in state1 + * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2 + * this.forEach(fn); // iterates over all paths in all states + * + * @param {...string} [state] one or more state names to filter by + * @param {Function} callback + * @api private + */ + +StateMachine.prototype.forEach = function forEach() { + this.forEach = this._iter('forEach'); + return this.forEach.apply(this, arguments); +}; + +/** + * Maps over the paths that belong to one of the parameter states. + * + * The function profile can look like: + * this.forEach(state1, fn); // iterates over all paths in state1 + * this.forEach(state1, state2, fn); // iterates over all paths in state1 or state2 + * this.forEach(fn); // iterates over all paths in all states + * + * @param {...string} [state] one or more state names to filter by + * @param {Function} callback + * @return {Array} + * @api private + */ + +StateMachine.prototype.map = function map() { + this.map = this._iter('map'); + return this.map.apply(this, arguments); +}; + +/** + * Returns a copy of this state machine + * + * @param {Function} callback + * @return {StateMachine} + * @api private + */ + +StateMachine.prototype.clone = function clone() { + const result = new this.constructor(); + result.paths = { ...this.paths }; + for (const state of this.stateNames) { + if (!(state in this.states)) { + continue; + } + result.states[state] = this.states[state] == null ? this.states[state] : { ...this.states[state] }; + } + return result; +}; diff --git a/backend/node_modules/mongoose/lib/types/array/index.js b/backend/node_modules/mongoose/lib/types/array/index.js new file mode 100644 index 0000000..b069be7 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/array/index.js @@ -0,0 +1,119 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const mongooseArrayMethods = require('./methods'); + +const arrayAtomicsSymbol = require('../../helpers/symbols').arrayAtomicsSymbol; +const arrayAtomicsBackupSymbol = require('../../helpers/symbols').arrayAtomicsBackupSymbol; +const arrayParentSymbol = require('../../helpers/symbols').arrayParentSymbol; +const arrayPathSymbol = require('../../helpers/symbols').arrayPathSymbol; +const arraySchemaSymbol = require('../../helpers/symbols').arraySchemaSymbol; + +/** + * Mongoose Array constructor. + * + * #### Note: + * + * _Values always have to be passed to the constructor to initialize, otherwise `MongooseArray#push` will mark the array as modified._ + * + * @param {Array} values + * @param {string} path + * @param {Document} doc parent document + * @api private + * @inherits Array https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array + * @see https://bit.ly/f6CnZU + */ +const _basePush = Array.prototype.push; +const numberRE = /^\d+$/; + +function MongooseArray(values, path, doc, schematype) { + let __array; + + if (Array.isArray(values)) { + const len = values.length; + + // Perf optimizations for small arrays: much faster to use `...` than `for` + `push`, + // but large arrays may cause stack overflows. And for arrays of length 0/1, just + // modifying the array is faster. Seems small, but adds up when you have a document + // with thousands of nested arrays. + if (len === 0) { + __array = new Array(); + } else if (len === 1) { + __array = new Array(1); + __array[0] = values[0]; + } else if (len < 10000) { + __array = new Array(); + _basePush.apply(__array, values); + } else { + __array = new Array(); + for (let i = 0; i < len; ++i) { + _basePush.call(__array, values[i]); + } + } + } else { + __array = []; + } + + const internals = { + [arrayAtomicsSymbol]: {}, + [arrayAtomicsBackupSymbol]: void 0, + [arrayPathSymbol]: path, + [arraySchemaSymbol]: schematype, + [arrayParentSymbol]: void 0, + isMongooseArray: true, + isMongooseArrayProxy: true, + __array: __array + }; + + if (values && values[arrayAtomicsSymbol] != null) { + internals[arrayAtomicsSymbol] = values[arrayAtomicsSymbol]; + } + + if (doc?.$__) { + internals[arrayParentSymbol] = doc; + internals[arraySchemaSymbol] = schematype || doc.schema.path(path); + } + + const proxy = new Proxy(__array, { + get: function(target, prop) { + if (Object.hasOwn(internals, prop)) { + return internals[prop]; + } + if (Object.hasOwn(mongooseArrayMethods, prop)) { + return mongooseArrayMethods[prop]; + } + if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { + return schematype.virtuals[prop].applyGetters(undefined, target); + } + if (typeof prop === 'string' && numberRE.test(prop) && schematype?.embeddedSchemaType != null) { + return schematype.embeddedSchemaType.applyGetters(__array[prop], doc); + } + + return __array[prop]; + }, + set: function(target, prop, value) { + if (typeof prop === 'string' && numberRE.test(prop)) { + mongooseArrayMethods.set.call(proxy, prop, value, false); + } else if (Object.hasOwn(internals, prop)) { + internals[prop] = value; + } else if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { + schematype.virtuals[prop].applySetters(value, target); + } else { + __array[prop] = value; + } + + return true; + } + }); + + return proxy; +} + +/*! + * Module exports. + */ + +module.exports = exports = MongooseArray; diff --git a/backend/node_modules/mongoose/lib/types/array/isMongooseArray.js b/backend/node_modules/mongoose/lib/types/array/isMongooseArray.js new file mode 100644 index 0000000..8932613 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/array/isMongooseArray.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.isMongooseArray = function(mongooseArray) { + return Array.isArray(mongooseArray) && mongooseArray.isMongooseArray; +}; diff --git a/backend/node_modules/mongoose/lib/types/array/methods/index.js b/backend/node_modules/mongoose/lib/types/array/methods/index.js new file mode 100644 index 0000000..c3c39d9 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/array/methods/index.js @@ -0,0 +1,1095 @@ +'use strict'; + +const Document = require('../../../document'); +const ArraySubdocument = require('../../arraySubdocument'); +const MongooseError = require('../../../error/mongooseError'); +const cleanModifiedSubpaths = require('../../../helpers/document/cleanModifiedSubpaths'); +const clone = require('../../../helpers/clone'); +const internalToObjectOptions = require('../../../options').internalToObjectOptions; +const mpath = require('mpath'); +const utils = require('../../../utils'); +const isBsonType = require('../../../helpers/isBsonType'); + +const arrayAtomicsBackupSymbol = require('../../../helpers/symbols').arrayAtomicsBackupSymbol; +const arrayAtomicsSymbol = require('../../../helpers/symbols').arrayAtomicsSymbol; +const arrayParentSymbol = require('../../../helpers/symbols').arrayParentSymbol; +const arrayPathSymbol = require('../../../helpers/symbols').arrayPathSymbol; +const arraySchemaSymbol = require('../../../helpers/symbols').arraySchemaSymbol; +const populateModelSymbol = require('../../../helpers/symbols').populateModelSymbol; +const slicedSymbol = Symbol('mongoose#Array#sliced'); + +const _basePush = Array.prototype.push; + +/*! + * ignore + */ + +const methods = { + /** + * Depopulates stored atomic operation values as necessary for direct insertion to MongoDB. + * + * If no atomics exist, we return all array values after conversion. + * + * @return {Array} + * @method $__getAtomics + * @memberOf MongooseArray + * @instance + * @api private + */ + + $__getAtomics() { + const ret = []; + const keys = Object.keys(this[arrayAtomicsSymbol] || {}); + let i = keys.length; + + const opts = Object.assign({}, internalToObjectOptions, { _isNested: true }); + + if (i === 0) { + ret[0] = ['$set', this.toObject(opts)]; + return ret; + } + + while (i--) { + const op = keys[i]; + let val = this[arrayAtomicsSymbol][op]; + + // the atomic values which are arrays are not MongooseArrays. we + // need to convert their elements as if they were MongooseArrays + // to handle populated arrays versus DocumentArrays properly. + if (utils.isMongooseObject(val)) { + val = val.toObject(opts); + } else if (Array.isArray(val)) { + val = this.toObject.call(val, opts); + } else if (Array.isArray(val?.$each)) { + val.$each = this.toObject.call(val.$each, opts); + } else if (typeof val?.valueOf === 'function') { + val = val.valueOf(); + } + + if (op === '$addToSet') { + val = { $each: val }; + } + + ret.push([op, val]); + } + + return ret; + }, + + /** + * Public API for getting atomics. Alias for $__getAtomics() that can be + * implemented by custom container types. + * + * @return {Array} + * @method getAtomics + * @memberOf MongooseArray + * @instance + * @api public + */ + + getAtomics() { + return this.$__getAtomics(); + }, + + /** + * Clears all pending atomic operations. Called by Mongoose after save(). + * + * @return {void} + * @method clearAtomics + * @memberOf MongooseArray + * @instance + * @api public + */ + + clearAtomics() { + this[arrayAtomicsBackupSymbol] = this[arrayAtomicsSymbol]; + this[arrayAtomicsSymbol] = {}; + }, + + /*! + * ignore + */ + + $atomics() { + return this[arrayAtomicsSymbol]; + }, + + /*! + * ignore + */ + + $parent() { + return this[arrayParentSymbol]; + }, + + /*! + * ignore + */ + + $path() { + return this[arrayPathSymbol]; + }, + + /*! + * ignore + */ + $schemaType() { + return this[arraySchemaSymbol]; + }, + + /** + * Atomically shifts the array at most one time per document `save()`. + * + * #### Note: + * + * _Calling this multiple times on an array before saving sends the same command as calling it once._ + * _This update is implemented using the MongoDB [$pop](https://www.mongodb.com/docs/manual/reference/operator/update/pop/) method which enforces this restriction._ + * + * doc.array = [1,2,3]; + * + * const shifted = doc.array.$shift(); + * console.log(shifted); // 1 + * console.log(doc.array); // [2,3] + * + * // no affect + * shifted = doc.array.$shift(); + * console.log(doc.array); // [2,3] + * + * doc.save(function (err) { + * if (err) return handleError(err); + * + * // we saved, now $shift works again + * shifted = doc.array.$shift(); + * console.log(shifted ); // 2 + * console.log(doc.array); // [3] + * }) + * + * @api public + * @memberOf MongooseArray + * @instance + * @method $shift + * @see mongodb https://www.mongodb.com/docs/manual/reference/operator/update/pop/ + */ + + $shift() { + this._registerAtomic('$pop', -1); + this._markModified(); + + // only allow shifting once + const __array = this.__array; + if (__array._shifted) { + return; + } + __array._shifted = true; + + return [].shift.call(__array); + }, + + /** + * Pops the array atomically at most one time per document `save()`. + * + * #### NOTE: + * + * _Calling this multiple times on an array before saving sends the same command as calling it once._ + * _This update is implemented using the MongoDB [$pop](https://www.mongodb.com/docs/manual/reference/operator/update/pop/) method which enforces this restriction._ + * + * doc.array = [1,2,3]; + * + * const popped = doc.array.$pop(); + * console.log(popped); // 3 + * console.log(doc.array); // [1,2] + * + * // no affect + * popped = doc.array.$pop(); + * console.log(doc.array); // [1,2] + * + * doc.save(function (err) { + * if (err) return handleError(err); + * + * // we saved, now $pop works again + * popped = doc.array.$pop(); + * console.log(popped); // 2 + * console.log(doc.array); // [1] + * }) + * + * @api public + * @method $pop + * @memberOf MongooseArray + * @instance + * @see mongodb https://www.mongodb.com/docs/manual/reference/operator/update/pop/ + * @method $pop + * @memberOf MongooseArray + */ + + $pop() { + this._registerAtomic('$pop', 1); + this._markModified(); + + // only allow popping once + if (this._popped) { + return; + } + this._popped = true; + + return [].pop.call(this); + }, + + /*! + * ignore + */ + + $schema() { + return this[arraySchemaSymbol]; + }, + + /** + * Casts a member based on this arrays schema. + * + * @param {any} value + * @return {any} the casted value + * @method _cast + * @api private + * @memberOf MongooseArray + */ + + _cast(value) { + let populated = false; + let Model; + + const parent = this[arrayParentSymbol]; + if (parent) { + populated = parent.$populated(this[arrayPathSymbol], true); + } + + if (populated && value != null) { + // cast to the populated Models schema + Model = populated.options[populateModelSymbol]; + if (Model == null) { + throw new MongooseError('No populated model found for path `' + this[arrayPathSymbol] + '`. This is likely a bug in Mongoose, please report an issue on github.com/Automattic/mongoose.'); + } + + // only objects are permitted so we can safely assume that + // non-objects are to be interpreted as _id + if (Buffer.isBuffer(value) || + isBsonType(value, 'ObjectId') || !utils.isObject(value)) { + value = { _id: value }; + } + + // gh-2399 + // we should cast model only when it's not a discriminator + const isDisc = value.schema?.discriminatorMapping?.key !== undefined; + if (!isDisc) { + value = new Model(value); + } + return this[arraySchemaSymbol].embeddedSchemaType.applySetters(value, parent, true); + } + + return this[arraySchemaSymbol].embeddedSchemaType.applySetters(value, parent, false); + }, + + /** + * Internal helper for .map() + * + * @api private + * @return {number} + * @method _mapCast + * @memberOf MongooseArray + */ + + _mapCast(val, index) { + return this._cast(val, this.length + index); + }, + + /** + * Marks this array as modified. + * + * If it bubbles up from an embedded document change, then it takes the following arguments (otherwise, takes 0 arguments) + * + * @param {ArraySubdocument} subdoc the embedded doc that invoked this method on the Array + * @param {string} embeddedPath the path which changed in the subdoc + * @method _markModified + * @api private + * @memberOf MongooseArray + */ + + _markModified(elem) { + const parent = this[arrayParentSymbol]; + let dirtyPath; + + if (parent) { + dirtyPath = this[arrayPathSymbol]; + + if (arguments.length) { + dirtyPath = dirtyPath + '.' + elem; + } + + if (dirtyPath != null && dirtyPath.endsWith('.$')) { + return this; + } + + parent.markModified(dirtyPath, arguments.length !== 0 ? elem : parent); + } + + return this; + }, + + /** + * Register an atomic operation with the parent. + * + * @param {Array} op operation + * @param {any} val + * @method _registerAtomic + * @api private + * @memberOf MongooseArray + */ + + _registerAtomic(op, val) { + if (this[slicedSymbol]) { + return; + } + if (op === '$set') { + // $set takes precedence over all other ops. + // mark entire array modified. + this[arrayAtomicsSymbol] = { $set: val }; + cleanModifiedSubpaths(this[arrayParentSymbol], this[arrayPathSymbol]); + this._markModified(); + return this; + } + + const atomics = this[arrayAtomicsSymbol]; + + // reset pop/shift after save + if (op === '$pop' && !('$pop' in atomics)) { + const _this = this; + this[arrayParentSymbol].once('save', function() { + _this._popped = _this._shifted = null; + }); + } + + // check for impossible $atomic combos (Mongo denies more than one + // $atomic op on a single path + if (atomics.$set || utils.hasOwnKeys(atomics) && !(op in atomics)) { + // a different op was previously registered. + // save the entire thing. + this[arrayAtomicsSymbol] = { $set: this }; + return this; + } + + let selector; + + if (op === '$pullAll' || op === '$addToSet') { + atomics[op] || (atomics[op] = []); + atomics[op] = atomics[op].concat(val); + } else if (op === '$pullDocs') { + const pullOp = atomics['$pull'] || (atomics['$pull'] = {}); + if (val[0] instanceof ArraySubdocument) { + selector = pullOp['$or'] || (pullOp['$or'] = []); + Array.prototype.push.apply(selector, val.map(v => { + return v.toObject({ + transform: (doc, ret) => { + if (v == null || v.$__ == null) { + return ret; + } + + Object.keys(v.$__.activePaths.getStatePaths('default')).forEach(path => { + mpath.unset(path, ret); + + _minimizePath(ret, path); + }); + + return ret; + }, + virtuals: false + }); + })); + } else { + selector = pullOp['_id'] || (pullOp['_id'] = { $in: [] }); + selector['$in'] = selector['$in'].concat(val); + } + } else if (op === '$push') { + atomics.$push = atomics.$push || { $each: [] }; + if (val != null && utils.hasUserDefinedProperty(val, '$each')) { + atomics.$push = val; + } else { + if (val.length === 1) { + atomics.$push.$each.push(val[0]); + } else if (val.length < 10000) { + atomics.$push.$each.push(...val); + } else { + for (const v of val) { + atomics.$push.$each.push(v); + } + } + } + } else { + atomics[op] = val; + } + + return this; + }, + + /** + * Adds values to the array if not already present. + * + * #### Example: + * + * console.log(doc.array) // [2,3,4] + * const added = doc.array.addToSet(4,5); + * console.log(doc.array) // [2,3,4,5] + * console.log(added) // [5] + * + * @param {...any} [args] + * @return {Array} the values that were added + * @memberOf MongooseArray + * @api public + * @method addToSet + */ + + addToSet() { + _checkManualPopulation(this, arguments); + _depopulateIfNecessary(this, arguments); + + const values = [].map.call(arguments, this._mapCast, this); + const added = []; + let type = ''; + if (values[0] instanceof ArraySubdocument) { + type = 'doc'; + } else if (values[0] instanceof Date) { + type = 'date'; + } else if (isBsonType(values[0], 'ObjectId')) { + type = 'ObjectId'; + } + + const rawValues = utils.isMongooseArray(values) ? values.__array : values; + const rawArray = utils.isMongooseArray(this) ? this.__array : this; + + rawValues.forEach(function(v) { + let found; + const val = +v; + switch (type) { + case 'doc': + found = this.some(function(doc) { + return doc.equals(v); + }); + break; + case 'date': + found = this.some(function(d) { + return +d === val; + }); + break; + case 'ObjectId': + found = this.find(o => o.toString() === v.toString()); + break; + default: + found = ~this.indexOf(v); + break; + } + + if (!found) { + this._markModified(); + rawArray.push(v); + this._registerAtomic('$addToSet', v); + [].push.call(added, v); + } + }, this); + + return added; + }, + + /** + * Returns the number of pending atomic operations to send to the db for this array. + * + * @api private + * @return {number} + * @method hasAtomics + * @memberOf MongooseArray + */ + + hasAtomics() { + if (!utils.isPOJO(this[arrayAtomicsSymbol])) { + return 0; + } + + return Object.keys(this[arrayAtomicsSymbol]).length; + }, + + /** + * Return whether or not the `obj` is included in the array. + * + * @param {object} obj the item to check + * @param {number} fromIndex + * @return {boolean} + * @api public + * @method includes + * @memberOf MongooseArray + */ + + includes(obj, fromIndex) { + const ret = this.indexOf(obj, fromIndex); + return ret !== -1; + }, + + /** + * Return the index of `obj` or `-1` if not found. + * + * @param {object} obj the item to look for + * @param {number} fromIndex + * @return {number} + * @api public + * @method indexOf + * @memberOf MongooseArray + */ + + indexOf(obj, fromIndex) { + if (isBsonType(obj, 'ObjectId')) { + obj = obj.toString(); + } + + fromIndex = fromIndex == null ? 0 : fromIndex; + const len = this.length; + for (let i = fromIndex; i < len; ++i) { + if (obj == this[i]) { + return i; + } + } + return -1; + }, + + /** + * Helper for console.log + * + * @api public + * @method inspect + * @memberOf MongooseArray + */ + + inspect() { + return JSON.stringify(this); + }, + + /** + * Pushes items to the array non-atomically. + * + * #### Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @param {...any} [args] + * @api public + * @method nonAtomicPush + * @memberOf MongooseArray + */ + + nonAtomicPush() { + const values = [].map.call(arguments, this._mapCast, this); + this._markModified(); + const ret = [].push.apply(this, values); + this._registerAtomic('$set', this); + return ret; + }, + + /** + * Wraps [`Array#pop`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/pop) with proper change tracking. + * + * #### Note: + * + * _marks the entire array as modified which will pass the entire thing to $set potentially overwriting any changes that happen between when you retrieved the object and when you save it._ + * + * @see MongooseArray#$pop https://mongoosejs.com/docs/api/array.html#MongooseArray.prototype.$pop() + * @api public + * @method pop + * @memberOf MongooseArray + */ + + pop() { + this._markModified(); + const ret = [].pop.call(this); + this._registerAtomic('$set', this); + return ret; + }, + + /** + * Pulls items from the array atomically. Equality is determined by casting + * the provided value to an embedded document and comparing using + * [the `Document.equals()` function.](https://mongoosejs.com/docs/api/document.html#Document.prototype.equals()) + * + * #### Example: + * + * doc.array.pull(ObjectId) + * doc.array.pull({ _id: 'someId' }) + * doc.array.pull(36) + * doc.array.pull('tag 1', 'tag 2') + * + * To remove a document from a subdocument array we may pass an object with a matching `_id`. + * + * doc.subdocs.push({ _id: 4815162342 }) + * doc.subdocs.pull({ _id: 4815162342 }) // removed + * + * Or we may passing the _id directly and let mongoose take care of it. + * + * doc.subdocs.push({ _id: 4815162342 }) + * doc.subdocs.pull(4815162342); // works + * + * The first pull call will result in a atomic operation on the database, if pull is called repeatedly without saving the document, a $set operation is used on the complete array instead, overwriting possible changes that happened on the database in the meantime. + * + * @param {...any} [args] + * @see mongodb https://www.mongodb.com/docs/manual/reference/operator/update/pull/ + * @api public + * @method pull + * @memberOf MongooseArray + */ + + pull() { + const values = [].map.call(arguments, (v, i) => this._cast(v, i, { defaults: false }), this); + let cur = this; + if (utils.isMongooseArray(cur)) { + cur = cur.__array; + } + let i = cur.length; + let mem; + this._markModified(); + + while (i--) { + mem = cur[i]; + if (mem instanceof Document) { + const some = values.some(function(v) { + return mem.equals(v); + }); + if (some) { + cur.splice(i, 1); + } + } else if (~this.indexOf.call(values, mem)) { + cur.splice(i, 1); + } + } + + if (values[0] instanceof ArraySubdocument) { + this._registerAtomic('$pullDocs', values.map(function(v) { + const _id = v.$__getValue('_id'); + if (_id === undefined || v.$isDefault('_id')) { + return v; + } + return _id; + })); + } else { + this._registerAtomic('$pullAll', values); + } + + + // Might have modified child paths and then pulled, like + // `doc.children[1].name = 'test';` followed by + // `doc.children.remove(doc.children[0]);`. In this case we fall back + // to a `$set` on the whole array. See #3511 + if (cleanModifiedSubpaths(this[arrayParentSymbol], this[arrayPathSymbol]) > 0) { + this._registerAtomic('$set', this); + } + + return this; + }, + + /** + * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking. + * + * #### Example: + * + * const schema = Schema({ nums: [Number] }); + * const Model = mongoose.model('Test', schema); + * + * const doc = await Model.create({ nums: [3, 4] }); + * doc.nums.push(5); // Add 5 to the end of the array + * await doc.save(); + * + * // You can also pass an object with `$each` as the + * // first parameter to use MongoDB's `$position` + * doc.nums.push({ + * $each: [1, 2], + * $position: 0 + * }); + * doc.nums; // [1, 2, 3, 4, 5] + * + * @param {...object} [args] + * @api public + * @method push + * @memberOf MongooseArray + */ + + push() { + let values = arguments; + let atomic = values; + const isOverwrite = values[0] != null && + utils.hasUserDefinedProperty(values[0], '$each'); + const arr = utils.isMongooseArray(this) ? this.__array : this; + if (isOverwrite) { + atomic = values[0]; + values = values[0].$each; + } + + if (this[arraySchemaSymbol] == null) { + return _basePush.apply(this, values); + } + + _checkManualPopulation(this, values); + _depopulateIfNecessary(this, values); + + values = [].map.call(values, this._mapCast, this); + let ret; + const atomics = this[arrayAtomicsSymbol]; + this._markModified(); + if (isOverwrite) { + atomic.$each = values; + + if ((atomics.$push && atomics.$push.$each && atomics.$push.$each.length || 0) !== 0 && + atomics.$push.$position != atomic.$position) { + if (atomic.$position != null) { + [].splice.apply(arr, [atomic.$position, 0].concat(values)); + ret = arr.length; + } else { + ret = [].push.apply(arr, values); + } + + this._registerAtomic('$set', this); + } else if (atomic.$position != null) { + [].splice.apply(arr, [atomic.$position, 0].concat(values)); + ret = this.length; + } else { + ret = [].push.apply(arr, values); + } + } else { + atomic = values; + ret = _basePush.apply(arr, values); + } + + this._registerAtomic('$push', atomic); + + return ret; + }, + + /** + * Alias of [pull](https://mongoosejs.com/docs/api/array.html#MongooseArray.prototype.pull()) + * + * @see MongooseArray#pull https://mongoosejs.com/docs/api/array.html#MongooseArray.prototype.pull() + * @see mongodb https://www.mongodb.com/docs/manual/reference/operator/update/pull/ + * @api public + * @memberOf MongooseArray + * @instance + * @method remove + */ + + remove() { + return this.pull.apply(this, arguments); + }, + + /** + * Sets the casted `val` at index `i` and marks the array modified. + * + * #### Example: + * + * // given documents based on the following + * const Doc = mongoose.model('Doc', new Schema({ array: [Number] })); + * + * const doc = new Doc({ array: [2,3,4] }) + * + * console.log(doc.array) // [2,3,4] + * + * doc.array.set(1,"5"); + * console.log(doc.array); // [2,5,4] // properly cast to number + * doc.save() // the change is saved + * + * // VS not using array#set + * doc.array[1] = "5"; + * console.log(doc.array); // [2,"5",4] // no casting + * doc.save() // change is not saved + * + * @return {Array} this + * @api public + * @method set + * @memberOf MongooseArray + */ + + set(i, val, skipModified) { + const arr = this.__array; + if (skipModified) { + arr[i] = val; + return this; + } + const value = methods._cast.call(this, val, i); + methods._markModified.call(this, i); + arr[i] = value; + return this; + }, + + /** + * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. + * + * #### Example: + * + * doc.array = [2,3]; + * const res = doc.array.shift(); + * console.log(res) // 2 + * console.log(doc.array) // [3] + * + * #### Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + * @method shift + * @memberOf MongooseArray + */ + + shift() { + const arr = utils.isMongooseArray(this) ? this.__array : this; + this._markModified(); + const ret = [].shift.call(arr); + this._registerAtomic('$set', this); + return ret; + }, + + /** + * Wraps [`Array#sort`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/sort) with proper change tracking. + * + * #### Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + * @method sort + * @memberOf MongooseArray + * @see MasteringJS: Array sort https://masteringjs.io/tutorials/fundamentals/array-sort + */ + + sort() { + const arr = utils.isMongooseArray(this) ? this.__array : this; + const ret = [].sort.apply(arr, arguments); + this._registerAtomic('$set', this); + return ret; + }, + + /** + * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting. + * + * #### Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwritting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + * @method splice + * @memberOf MongooseArray + * @see MasteringJS: Array splice https://masteringjs.io/tutorials/fundamentals/array-splice + */ + + splice() { + let ret; + const arr = utils.isMongooseArray(this) ? this.__array : this; + + this._markModified(); + _checkManualPopulation(this, Array.prototype.slice.call(arguments, 2)); + + if (arguments.length) { + let vals; + if (this[arraySchemaSymbol] == null) { + vals = arguments; + } else { + vals = []; + for (let i = 0; i < arguments.length; ++i) { + vals[i] = i < 2 ? + arguments[i] : + this._cast(arguments[i], arguments[0] + (i - 2)); + } + } + + ret = [].splice.apply(arr, vals); + this._registerAtomic('$set', this); + } + + return ret; + }, + + /*! + * ignore + */ + + toBSON() { + return this.toObject(internalToObjectOptions); + }, + + /** + * Returns a native js Array. + * + * @param {object} options + * @return {Array} + * @api public + * @method toObject + * @memberOf MongooseArray + */ + + toObject(options) { + const arr = utils.isMongooseArray(this) ? this.__array : this; + if (options?.depopulate) { + options = clone(options); + options._isNested = true; + // Ensure return value is a vanilla array, because in Node.js 6+ `map()` + // is smart enough to use the inherited array's constructor. + return [].concat(arr).map(function(doc) { + return doc instanceof Document + ? doc.toObject(options) + : doc; + }); + } + + return [].concat(arr); + }, + + $toObject() { + return this.constructor.prototype.toObject.apply(this, arguments); + }, + /** + * Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. + * + * #### Note: + * + * _marks the entire array as modified, which if saved, will store it as a `$set` operation, potentially overwriting any changes that happen between when you retrieved the object and when you save it._ + * + * @api public + * @method unshift + * @memberOf MongooseArray + */ + + unshift() { + _checkManualPopulation(this, arguments); + + let values; + if (this[arraySchemaSymbol] == null) { + values = arguments; + } else { + values = [].map.call(arguments, this._cast, this); + } + + const arr = utils.isMongooseArray(this) ? this.__array : this; + this._markModified(); + [].unshift.apply(arr, values); + this._registerAtomic('$set', this); + return this.length; + } +}; + +/*! + * ignore + */ + +function _isAllSubdocs(docs, ref) { + if (!ref) { + return false; + } + + for (const arg of docs) { + if (arg == null) { + return false; + } + const model = arg.constructor; + if (!(arg instanceof Document) || + (model.modelName !== ref && model.baseModelName !== ref)) { + return false; + } + } + + return true; +} + +/*! + * Minimize _just_ empty objects along the path chain specified + * by `parts`, ignoring all other paths. Useful in cases where + * you want to minimize after unsetting a path. + * + * #### Example: + * + * const obj = { foo: { bar: { baz: {} } }, a: {} }; + * _minimizePath(obj, 'foo.bar.baz'); + * obj; // { a: {} } + */ + +function _minimizePath(obj, parts, i) { + if (typeof parts === 'string') { + if (parts.indexOf('.') === -1) { + return; + } + + parts = mpath.stringToParts(parts); + } + i = i || 0; + if (i >= parts.length) { + return; + } + if (obj == null || typeof obj !== 'object') { + return; + } + + _minimizePath(obj[parts[0]], parts, i + 1); + if (obj[parts[0]] != null && typeof obj[parts[0]] === 'object' && utils.hasOwnKeys(obj[parts[0]]) === false) { + delete obj[parts[0]]; + } +} + +/*! + * ignore + */ + +function _checkManualPopulation(arr, docs) { + const ref = arr == null ? + null : + arr[arraySchemaSymbol]?.embeddedSchemaType?.options?.ref || null; + if (arr.length === 0 && + docs.length !== 0) { + if (_isAllSubdocs(docs, ref)) { + arr[arrayParentSymbol].$populated(arr[arrayPathSymbol], [], { + [populateModelSymbol]: docs[0].constructor + }); + } + } +} + +/*! + * If `docs` isn't all instances of the right model, depopulate `arr` + */ + +function _depopulateIfNecessary(arr, docs) { + const ref = arr == null ? + null : + arr[arraySchemaSymbol]?.embeddedSchemaType?.options?.ref || null; + const parentDoc = arr[arrayParentSymbol]; + const path = arr[arrayPathSymbol]; + if (!ref || !parentDoc.populated(path)) { + return; + } + for (const doc of docs) { + if (doc == null) { + continue; + } + if (typeof doc !== 'object' || doc instanceof String || doc instanceof Number || doc instanceof Buffer || utils.isMongooseType(doc)) { + parentDoc.depopulate(path); + break; + } + } +} + +const returnVanillaArrayMethods = [ + 'filter', + 'flat', + 'flatMap', + 'map', + 'slice' +]; +for (const method of returnVanillaArrayMethods) { + if (Array.prototype[method] == null) { + continue; + } + + methods[method] = function() { + const _arr = utils.isMongooseArray(this) ? this.__array : this; + const arr = [].concat(_arr); + + return arr[method].apply(arr, arguments); + }; +} + +module.exports = methods; diff --git a/backend/node_modules/mongoose/lib/types/arraySubdocument.js b/backend/node_modules/mongoose/lib/types/arraySubdocument.js new file mode 100644 index 0000000..6dc69ac --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/arraySubdocument.js @@ -0,0 +1,208 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const EventEmitter = require('events').EventEmitter; +const Subdocument = require('./subdocument'); +const utils = require('../utils'); + +const documentArrayParent = require('../helpers/symbols').documentArrayParent; + +/** + * A constructor. + * + * @param {object} obj js object returned from the db + * @param {MongooseDocumentArray} parentArr the parent array of this document + * @param {boolean} skipId + * @param {object} fields + * @param {number} index + * @inherits Document + * @api private + */ + +function ArraySubdocument(obj, parentArr, skipId, fields, index) { + if (utils.isMongooseDocumentArray(parentArr)) { + this.__parentArray = parentArr; + this[documentArrayParent] = parentArr.$parent(); + } else { + this.__parentArray = undefined; + this[documentArrayParent] = undefined; + } + this.$setIndex(index); + this.$__parent = this[documentArrayParent]; + + let options; + if (typeof skipId === 'object' && skipId != null) { + options = { isNew: true, ...skipId }; + skipId = undefined; + } else { + options = { isNew: true }; + } + + Subdocument.call(this, obj, fields, this[documentArrayParent], options); +} + +/*! + * Inherit from Subdocument + */ +ArraySubdocument.prototype = Object.create(Subdocument.prototype); +ArraySubdocument.prototype.constructor = ArraySubdocument; + +Object.defineProperty(ArraySubdocument.prototype, '$isSingleNested', { + configurable: false, + writable: false, + value: false +}); + +Object.defineProperty(ArraySubdocument.prototype, '$isDocumentArrayElement', { + configurable: false, + writable: false, + value: true +}); + +for (const i in EventEmitter.prototype) { + ArraySubdocument[i] = EventEmitter.prototype[i]; +} + +/*! + * ignore + */ + +ArraySubdocument.prototype.$setIndex = function(index) { + this.__index = index; + + if (this.$__?.validationError != null) { + const keys = Object.keys(this.$__.validationError.errors); + for (const key of keys) { + this.invalidate(key, this.$__.validationError.errors[key]); + } + } +}; + +/*! + * ignore + */ + +ArraySubdocument.prototype.populate = function() { + throw new Error('Mongoose does not support calling populate() on nested ' + + 'docs. Instead of `doc.arr[0].populate("path")`, use ' + + '`doc.populate("arr.0.path")`'); +}; + +/*! + * ignore + */ + +ArraySubdocument.prototype.$__removeFromParent = function() { + const _id = this._doc._id; + if (!_id) { + throw new Error('For your own good, Mongoose does not know ' + + 'how to remove an ArraySubdocument that has no _id'); + } + this.__parentArray.pull({ _id: _id }); +}; + +/** + * Returns the full path to this document. If optional `path` is passed, it is appended to the full path. + * + * @param {string} [path] + * @param {boolean} [skipIndex] Skip adding the array index. For example `arr.foo` instead of `arr.0.foo`. + * @return {string} + * @api private + * @method $__fullPath + * @memberOf ArraySubdocument + * @instance + */ + +ArraySubdocument.prototype.$__fullPath = function(path, skipIndex) { + if (this.__index == null) { + return null; + } + if (!this.$__.fullPath) { + this.ownerDocument(); + } + + if (skipIndex) { + return path ? + this.$__.fullPath + '.' + path : + this.$__.fullPath; + } + + return path ? + this.$__.fullPath + '.' + this.__index + '.' + path : + this.$__.fullPath + '.' + this.__index; +}; + +/** + * Given a path relative to this document, return the path relative + * to the top-level document. + * @method $__pathRelativeToParent + * @memberOf ArraySubdocument + * @instance + * @api private + */ + +ArraySubdocument.prototype.$__pathRelativeToParent = function(path, skipIndex) { + if (this.__index == null || !this.__parentArray?.$path) { + return null; + } + if (skipIndex) { + return path == null ? this.__parentArray.$path() : this.__parentArray.$path() + '.' + path; + } + if (path == null) { + return this.__parentArray.$path() + '.' + this.__index; + } + return this.__parentArray.$path() + '.' + this.__index + '.' + path; +}; + +/** + * Returns this sub-documents parent document. + * @method $parent + * @memberOf ArraySubdocument + * @instance + * @api public + */ + +ArraySubdocument.prototype.$parent = function() { + return this[documentArrayParent]; +}; + +/*! + * Sets this sub-documents parent document. + * + * @api private + */ + +ArraySubdocument.prototype.$__setParent = function $__setParent(parent) { + this[documentArrayParent] = parent; + this.$__parent = parent; +}; + +/** + * Returns this subdocument's parent array. + * + * #### Example: + * + * const Test = mongoose.model('Test', new Schema({ + * docArr: [{ name: String }] + * })); + * const doc = new Test({ docArr: [{ name: 'test subdoc' }] }); + * + * doc.docArr[0].parentArray() === doc.docArr; // true + * + * @api public + * @method parentArray + * @returns {DocumentArray} + */ + +ArraySubdocument.prototype.parentArray = function() { + return this.__parentArray; +}; + +/*! + * Module exports. + */ + +module.exports = ArraySubdocument; diff --git a/backend/node_modules/mongoose/lib/types/buffer.js b/backend/node_modules/mongoose/lib/types/buffer.js new file mode 100644 index 0000000..e9692c5 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/buffer.js @@ -0,0 +1,294 @@ +/*! + * Module dependencies. + */ + +'use strict'; + +const Binary = require('mongodb/lib/bson').Binary; +const UUID = require('mongodb/lib/bson').UUID; +const utils = require('../utils'); + +/** + * Mongoose Buffer constructor. + * + * Values always have to be passed to the constructor to initialize. + * + * @param {Buffer} value + * @param {string} encode + * @param {number} offset + * @api private + * @inherits Buffer https://nodejs.org/api/buffer.html + * @see https://bit.ly/f6CnZU + */ + +function MongooseBuffer(value, encode, offset) { + let val = value; + if (value == null) { + val = 0; + } + + let encoding; + let path; + let doc; + + if (Array.isArray(encode)) { + // internal casting + path = encode[0]; + doc = encode[1]; + } else { + encoding = encode; + } + + let buf; + if (typeof val === 'number' || val instanceof Number) { + buf = Buffer.alloc(val); + } else { // string, array or object { type: 'Buffer', data: [...] } + buf = Buffer.from(val, encoding, offset); + } + utils.decorate(buf, MongooseBuffer.mixin); + buf.isMongooseBuffer = true; + + // make sure these internal props don't show up in Object.keys() + buf[MongooseBuffer.pathSymbol] = path; + buf[parentSymbol] = doc; + + buf._subtype = 0; + return buf; +} + +const pathSymbol = Symbol.for('mongoose#Buffer#_path'); +const parentSymbol = Symbol.for('mongoose#Buffer#_parent'); +MongooseBuffer.pathSymbol = pathSymbol; + +/*! + * Inherit from Buffer. + */ + +MongooseBuffer.mixin = { + + /** + * Default subtype for the Binary representing this Buffer + * + * @api private + * @property _subtype + * @memberOf MongooseBuffer.mixin + * @static + */ + + _subtype: undefined, + + /** + * Marks this buffer as modified. + * + * @api private + * @method _markModified + * @memberOf MongooseBuffer.mixin + * @static + */ + + _markModified: function() { + const parent = this[parentSymbol]; + + if (parent) { + parent.markModified(this[MongooseBuffer.pathSymbol]); + } + return this; + }, + + /** + * Writes the buffer. + * + * @api public + * @method write + * @memberOf MongooseBuffer.mixin + * @static + */ + + write: function() { + const written = Buffer.prototype.write.apply(this, arguments); + + if (written > 0) { + this._markModified(); + } + + return written; + }, + + /** + * Copies the buffer. + * + * #### Note: + * + * `Buffer#copy` does not mark `target` as modified so you must copy from a `MongooseBuffer` for it to work as expected. This is a work around since `copy` modifies the target, not this. + * + * @return {number} The number of bytes copied. + * @param {Buffer} target + * @method copy + * @memberOf MongooseBuffer.mixin + * @static + */ + + copy: function(target) { + const ret = Buffer.prototype.copy.apply(this, arguments); + + if (target?.isMongooseBuffer) { + target._markModified(); + } + + return ret; + } +}; + +/*! + * Compile other Buffer methods marking this buffer as modified. + */ + +utils.each( + [ + // node < 0.5 + 'writeUInt8', 'writeUInt16', 'writeUInt32', 'writeInt8', 'writeInt16', 'writeInt32', + 'writeFloat', 'writeDouble', 'fill', + 'utf8Write', 'binaryWrite', 'asciiWrite', 'set', + + // node >= 0.5 + 'writeUInt16LE', 'writeUInt16BE', 'writeUInt32LE', 'writeUInt32BE', + 'writeInt16LE', 'writeInt16BE', 'writeInt32LE', 'writeInt32BE', 'writeFloatLE', 'writeFloatBE', 'writeDoubleLE', 'writeDoubleBE'] + , function(method) { + if (!Buffer.prototype[method]) { + return; + } + MongooseBuffer.mixin[method] = function() { + const ret = Buffer.prototype[method].apply(this, arguments); + this._markModified(); + return ret; + }; + }); + +/** + * Converts this buffer to its Binary type representation. + * + * #### SubTypes: + * + * const mongodb = require('mongodb') + * mongodb.BSON.BSON_BINARY_SUBTYPE_DEFAULT + * mongodb.BSON.BSON_BINARY_SUBTYPE_FUNCTION + * mongodb.BSON.BSON_BINARY_SUBTYPE_BYTE_ARRAY + * mongodb.BSON.BSON_BINARY_SUBTYPE_UUID + * mongodb.BSON.BSON_BINARY_SUBTYPE_MD5 + * mongodb.BSON.BSON_BINARY_SUBTYPE_USER_DEFINED + * doc.buffer.toObject(mongodb.BSON.BSON_BINARY_SUBTYPE_USER_DEFINED); + * + * @see bsonspec https://bsonspec.org/#/specification + * @param {Hex} [subtype] + * @return {Binary} + * @api public + * @method toObject + * @memberOf MongooseBuffer + */ + +MongooseBuffer.mixin.toObject = function(options) { + const subtype = typeof options === 'number' + ? options + : (this._subtype || 0); + return new Binary(Buffer.from(this), subtype); +}; + +MongooseBuffer.mixin.$toObject = MongooseBuffer.mixin.toObject; + +/** + * Converts this buffer for storage in MongoDB, including subtype + * + * @return {Binary} + * @api public + * @method toBSON + * @memberOf MongooseBuffer + */ + +MongooseBuffer.mixin.toBSON = function() { + return new Binary(this, this._subtype || 0); +}; + +/** + * Converts this buffer to a UUID. Throws an error if subtype is not 4. + * + * @return {UUID} + * @api public + * @method toUUID + * @memberOf MongooseBuffer + */ + +MongooseBuffer.mixin.toUUID = function() { + if (this._subtype !== 4) { + throw new Error('Cannot convert a Buffer with subtype ' + this._subtype + ' to a UUID'); + } + return new UUID(this); +}; + +/** + * Determines if this buffer is equals to `other` buffer + * + * @param {Buffer} other + * @return {boolean} + * @method equals + * @memberOf MongooseBuffer + */ + +MongooseBuffer.mixin.equals = function(other) { + if (!Buffer.isBuffer(other)) { + return false; + } + + if (this.length !== other.length) { + return false; + } + + for (let i = 0; i < this.length; ++i) { + if (this[i] !== other[i]) { + return false; + } + } + + return true; +}; + +/** + * Sets the subtype option and marks the buffer modified. + * + * #### SubTypes: + * + * const bson = require('bson') + * bson.BSON_BINARY_SUBTYPE_DEFAULT + * bson.BSON_BINARY_SUBTYPE_FUNCTION + * bson.BSON_BINARY_SUBTYPE_BYTE_ARRAY + * bson.BSON_BINARY_SUBTYPE_UUID + * bson.BSON_BINARY_SUBTYPE_MD5 + * bson.BSON_BINARY_SUBTYPE_USER_DEFINED + * + * doc.buffer.subtype(bson.BSON_BINARY_SUBTYPE_UUID); + * + * @see bsonspec https://bsonspec.org/#/specification + * @param {Hex} subtype + * @api public + * @method subtype + * @memberOf MongooseBuffer + */ + +MongooseBuffer.mixin.subtype = function(subtype) { + if (typeof subtype !== 'number') { + throw new TypeError('Invalid subtype. Expected a number'); + } + + if (this._subtype !== subtype) { + this._markModified(); + } + + this._subtype = subtype; +}; + +/*! + * Module exports. + */ + +MongooseBuffer.Binary = Binary; + +module.exports = MongooseBuffer; diff --git a/backend/node_modules/mongoose/lib/types/decimal128.js b/backend/node_modules/mongoose/lib/types/decimal128.js new file mode 100644 index 0000000..ab7b27b --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/decimal128.js @@ -0,0 +1,13 @@ +/** + * Decimal128 type constructor + * + * #### Example: + * + * const id = new mongoose.Types.Decimal128('3.1415'); + * + * @constructor Decimal128 + */ + +'use strict'; + +module.exports = require('mongodb/lib/bson').Decimal128; diff --git a/backend/node_modules/mongoose/lib/types/documentArray/index.js b/backend/node_modules/mongoose/lib/types/documentArray/index.js new file mode 100644 index 0000000..93e2a58 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/documentArray/index.js @@ -0,0 +1,113 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const ArrayMethods = require('../array/methods'); +const DocumentArrayMethods = require('./methods'); + +const arrayAtomicsSymbol = require('../../helpers/symbols').arrayAtomicsSymbol; +const arrayAtomicsBackupSymbol = require('../../helpers/symbols').arrayAtomicsBackupSymbol; +const arrayParentSymbol = require('../../helpers/symbols').arrayParentSymbol; +const arrayPathSymbol = require('../../helpers/symbols').arrayPathSymbol; +const arraySchemaSymbol = require('../../helpers/symbols').arraySchemaSymbol; + +const _basePush = Array.prototype.push; +const numberRE = /^\d+$/; +/** + * DocumentArray constructor + * + * @param {Array} values + * @param {string} path the path to this array + * @param {Document} doc parent document + * @api private + * @return {MongooseDocumentArray} + * @inherits MongooseArray + * @see https://bit.ly/f6CnZU + */ + +function MongooseDocumentArray(values, path, doc, schematype) { + const __array = []; + + const internals = { + [arrayAtomicsSymbol]: {}, + [arrayAtomicsBackupSymbol]: void 0, + [arrayPathSymbol]: path, + [arraySchemaSymbol]: void 0, + [arrayParentSymbol]: void 0 + }; + + if (Array.isArray(values)) { + if (values[arrayPathSymbol] === path && + values[arrayParentSymbol] === doc) { + internals[arrayAtomicsSymbol] = Object.assign({}, values[arrayAtomicsSymbol]); + } + values.forEach(v => { + _basePush.call(__array, v); + }); + } + internals[arrayPathSymbol] = path; + internals.__array = __array; + + if (doc?.$__) { + internals[arrayParentSymbol] = doc; + internals[arraySchemaSymbol] = doc.$__schema.path(path); + + // `schema.path()` doesn't drill into nested arrays properly yet, see + // gh-6398, gh-6602. This is a workaround because nested arrays are + // always plain non-document arrays, so once you get to a document array + // nesting is done. Matryoshka code. + while (internals[arraySchemaSymbol] != null && + internals[arraySchemaSymbol].$isMongooseArray && + !internals[arraySchemaSymbol].$isMongooseDocumentArray) { + internals[arraySchemaSymbol] = internals[arraySchemaSymbol].embeddedSchemaType; + } + } + + const proxy = new Proxy(__array, { + get: function(target, prop) { + if (prop === 'isMongooseArray' || + prop === 'isMongooseArrayProxy' || + prop === 'isMongooseDocumentArray' || + prop === 'isMongooseDocumentArrayProxy') { + return true; + } + if (Object.hasOwn(internals, prop)) { + return internals[prop]; + } + if (Object.hasOwn(DocumentArrayMethods, prop)) { + return DocumentArrayMethods[prop]; + } + if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { + return schematype.virtuals[prop].applyGetters(undefined, target); + } + if (Object.hasOwn(ArrayMethods, prop)) { + return ArrayMethods[prop]; + } + + return __array[prop]; + }, + set: function(target, prop, value) { + if (typeof prop === 'string' && numberRE.test(prop)) { + DocumentArrayMethods.set.call(proxy, prop, value, false); + } else if (Object.hasOwn(internals, prop)) { + internals[prop] = value; + } else if (schematype?.virtuals && Object.hasOwn(schematype.virtuals, prop)) { + schematype.virtuals[prop].applySetters(value, target); + } else { + __array[prop] = value; + } + + return true; + } + }); + + return proxy; +} + +/*! + * Module exports. + */ + +module.exports = MongooseDocumentArray; diff --git a/backend/node_modules/mongoose/lib/types/documentArray/isMongooseDocumentArray.js b/backend/node_modules/mongoose/lib/types/documentArray/isMongooseDocumentArray.js new file mode 100644 index 0000000..6e6a169 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/documentArray/isMongooseDocumentArray.js @@ -0,0 +1,5 @@ +'use strict'; + +exports.isMongooseDocumentArray = function(mongooseDocumentArray) { + return Array.isArray(mongooseDocumentArray) && mongooseDocumentArray.isMongooseDocumentArray; +}; diff --git a/backend/node_modules/mongoose/lib/types/documentArray/methods/index.js b/backend/node_modules/mongoose/lib/types/documentArray/methods/index.js new file mode 100644 index 0000000..d5cca29 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/documentArray/methods/index.js @@ -0,0 +1,415 @@ +'use strict'; + +const ArrayMethods = require('../../array/methods'); +const Document = require('../../../document'); +const getDiscriminatorByValue = require('../../../helpers/discriminator/getDiscriminatorByValue'); +const internalToObjectOptions = require('../../../options').internalToObjectOptions; +const utils = require('../../../utils'); +const isBsonType = require('../../../helpers/isBsonType'); + +const arrayParentSymbol = require('../../../helpers/symbols').arrayParentSymbol; +const arrayPathSymbol = require('../../../helpers/symbols').arrayPathSymbol; +const arraySchemaSymbol = require('../../../helpers/symbols').arraySchemaSymbol; +const documentArrayParent = require('../../../helpers/symbols').documentArrayParent; + +const _baseToString = Array.prototype.toString; + +const methods = { + /*! + * ignore + */ + + toBSON() { + return this.toObject(internalToObjectOptions); + }, + + toString() { + return _baseToString.call(this.__array.map(subdoc => { + if (subdoc?.$__ != null) { + return subdoc.toString(); + } + return subdoc; + })); + }, + + /*! + * ignore + */ + + getArrayParent() { + return this[arrayParentSymbol]; + }, + + /*! + * ignore + */ + $schemaType() { + return this[arraySchemaSymbol]; + }, + + /** + * Overrides MongooseArray#cast + * + * @method _cast + * @api private + * @memberOf MongooseDocumentArray + */ + + _cast(value, index, options) { + if (this[arraySchemaSymbol] == null) { + return value; + } + let Constructor = this[arraySchemaSymbol].Constructor; + const isInstance = Constructor.$isMongooseDocumentArray ? + utils.isMongooseDocumentArray(value) : + value instanceof Constructor; + if (isInstance || + // Hack re: #5001, see #5005 + value?.constructor?.baseCasterConstructor === Constructor) { + if (!(value[documentArrayParent] && value.__parentArray)) { + // value may have been created using array.create() + value[documentArrayParent] = this[arrayParentSymbol]; + value.__parentArray = this; + } + value.$setIndex(index); + return value; + } + + if (value == null) { + return null; + } + + // handle cast('string') or cast(ObjectId) etc. + // only objects are permitted so we can safely assume that + // non-objects are to be interpreted as _id + if (Buffer.isBuffer(value) || + isBsonType(value, 'ObjectId') || !utils.isObject(value)) { + value = { _id: value }; + } + + if (value && + Constructor.discriminators && + Constructor.schema && + Constructor.schema.options && + Constructor.schema.options.discriminatorKey) { + if (typeof value[Constructor.schema.options.discriminatorKey] === 'string' && + Constructor.discriminators[value[Constructor.schema.options.discriminatorKey]]) { + Constructor = Constructor.discriminators[value[Constructor.schema.options.discriminatorKey]]; + } else { + const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, value[Constructor.schema.options.discriminatorKey]); + if (constructorByValue) { + Constructor = constructorByValue; + } + } + } + + if (Constructor.$isMongooseDocumentArray) { + return Constructor.cast(value, this, undefined, undefined, index); + } + const ret = new Constructor(value, this, options, undefined, index); + ret.isNew = true; + return ret; + }, + + /** + * Searches array items for the first document with a matching _id. + * + * #### Example: + * + * const embeddedDoc = m.array.id(some_id); + * + * @return {EmbeddedDocument|null} the subdocument or null if not found. + * @param {ObjectId|string|number|Buffer} id + * @method id + * @api public + * @memberOf MongooseDocumentArray + */ + + id(id) { + if (id == null) { + return null; + } + + const schemaType = this[arraySchemaSymbol]; + let idSchemaType = null; + + if (schemaType?.schema) { + idSchemaType = schemaType.schema.path('_id'); + } else if (schemaType?.casterConstructor?.schema) { + idSchemaType = schemaType.casterConstructor.schema.path('_id'); + } + + let castedId = null; + if (idSchemaType) { + try { + castedId = idSchemaType.cast(id); + } catch { + // ignore error + } + } + + let _id; + + for (const val of this) { + if (!val) { + continue; + } + + _id = val.get('_id'); + + if (_id == null) { + continue; + } else if (_id instanceof Document) { + _id = _id.get('_id'); + if (castedId != null && utils.deepEqual(castedId, _id)) { + return val; + } else if (castedId == null && (id == _id || utils.deepEqual(id, _id))) { + // Backwards compat: compare user-specified param to _id using loose equality + return val; + } + } else if (castedId != null && utils.deepEqual(castedId, _id)) { + return val; + } else if (castedId == null && (_id == id || utils.deepEqual(id, _id))) { + // Backwards compat: compare user-specified param to _id using loose equality + return val; + } + } + + return null; + }, + + /** + * Returns a native js Array of plain js objects + * + * #### Note: + * + * _Each sub-document is converted to a plain object by calling its `#toObject` method._ + * + * @param {object} [options] optional options to pass to each documents `toObject` method call during conversion + * @return {Array} + * @method toObject + * @api public + * @memberOf MongooseDocumentArray + */ + + toObject(options) { + // `[].concat` coerces the return value into a vanilla JS array, rather + // than a Mongoose array. + return [].concat(this.map(function(doc) { + if (doc == null) { + return null; + } + if (typeof doc.toObject !== 'function') { + return doc; + } + return doc.toObject(options); + })); + }, + + $toObject() { + return this.constructor.prototype.toObject.apply(this, arguments); + }, + + /** + * Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking. + * + * @param {...object} [args] + * @api public + * @method push + * @memberOf MongooseDocumentArray + */ + + push() { + const ret = ArrayMethods.push.apply(this, arguments); + + _updateParentPopulated(this); + + return ret; + }, + + /** + * Pulls items from the array atomically. + * + * @param {...object} [args] + * @api public + * @method pull + * @memberOf MongooseDocumentArray + */ + + pull() { + const ret = ArrayMethods.pull.apply(this, arguments); + + _updateParentPopulated(this); + + return ret; + }, + + /** + * Wraps [`Array#shift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. + * @api private + */ + + shift() { + const ret = ArrayMethods.shift.apply(this, arguments); + + _updateParentPopulated(this); + + return ret; + }, + + /** + * Wraps [`Array#splice`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice) with proper change tracking and casting. + * @api private + */ + + splice() { + const ret = ArrayMethods.splice.apply(this, arguments); + + _updateParentPopulated(this); + + return ret; + }, + + /** + * Helper for console.log + * + * @method inspect + * @api public + * @memberOf MongooseDocumentArray + */ + + inspect() { + return this.toObject(); + }, + + /** + * Creates a subdocument casted to this schema. + * + * This is the same subdocument constructor used for casting. + * + * @param {object} obj the value to cast to this arrays SubDocument schema + * @method create + * @api public + * @memberOf MongooseDocumentArray + */ + + create(obj) { + let Constructor = this[arraySchemaSymbol].Constructor; + if (obj && + Constructor.discriminators && + Constructor.schema && + Constructor.schema.options && + Constructor.schema.options.discriminatorKey) { + if (typeof obj[Constructor.schema.options.discriminatorKey] === 'string' && + Constructor.discriminators[obj[Constructor.schema.options.discriminatorKey]]) { + Constructor = Constructor.discriminators[obj[Constructor.schema.options.discriminatorKey]]; + } else { + const constructorByValue = getDiscriminatorByValue(Constructor.discriminators, obj[Constructor.schema.options.discriminatorKey]); + if (constructorByValue) { + Constructor = constructorByValue; + } + } + } + + return new Constructor(obj, this); + }, + + /*! + * ignore + */ + + notify(event) { + const _this = this; + return function notify(val, _arr) { + _arr = _arr || _this; + let i = _arr.length; + while (i--) { + if (_arr[i] == null) { + continue; + } + switch (event) { + // only swap for save event for now, we may change this to all event types later + case 'save': + val = _this[i]; + break; + default: + // NO-OP + break; + } + + if (utils.isMongooseArray(_arr[i])) { + notify(val, _arr[i]); + } else if (_arr[i]) { + _arr[i].emit(event, val); + } + } + }; + }, + + set(i, val, skipModified) { + const arr = this.__array; + if (skipModified) { + arr[i] = val; + return this; + } + const value = methods._cast.call(this, val, i); + methods._markModified.call(this, i); + arr[i] = value; + return this; + }, + + _markModified(elem, embeddedPath) { + const parent = this[arrayParentSymbol]; + let dirtyPath; + + if (parent) { + dirtyPath = this[arrayPathSymbol]; + + if (arguments.length) { + if (embeddedPath != null) { + // an embedded doc bubbled up the change + const index = elem.__index; + dirtyPath = dirtyPath + '.' + index + '.' + embeddedPath; + } else { + // directly set an index + dirtyPath = dirtyPath + '.' + elem; + } + } + + if (dirtyPath != null && dirtyPath.endsWith('.$')) { + return this; + } + + parent.markModified(dirtyPath, arguments.length !== 0 ? elem : parent); + } + + return this; + } +}; + +module.exports = methods; + +/** + * If this is a document array, each element may contain single + * populated paths, so we need to modify the top-level document's + * populated cache. See gh-8247, gh-8265. + * @param {Array} arr + * @api private + */ + +function _updateParentPopulated(arr) { + const parent = arr[arrayParentSymbol]; + if (!parent || parent.$__.populated == null) return; + + const populatedPaths = Object.keys(parent.$__.populated). + filter(p => p.startsWith(arr[arrayPathSymbol] + '.')); + + for (const path of populatedPaths) { + const remnant = path.slice((arr[arrayPathSymbol] + '.').length); + if (!Array.isArray(parent.$__.populated[path].value)) { + continue; + } + + parent.$__.populated[path].value = arr.map(val => val.$populated(remnant)); + } +} diff --git a/backend/node_modules/mongoose/lib/types/double.js b/backend/node_modules/mongoose/lib/types/double.js new file mode 100644 index 0000000..65a3892 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/double.js @@ -0,0 +1,13 @@ +/** + * Double type constructor + * + * #### Example: + * + * const pi = new mongoose.Types.Double(3.1415); + * + * @constructor Double + */ + +'use strict'; + +module.exports = require('mongodb/lib/bson').Double; diff --git a/backend/node_modules/mongoose/lib/types/index.js b/backend/node_modules/mongoose/lib/types/index.js new file mode 100644 index 0000000..8252aab --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/index.js @@ -0,0 +1,23 @@ + +/*! + * Module exports. + */ + +'use strict'; + +exports.Array = require('./array'); +exports.Buffer = require('./buffer'); + +exports.Document = // @deprecate +exports.Embedded = require('./arraySubdocument'); + +exports.DocumentArray = require('./documentArray'); +exports.Double = require('./double'); +exports.Decimal128 = require('./decimal128'); +exports.ObjectId = require('./objectid'); + +exports.Map = require('./map'); + +exports.Subdocument = require('./subdocument'); + +exports.UUID = require('./uuid'); diff --git a/backend/node_modules/mongoose/lib/types/map.js b/backend/node_modules/mongoose/lib/types/map.js new file mode 100644 index 0000000..8912cc2 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/map.js @@ -0,0 +1,368 @@ +'use strict'; + +const Mixed = require('../schema/mixed'); +const MongooseError = require('../error/mongooseError'); +const clone = require('../helpers/clone'); +const deepEqual = require('../utils').deepEqual; +const getConstructorName = require('../helpers/getConstructorName'); +const handleSpreadDoc = require('../helpers/document/handleSpreadDoc'); +const util = require('util'); +const specialProperties = require('../helpers/specialProperties'); +const isBsonType = require('../helpers/isBsonType'); +const cleanModifiedSubpaths = require('../helpers/document/cleanModifiedSubpaths'); + +const populateModelSymbol = require('../helpers/symbols').populateModelSymbol; + +/*! + * ignore + */ + +class MongooseMap extends Map { + constructor(v, path, doc, schemaType) { + if (getConstructorName(v) === 'Object') { + v = Object.keys(v).reduce((arr, key) => arr.concat([[key, v[key]]]), []); + } + super(v); + this.$__parent = doc?.$__ != null ? doc : null; + this.$__path = path; + this.$__schemaType = schemaType == null ? new Mixed(path) : schemaType; + + this.$__runDeferred(); + } + + $init(key, value) { + checkValidKey(key); + + super.set(key, value); + + if (value?.$isSingleNested) { + value.$basePath = this.$__path + '.' + key; + } + } + + $__set(key, value) { + super.set(key, value); + } + + /** + * Overwrites native Map's `get()` function to support Mongoose getters. + * + * @api public + * @method get + * @memberOf Map + */ + + get(key, options) { + if (isBsonType(key, 'ObjectId')) { + key = key.toString(); + } + + options = options || {}; + if (options.getters === false) { + return super.get(key); + } + return this.$__schemaType.applyGetters(super.get(key), this.$__parent); + } + + /** + * Overwrites native Map's `set()` function to support setters, `populate()`, + * and change tracking. Note that Mongoose maps _only_ support strings and + * ObjectIds as keys. + * + * Keys also cannot: + * - be named after special properties `prototype`, `constructor`, and `__proto__` + * - start with a dollar sign (`$`) + * - contain any dots (`.`) + * + * #### Example: + * + * doc.myMap.set('test', 42); // works + * doc.myMap.set({ obj: 42 }, 42); // Throws "Mongoose maps only support string keys" + * doc.myMap.set(10, 42); // Throws "Mongoose maps only support string keys" + * doc.myMap.set("$test", 42); // Throws "Mongoose maps do not support keys that start with "$", got "$test"" + * + * @api public + * @method set + * @memberOf Map + */ + + set(key, value) { + if (isBsonType(key, 'ObjectId')) { + key = key.toString(); + } + + checkValidKey(key); + value = handleSpreadDoc(value); + + // Weird, but because you can't assign to `this` before calling `super()` + // you can't get access to `$__schemaType` to cast in the initial call to + // `set()` from the `super()` constructor. + + if (this.$__schemaType == null) { + this.$__deferred = this.$__deferred || []; + this.$__deferred.push({ key: key, value: value }); + return; + } + + let _fullPath; + const parent = this.$__parent; + const populated = parent?.$__?.populated ? + parent.$populated(fullPath.call(this), true) || parent.$populated(this.$__path, true) : + null; + const priorVal = this.get(key); + + if (populated != null) { + if (this.$__schemaType.$isSingleNested) { + throw new MongooseError( + 'Cannot manually populate single nested subdoc underneath Map ' + + `at path "${this.$__path}". Try using an array instead of a Map.` + ); + } + if (Array.isArray(value) && this.$__schemaType.$isMongooseArray) { + value = value.map(v => { + if (v.$__ == null) { + v = new populated.options[populateModelSymbol](v); + } + // Doesn't support single nested "in-place" populate + v.$__.wasPopulated = { value: v._doc._id }; + return v; + }); + } else if (value != null) { + if (value.$__ == null) { + value = new populated.options[populateModelSymbol](value); + } + // Doesn't support single nested "in-place" populate + value.$__.wasPopulated = { value: value._doc._id }; + } + } else { + try { + const options = this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested || this.$__schemaType.$isMongooseArray || this.$__schemaType.$isSchemaMap ? + { path: fullPath.call(this) } : + null; + value = this.$__schemaType.applySetters( + value, + this.$__parent, + false, + this.get(key), + options + ); + } catch (error) { + if (this.$__parent?.$__ != null) { + this.$__parent.invalidate(fullPath.call(this), error); + return; + } + throw error; + } + } + + super.set(key, value); + + if (parent?.$__ != null && !deepEqual(value, priorVal)) { + const path = fullPath.call(this); + parent.markModified(path); + // If overwriting the full document array or subdoc, make sure to clean up any paths that were modified + // before re: #15108 + if (this.$__schemaType.$isMongooseDocumentArray || this.$__schemaType.$isSingleNested) { + cleanModifiedSubpaths(parent, path); + } + } + + // Delay calculating full path unless absolutely necessary, because string + // concatenation is a bottleneck re: #13171 + function fullPath() { + if (_fullPath) { + return _fullPath; + } + _fullPath = this.$__path + '.' + key; + return _fullPath; + } + } + + /** + * Overwrites native Map's `clear()` function to support change tracking. + * + * @api public + * @method clear + * @memberOf Map + */ + + clear() { + super.clear(); + const parent = this.$__parent; + if (parent != null) { + parent.markModified(this.$__path); + } + } + + /** + * Overwrites native Map's `delete()` function to support change tracking. + * + * @api public + * @method delete + * @memberOf Map + */ + + delete(key) { + if (isBsonType(key, 'ObjectId')) { + key = key.toString(); + } + + this.set(key, undefined); + return super.delete(key); + } + + /** + * Converts this map to a native JavaScript Map so the MongoDB driver can serialize it. + * + * @api public + * @method toBSON + * @memberOf Map + */ + + toBSON() { + return new Map(this); + } + + toObject(options) { + if (options?.flattenMaps) { + const ret = {}; + const keys = this.keys(); + for (const key of keys) { + ret[key] = clone(this.get(key), options); + } + return ret; + } + + return new Map(this); + } + + $toObject() { + return this.constructor.prototype.toObject.apply(this, arguments); + } + + /** + * Converts this map to a native JavaScript Map for `JSON.stringify()`. Set + * the `flattenMaps` option to convert this map to a POJO instead. + * + * #### Example: + * + * doc.myMap.toJSON() instanceof Map; // true + * doc.myMap.toJSON({ flattenMaps: true }) instanceof Map; // false + * + * @api public + * @method toJSON + * @param {object} [options] + * @param {boolean} [options.flattenMaps=false] set to `true` to convert the map to a POJO rather than a native JavaScript map + * @memberOf Map + */ + + toJSON(options) { + if (typeof options?.flattenMaps === 'boolean' ? options.flattenMaps : true) { + const ret = {}; + const keys = this.keys(); + for (const key of keys) { + ret[key] = clone(this.get(key), options); + } + return ret; + } + + return new Map(this); + } + + inspect() { + return new Map(this); + } + + $__runDeferred() { + if (!this.$__deferred) { + return; + } + + for (const keyValueObject of this.$__deferred) { + this.set(keyValueObject.key, keyValueObject.value); + } + + this.$__deferred = null; + } +} + +if (util.inspect.custom) { + Object.defineProperty(MongooseMap.prototype, util.inspect.custom, { + enumerable: false, + writable: false, + configurable: false, + value: MongooseMap.prototype.inspect + }); +} + +Object.defineProperty(MongooseMap.prototype, '$__set', { + enumerable: false, + writable: true, + configurable: false +}); + +Object.defineProperty(MongooseMap.prototype, '$__parent', { + enumerable: false, + writable: true, + configurable: false +}); + +Object.defineProperty(MongooseMap.prototype, '$__path', { + enumerable: false, + writable: true, + configurable: false +}); + +Object.defineProperty(MongooseMap.prototype, '$__schemaType', { + enumerable: false, + writable: true, + configurable: false +}); + +/** + * Set to `true` for all Mongoose map instances + * + * @api public + * @property $isMongooseMap + * @memberOf MongooseMap + * @instance + */ + +Object.defineProperty(MongooseMap.prototype, '$isMongooseMap', { + enumerable: false, + writable: false, + configurable: false, + value: true +}); + +Object.defineProperty(MongooseMap.prototype, '$__deferredCalls', { + enumerable: false, + writable: false, + configurable: false, + value: true +}); + +/** + * Since maps are stored as objects under the hood, keys must be strings + * and can't contain any invalid characters + * @param {string} key + * @api private + */ + +function checkValidKey(key) { + const keyType = typeof key; + if (keyType !== 'string') { + throw new TypeError(`Mongoose maps only support string keys, got ${keyType}`); + } + if (key.startsWith('$')) { + throw new Error(`Mongoose maps do not support keys that start with "$", got "${key}"`); + } + if (key.includes('.')) { + throw new Error(`Mongoose maps do not support keys that contain ".", got "${key}"`); + } + if (specialProperties.has(key)) { + throw new Error(`Mongoose maps do not support reserved key name "${key}"`); + } +} + +module.exports = MongooseMap; diff --git a/backend/node_modules/mongoose/lib/types/objectid.js b/backend/node_modules/mongoose/lib/types/objectid.js new file mode 100644 index 0000000..f0a9b25 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/objectid.js @@ -0,0 +1,41 @@ +/** + * ObjectId type constructor + * + * #### Example: + * + * const id = new mongoose.Types.ObjectId; + * + * @constructor ObjectId + */ + +'use strict'; + +const ObjectId = require('mongodb/lib/bson').ObjectId; +const objectIdSymbol = require('../helpers/symbols').objectIdSymbol; + +/** + * Getter for convenience with populate, see gh-6115 + * @api private + */ + +Object.defineProperty(ObjectId.prototype, '_id', { + enumerable: false, + configurable: true, + get: function() { + return this; + } +}); + +/*! + * Convenience `valueOf()` to allow comparing ObjectIds using double equals re: gh-7299 + */ + +if (!Object.hasOwn(ObjectId.prototype, 'valueOf')) { + ObjectId.prototype.valueOf = function objectIdValueOf() { + return this.toString(); + }; +} + +ObjectId.prototype[objectIdSymbol] = true; + +module.exports = ObjectId; diff --git a/backend/node_modules/mongoose/lib/types/subdocument.js b/backend/node_modules/mongoose/lib/types/subdocument.js new file mode 100644 index 0000000..4600702 --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/subdocument.js @@ -0,0 +1,443 @@ +'use strict'; + +const Document = require('../document'); +const internalToObjectOptions = require('../options').internalToObjectOptions; +const util = require('util'); +const utils = require('../utils'); + +module.exports = Subdocument; + +/** + * Subdocument constructor. + * + * @inherits Document + * @api private + */ + +function Subdocument(value, fields, parent, options) { + if (parent != null) { + // If setting a nested path, should copy isNew from parent re: gh-7048 + const parentOptions = { isNew: parent.isNew }; + if ('defaults' in parent.$__) { + parentOptions.defaults = parent.$__.defaults; + } + options = Object.assign(parentOptions, options); + } + if (options?.path != null) { + this.$basePath = options.path; + } + Document.call(this, value, fields, options); + + delete this.$__.priorDoc; +} + +Subdocument.prototype = Object.create(Document.prototype); + +Object.defineProperty(Subdocument.prototype, '$isSubdocument', { + configurable: false, + writable: false, + value: true +}); + +Object.defineProperty(Subdocument.prototype, '$isSingleNested', { + configurable: false, + writable: false, + value: true +}); + +/*! + * ignore + */ + +Subdocument.prototype.toBSON = function() { + return this.toObject(internalToObjectOptions); +}; + +/** + * Used as a stub for middleware + * + * #### Note: + * + * _This is a no-op. Does not actually save the doc to the db._ + * + * @param {object} [options] + * @param {boolean} [options.suppressWarning=false] If `true`, suppress the warning about calling `save()` on a subdoc. + * @param {boolean|object} [options.middleware=true] set to `false` to skip all user-defined middleware + * @param {boolean} [options.middleware.pre=true] set to `false` to skip only pre hooks + * @param {boolean} [options.middleware.post=true] set to `false` to skip only post hooks + * @return {Promise} resolved Promise + * @api private + */ + +Subdocument.prototype.save = async function save(options) { + options = options || {}; + + if (!options.suppressWarning) { + utils.warn('mongoose: calling `save()` on a subdoc does **not** save ' + + 'the document to MongoDB, it only runs save middleware. ' + + 'Use `subdoc.save({ suppressWarning: true })` to hide this warning ' + + 'if you\'re sure this behavior is right for your app.'); + } + + return await this.$__save(options); +}; + +/** + * Given a path relative to this document, return the path relative + * to the top-level document. + * @param {string} path + * @method $__fullPath + * @memberOf Subdocument + * @instance + * @returns {string} + * @api private + */ + +Subdocument.prototype.$__fullPath = function(path) { + if (!this.$__.fullPath) { + this.ownerDocument(); + } + + return path ? + this.$__.fullPath + '.' + path : + this.$__.fullPath; +}; + +/** + * Given a path relative to this document, return the path relative + * to the parent document. + * @param {string} p + * @returns {string} + * @method $__pathRelativeToParent + * @memberOf Subdocument + * @instance + * @api private + */ + +Subdocument.prototype.$__pathRelativeToParent = function(p) { + if (p == null) { + return this.$basePath; + } + if (!this.$basePath) { + return p; + } + return [this.$basePath, p].join('.'); +}; + +/** + * Used as a stub for middleware + * + * #### Note: + * + * _This is a no-op. Does not actually save the doc to the db._ + * + * @param {Function} [fn] + * @method $__save + * @api private + */ + +Subdocument.prototype.$__save = async function $__save(options) { + try { + await this._execDocumentPreHooks('save', options, [options]); + } catch (error) { + await this._execDocumentPostHooks('save', options, error); + return; + } + + await this._execDocumentPostHooks('save', options); +}; + +/*! + * ignore + */ + +Subdocument.prototype.$isValid = function(path) { + const parent = this.$parent(); + const fullPath = this.$__pathRelativeToParent(path); + if (parent != null && fullPath != null) { + return parent.$isValid(fullPath); + } + return Document.prototype.$isValid.call(this, path); +}; + +/*! + * ignore + */ + +Subdocument.prototype.markModified = function(path) { + Document.prototype.markModified.call(this, path); + const parent = this.$parent(); + const fullPath = this.$__pathRelativeToParent(path); + + if (parent == null || fullPath == null) { + return; + } + + const myPath = this.$__pathRelativeToParent().replace(/\.$/, ''); + if (parent.isDirectModified(myPath) || this.isNew) { + return; + } + this.$__parent.markModified(fullPath, this); +}; + +/*! + * ignore + */ + +Subdocument.prototype.isModified = function(paths, options, modifiedPaths) { + const parent = this.$parent(); + if (parent != null) { + if (Array.isArray(paths) || typeof paths === 'string') { + paths = (Array.isArray(paths) ? paths : paths.split(' ')); + paths = paths.map(p => this.$__pathRelativeToParent(p)).filter(p => p != null); + } else if (!paths) { + paths = this.$__pathRelativeToParent(); + } + + return parent.$isModified(paths, options, modifiedPaths); + } + + return Document.prototype.isModified.call(this, paths, options, modifiedPaths); +}; + +/** + * Marks a path as valid, removing existing validation errors. + * + * @param {string} path the field to mark as valid + * @api private + * @method $markValid + * @memberOf Subdocument + */ + +Subdocument.prototype.$markValid = function(path) { + Document.prototype.$markValid.call(this, path); + const parent = this.$parent(); + const fullPath = this.$__pathRelativeToParent(path); + if (parent != null && fullPath != null) { + parent.$markValid(fullPath); + } +}; + +/*! + * ignore + */ + +Subdocument.prototype.invalidate = function(path, err, val) { + Document.prototype.invalidate.call(this, path, err, val); + + const parent = this.$parent(); + const fullPath = this.$__pathRelativeToParent(path); + if (parent != null && fullPath != null) { + parent.invalidate(fullPath, err, val); + } else if (err.kind === 'cast' || err.name === 'CastError' || fullPath == null) { + throw err; + } + + return this.ownerDocument().$__.validationError; +}; + +/*! + * ignore + */ + +Subdocument.prototype.$ignore = function(path) { + Document.prototype.$ignore.call(this, path); + const parent = this.$parent(); + const fullPath = this.$__pathRelativeToParent(path); + if (parent != null && fullPath != null) { + parent.$ignore(fullPath); + } +}; + +/** + * Returns the top level document of this sub-document. + * + * @return {Document} + */ + +Subdocument.prototype.ownerDocument = function() { + if (this.$__.ownerDocument) { + return this.$__.ownerDocument; + } + + let parent = this; + const paths = []; + const seenDocs = new Set([parent]); + + while (true) { + if (typeof parent.$__pathRelativeToParent !== 'function') { + break; + } + paths.unshift(parent.$__pathRelativeToParent(void 0, true)); + const _parent = parent.$parent(); + if (_parent == null) { + break; + } + parent = _parent; + if (seenDocs.has(parent)) { + throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); + } + + seenDocs.add(parent); + } + + this.$__.fullPath = paths.join('.'); + + this.$__.ownerDocument = parent; + return this.$__.ownerDocument; +}; + +/*! + * ignore + */ + +Subdocument.prototype.$__fullPathWithIndexes = function() { + let parent = this; + const paths = []; + const seenDocs = new Set([parent]); + + while (true) { + if (typeof parent.$__pathRelativeToParent !== 'function') { + break; + } + paths.unshift(parent.$__pathRelativeToParent(void 0, false)); + const _parent = parent.$parent(); + if (_parent == null) { + break; + } + parent = _parent; + if (seenDocs.has(parent)) { + throw new Error('Infinite subdocument loop: subdoc with _id ' + parent._id + ' is a parent of itself'); + } + + seenDocs.add(parent); + } + + return paths.join('.'); +}; + +/** + * Returns this sub-documents parent document. + * + * @api public + */ + +Subdocument.prototype.parent = function() { + return this.$__parent; +}; + +/** + * Returns this sub-documents parent document. + * + * @api public + * @method $parent + */ + +Subdocument.prototype.$parent = Subdocument.prototype.parent; + +/*! + * Sets this sub-documents parent document. + * + * @api private + */ + +Subdocument.prototype.$__setParent = function $__setParent(parent) { + this.$__parent = parent; +}; + +/** + * ignore + * @method $__removeFromParent + * @memberOf Subdocument + * @instance + * @api private + */ + +Subdocument.prototype.$__removeFromParent = function() { + this.$__parent.set(this.$basePath, null); +}; + +/** + * Null-out this subdoc + * + * @param {object} [options] + */ + +Subdocument.prototype.deleteOne = function deleteOne(options) { + registerRemoveListener(this); + + // If removing entire doc, no need to remove subdoc + if (!options?.noop) { + this.$__removeFromParent(); + + const owner = this.ownerDocument(); + owner.$__.removedSubdocs = owner.$__.removedSubdocs || []; + owner.$__.removedSubdocs.push(this); + } +}; + +/*! + * ignore + */ + +Subdocument.prototype.populate = function() { + throw new Error('Mongoose does not support calling populate() on nested ' + + 'docs. Instead of `doc.nested.populate("path")`, use ' + + '`doc.populate("nested.path")`'); +}; + +/** + * Helper for console.log + * + * @api public + */ + +Subdocument.prototype.inspect = function() { + return this.toObject(); +}; + +if (util.inspect.custom) { + // Avoid Node deprecation warning DEP0079 + Subdocument.prototype[util.inspect.custom] = Subdocument.prototype.inspect; +} + +/** + * Override `$toObject()` to handle minimizing the whole path. Should not minimize if schematype-level minimize + * is set to false re: gh-11247, gh-14058, gh-14151 + */ + +Subdocument.prototype.$toObject = function $toObject(options, json) { + const ret = Document.prototype.$toObject.call(this, options, json); + + // If `$toObject()` was called recursively, respect the minimize option, including schematype level minimize. + // If minimize is set, then we can minimize out the whole object. + if (utils.hasOwnKeys(ret) === false && options?._calledWithOptions != null) { + const minimize = options._calledWithOptions?.minimize ?? this?.$__schemaTypeOptions?.minimize ?? options.minimize; + if (minimize && !this.constructor.$__required) { + return undefined; + } + } + return ret; +}; + +/** + * Registers remove event listeners for triggering + * on subdocuments. + * + * @param {Subdocument} sub + * @api private + */ + +function registerRemoveListener(sub) { + const owner = sub.ownerDocument(); + + function emitRemove() { + owner.$removeListener('save', emitRemove); + owner.$removeListener('deleteOne', emitRemove); + sub.emit('deleteOne', sub); + sub.constructor.emit('deleteOne', sub); + } + + owner.$on('save', emitRemove); + owner.$on('deleteOne', emitRemove); +} diff --git a/backend/node_modules/mongoose/lib/types/uuid.js b/backend/node_modules/mongoose/lib/types/uuid.js new file mode 100644 index 0000000..382c93e --- /dev/null +++ b/backend/node_modules/mongoose/lib/types/uuid.js @@ -0,0 +1,13 @@ +/** + * UUID type constructor + * + * #### Example: + * + * const id = new mongoose.Types.UUID(); + * + * @constructor UUID + */ + +'use strict'; + +module.exports = require('mongodb/lib/bson').UUID; diff --git a/backend/node_modules/mongoose/lib/utils.js b/backend/node_modules/mongoose/lib/utils.js new file mode 100644 index 0000000..e3f3c4f --- /dev/null +++ b/backend/node_modules/mongoose/lib/utils.js @@ -0,0 +1,1054 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const UUID = require('mongodb/lib/bson').UUID; +const ms = require('ms'); +const mpath = require('mpath'); +const ObjectId = require('./types/objectid'); +const PopulateOptions = require('./options/populateOptions'); +const clone = require('./helpers/clone'); +const immediate = require('./helpers/immediate'); +const isObject = require('./helpers/isObject'); +const isMongooseArray = require('./types/array/isMongooseArray'); +const isMongooseDocumentArray = require('./types/documentArray/isMongooseDocumentArray'); +const isBsonType = require('./helpers/isBsonType'); +const isPOJO = require('./helpers/isPOJO'); +const getFunctionName = require('./helpers/getFunctionName'); +const isMongooseObject = require('./helpers/isMongooseObject'); +const schemaMerge = require('./helpers/schema/merge'); +const specialProperties = require('./helpers/specialProperties'); +const { trustedSymbol } = require('./helpers/query/trusted'); + +let Document; + +exports.specialProperties = specialProperties; + +exports.isMongooseArray = isMongooseArray.isMongooseArray; +exports.isMongooseDocumentArray = isMongooseDocumentArray.isMongooseDocumentArray; +exports.registerMongooseArray = isMongooseArray.registerMongooseArray; +exports.registerMongooseDocumentArray = isMongooseDocumentArray.registerMongooseDocumentArray; + +const oneSpaceRE = /\s/; +const manySpaceRE = /\s+/; + +/** + * Produces a collection name from model `name`. By default, just returns + * the model name + * + * @param {string} name a model name + * @param {Function} pluralize function that pluralizes the collection name + * @return {string} a collection name + * @api private + */ + +exports.toCollectionName = function(name, pluralize) { + if (name === 'system.profile') { + return name; + } + if (name === 'system.indexes') { + return name; + } + if (typeof pluralize === 'function') { + if (typeof name !== 'string') { + throw new TypeError('Collection name must be a string'); + } + if (name.length === 0) { + throw new TypeError('Collection name cannot be empty'); + } + return pluralize(name); + } + return name; +}; + +/** + * Determines if `a` and `b` are deep equal. + * + * Modified from node/lib/assert.js + * + * @param {any} a a value to compare to `b` + * @param {any} b a value to compare to `a` + * @return {boolean} + * @api private + */ + +exports.deepEqual = function deepEqual(a, b) { + if (a === b) { + return true; + } + + if (typeof a !== 'object' || typeof b !== 'object') { + return a === b; + } + + if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime(); + } + + if ((isBsonType(a, 'ObjectId') && isBsonType(b, 'ObjectId')) || + (isBsonType(a, 'Decimal128') && isBsonType(b, 'Decimal128'))) { + return a.toString() === b.toString(); + } + + if (a instanceof RegExp && b instanceof RegExp) { + return a.source === b.source && + a.ignoreCase === b.ignoreCase && + a.multiline === b.multiline && + a.global === b.global && + a.dotAll === b.dotAll && + a.unicode === b.unicode && + a.sticky === b.sticky && + a.hasIndices === b.hasIndices; + } + + if (a == null || b == null) { + return false; + } + + if (a.prototype !== b.prototype) { + return false; + } + + if (a instanceof Map || b instanceof Map) { + if (!(a instanceof Map) || !(b instanceof Map)) { + return false; + } + return deepEqual(Array.from(a.keys()), Array.from(b.keys())) && + deepEqual(Array.from(a.values()), Array.from(b.values())); + } + + // Handle MongooseNumbers + if (a instanceof Number && b instanceof Number) { + return a.valueOf() === b.valueOf(); + } + + if (Buffer.isBuffer(a)) { + return exports.buffer.areEqual(a, b); + } + + if (Array.isArray(a) || Array.isArray(b)) { + if (!Array.isArray(a) || !Array.isArray(b)) { + return false; + } + const len = a.length; + if (len !== b.length) { + return false; + } + for (let i = 0; i < len; ++i) { + if (!deepEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + if (a.$__ != null) { + a = a._doc; + } else if (isMongooseObject(a)) { + a = a.toObject(); + } + + if (b.$__ != null) { + b = b._doc; + } else if (isMongooseObject(b)) { + b = b.toObject(); + } + + const ka = Object.keys(a); + const kb = Object.keys(b); + const kaLength = ka.length; + + // having the same number of owned properties (keys incorporates + // hasOwnProperty) + if (kaLength !== kb.length) { + return false; + } + + // ~~~cheap key test + for (let i = kaLength - 1; i >= 0; i--) { + if (ka[i] !== kb[i]) { + return false; + } + } + + // equivalent values for every corresponding key, and + // ~~~possibly expensive deep test + for (const key of ka) { + if (!deepEqual(a[key], b[key])) { + return false; + } + } + + return true; +}; + +/** + * Get the last element of an array + * @param {Array} arr + */ + +exports.last = function(arr) { + if (arr == null) { + return void 0; + } + if (arr.length > 0) { + return arr[arr.length - 1]; + } + return void 0; +}; + +/*! + * ignore + */ + +exports.cloneArrays = function cloneArrays(arr) { + if (!Array.isArray(arr)) { + return arr; + } + + return arr.map(el => exports.cloneArrays(el)); +}; + +/*! + * ignore + */ + +exports.omit = function omit(obj, keys) { + if (keys == null) { + return Object.assign({}, obj); + } + if (!Array.isArray(keys)) { + keys = [keys]; + } + + const ret = Object.assign({}, obj); + for (const key of keys) { + delete ret[key]; + } + return ret; +}; + +/** + * Simplified version of `clone()` that only clones POJOs and arrays. Skips documents, dates, objectids, etc. + * @param {any} val + * @returns +*/ + +exports.clonePOJOsAndArrays = function clonePOJOsAndArrays(val) { + if (val == null) { + return val; + } + // Skip documents because we assume they'll be cloned later. See gh-15312 for how documents are handled with `merge()`. + if (val.$__ != null) { + return val; + } + if (isPOJO(val)) { + val = { ...val }; + for (const key of Object.keys(val)) { + val[key] = exports.clonePOJOsAndArrays(val[key]); + } + return val; + } + if (Array.isArray(val)) { + val = [...val]; + for (let i = 0; i < val.length; ++i) { + val[i] = exports.clonePOJOsAndArrays(val[i]); + } + return val; + } + + return val; +}; + +/** + * Merges `from` into `to` without overwriting existing properties. + * + * @param {object} to + * @param {object} from + * @param {object} [options] + * @param {string} [path] + * @api private + */ + +exports.merge = function merge(to, from, options, path) { + options = options || {}; + + if (from == null) { + return to; + } + + const keys = Object.keys(from); + let i = 0; + const len = keys.length; + let key; + + if (from[trustedSymbol]) { + to[trustedSymbol] = from[trustedSymbol]; + } + + path = path || ''; + const omitNested = options.omitNested || {}; + + while (i < len) { + key = keys[i++]; + if (options.omit && options.omit[key]) { + continue; + } + if (omitNested[path]) { + continue; + } + if (specialProperties.has(key)) { + continue; + } + if (to[key] == null) { + to[key] = exports.clonePOJOsAndArrays(from[key]); + } else if (exports.isObject(from[key])) { + if (!exports.isObject(to[key])) { + to[key] = {}; + } + if (from[key] != null) { + // Skip merging schemas if we're creating a discriminator schema and + // base schema has a given path as a single nested but discriminator schema + // has the path as a document array, or vice versa (gh-9534) + if (options.isDiscriminatorSchemaMerge && + (from[key].$isSingleNested && to[key].$isMongooseDocumentArray) || + (from[key].$isMongooseDocumentArray && to[key].$isSingleNested)) { + continue; + } else if (from[key].instanceOfSchema) { + if (to[key].instanceOfSchema) { + schemaMerge(to[key], from[key].clone(), options.isDiscriminatorSchemaMerge); + } else { + to[key] = from[key].clone(); + } + continue; + } else if (isBsonType(from[key], 'ObjectId')) { + to[key] = new ObjectId(from[key]); + continue; + } + } + merge(to[key], from[key], options, path ? path + '.' + key : key); + } else if (options.overwrite) { + to[key] = from[key]; + } + } + + return to; +}; + +/** + * Applies toObject recursively. + * + * @param {Document|Array|object} obj + * @return {object} + * @api private + */ + +exports.toObject = function toObject(obj) { + Document || (Document = require('./document')); + let ret; + + if (obj == null) { + return obj; + } + + if (obj instanceof Document) { + return obj.toObject(); + } + + if (Array.isArray(obj)) { + ret = []; + + for (const doc of obj) { + ret.push(toObject(doc)); + } + + return ret; + } + + if (exports.isPOJO(obj)) { + ret = {}; + + if (obj[trustedSymbol]) { + ret[trustedSymbol] = obj[trustedSymbol]; + } + + for (const k of Object.keys(obj)) { + if (specialProperties.has(k)) { + continue; + } + ret[k] = toObject(obj[k]); + } + + return ret; + } + + return obj; +}; + +exports.isObject = isObject; + +/** + * Determines if `arg` is a plain old JavaScript object (POJO). Specifically, + * `arg` must be an object but not an instance of any special class, like String, + * ObjectId, etc. + * + * `Object.getPrototypeOf()` is part of ES5: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/getPrototypeOf + * + * @param {object|Array|string|Function|RegExp|any} arg + * @api private + * @return {boolean} + */ + +exports.isPOJO = require('./helpers/isPOJO'); + +/** + * Determines if `arg` is an object that isn't an instance of a built-in value + * class, like Array, Buffer, ObjectId, etc. + * @param {any} val + */ + +exports.isNonBuiltinObject = function isNonBuiltinObject(val) { + return typeof val === 'object' && + !exports.isNativeObject(val) && + !exports.isMongooseType(val) && + !(val instanceof UUID) && + val != null; +}; + +/** + * Determines if `obj` is a built-in object like an array, date, boolean, + * etc. + * @param {any} arg + */ + +exports.isNativeObject = function(arg) { + return Array.isArray(arg) || + arg instanceof Date || + arg instanceof Boolean || + arg instanceof Number || + arg instanceof String; +}; + +/** + * Determines if `val` is an object that has no own keys + * @param {any} val + */ + +exports.isEmptyObject = function isEmptyObject(val) { + if (val == null || typeof val !== 'object') { + return false; + } + return exports.hasOwnKeys(val) === false; +}; + +/** + * Determines if `obj` has any own keys. Assumes obj is already an object. + * Faster than Object.keys(obj).length > 0. + * @param {object} obj + */ + +exports.hasOwnKeys = function hasOwnKeys(obj) { + for (const key in obj) { + if (Object.prototype.hasOwnProperty.call(obj, key)) { + return true; + } + } + return false; +}; + +/** + * Search if `obj` or any POJOs nested underneath `obj` has a property named + * `key` + * @param {object} obj + * @param {string} key + */ + +exports.hasKey = function hasKey(obj, key) { + const props = Object.keys(obj); + for (const prop of props) { + if (prop === key) { + return true; + } + if (exports.isPOJO(obj[prop]) && exports.hasKey(obj[prop], key)) { + return true; + } + } + return false; +}; + +/** + * process.nextTick helper. + * + * Wraps `callback` in a try/catch + nextTick. + * + * node-mongodb-native has a habit of state corruption when an error is immediately thrown from within a collection callback. + * + * @param {Function} callback + * @api private + */ + +exports.tick = function tick(callback) { + if (typeof callback !== 'function') { + return; + } + return function() { + try { + callback.apply(this, arguments); + } catch (err) { + // only nextTick on err to get out of + // the event loop and avoid state corruption. + immediate(function() { + throw err; + }); + } + }; +}; + +/** + * Returns true if `v` is an object that can be serialized as a primitive in + * MongoDB + * @param {any} v + */ + +exports.isMongooseType = function(v) { + return isBsonType(v, 'ObjectId') || isBsonType(v, 'Decimal128') || v instanceof Buffer; +}; + +exports.isMongooseObject = isMongooseObject; + +/** + * Converts `expires` options of index objects to `expiresAfterSeconds` options for MongoDB. + * + * @param {object} object + * @api private + */ + +exports.expires = function expires(object) { + if (!(object && object.constructor.name === 'Object')) { + return; + } + if (!('expires' in object)) { + return; + } + + object.expireAfterSeconds = (typeof object.expires !== 'string') + ? object.expires + : Math.round(ms(object.expires) / 1000); + delete object.expires; +}; + +/** + * populate helper + * @param {string} path + * @param {string} select + * @param {Model} model + * @param {object} match + * @param {object} options + * @param {any} subPopulate + * @param {boolean} justOne + * @param {boolean} count + */ + +exports.populate = function populate(path, select, model, match, options, subPopulate, justOne, count) { + // might have passed an object specifying all arguments + let obj = null; + if (arguments.length === 1) { + if (path instanceof PopulateOptions) { + // If reusing old populate docs, avoid reusing `_docs` because that may + // lead to bugs and memory leaks. See gh-11641 + path._docs = {}; + path._childDocs = []; + return [path]; + } + + if (Array.isArray(path)) { + const singles = makeSingles(path); + return singles.map(o => exports.populate(o)[0]); + } + + if (exports.isObject(path)) { + obj = Object.assign({}, path); + } else { + obj = { path: path }; + } + } else if (typeof model === 'object') { + obj = { + path: path, + select: select, + match: model, + options: match + }; + } else { + obj = { + path: path, + select: select, + model: model, + match: match, + options: options, + populate: subPopulate, + justOne: justOne, + count: count + }; + } + + if (typeof obj.path !== 'string' && !(Array.isArray(obj.path) && obj.path.every(el => typeof el === 'string'))) { + throw new TypeError('utils.populate: invalid path. Expected string or array of strings. Got typeof `' + typeof path + '`'); + } + + return _populateObj(obj); + + // The order of select/conditions args is opposite Model.find but + // necessary to keep backward compatibility (select could be + // an array, string, or object literal). + function makeSingles(arr) { + const ret = []; + arr.forEach(function(obj) { + if (oneSpaceRE.test(obj.path)) { + const paths = obj.path.split(manySpaceRE); + paths.forEach(function(p) { + const copy = Object.assign({}, obj); + copy.path = p; + ret.push(copy); + }); + } else { + ret.push(obj); + } + }); + + return ret; + } +}; + +function _populateObj(obj) { + if (Array.isArray(obj.populate)) { + const ret = []; + obj.populate.forEach(function(obj) { + if (oneSpaceRE.test(obj.path)) { + const copy = Object.assign({}, obj); + const paths = copy.path.split(manySpaceRE); + paths.forEach(function(p) { + copy.path = p; + ret.push(exports.populate(copy)[0]); + }); + } else { + ret.push(exports.populate(obj)[0]); + } + }); + obj.populate = exports.populate(ret); + } else if (obj.populate != null && typeof obj.populate === 'object') { + obj.populate = exports.populate(obj.populate); + } + + const ret = []; + const paths = oneSpaceRE.test(obj.path) + ? obj.path.split(manySpaceRE) + : Array.isArray(obj.path) + ? obj.path + : [obj.path]; + if (obj.options != null) { + obj.options = clone(obj.options); + } + + for (const path of paths) { + ret.push(new PopulateOptions(Object.assign({}, obj, { path: path }))); + } + + return ret; +} + +/** + * Return the value of `obj` at the given `path`. + * + * @param {string} path + * @param {object} obj + * @param {any} map + */ + +exports.getValue = function(path, obj, map) { + return mpath.get(path, obj, getValueLookup, map); +}; + +/*! + * ignore + */ + +const mapGetterOptions = Object.freeze({ getters: false }); + +function getValueLookup(obj, part) { + if (part === '$*' && obj instanceof Map) { + return obj; + } + let _from = obj?._doc || obj; + if (_from?.isMongooseArrayProxy) { + _from = _from.__array; + } + return _from instanceof Map ? + _from.get(part, mapGetterOptions) : + _from[part]; +} + +/** + * Sets the value of `obj` at the given `path`. + * + * @param {string} path + * @param {Anything} val + * @param {object} obj + * @param {any} map + * @param {any} _copying + */ + +exports.setValue = function(path, val, obj, map, _copying) { + mpath.set(path, val, obj, '_doc', map, _copying); +}; + +/** + * Returns an array of values from object `o`. + * + * @param {object} o + * @return {Array} + * @api private + */ + +exports.object = {}; +exports.object.vals = function vals(o) { + if (o == null) { + return []; + } + const keys = Object.keys(o); + let i = keys.length; + const ret = []; + + while (i--) { + ret.push(o[keys[i]]); + } + + return ret; +}; + + +/** + * Determine if `val` is null or undefined + * + * @param {any} val + * @return {boolean} + */ + +exports.isNullOrUndefined = function(val) { + return val == null; +}; + +/*! + * ignore + */ + +exports.array = {}; + +/** + * Flattens an array. + * + * [ 1, [ 2, 3, [4] ]] -> [1,2,3,4] + * + * @param {Array} arr + * @param {Function} [filter] If passed, will be invoked with each item in the array. If `filter` returns a falsy value, the item will not be included in the results. + * @param {Array} ret + * @return {Array} + * @api private + */ + +exports.array.flatten = function flatten(arr, filter, ret) { + ret || (ret = []); + + arr.forEach(function(item) { + if (Array.isArray(item)) { + flatten(item, filter, ret); + } else { + if (!filter || filter(item)) { + ret.push(item); + } + } + }); + + return ret; +}; + +/*! + * ignore + */ + +exports.hasUserDefinedProperty = function(obj, key) { + if (obj == null) { + return false; + } + + if (Array.isArray(key)) { + for (const k of key) { + if (exports.hasUserDefinedProperty(obj, k)) { + return true; + } + } + return false; + } + + if (Object.hasOwn(obj, key)) { + return true; + } + if (typeof obj === 'object' && key in obj) { + const v = obj[key]; + return v !== Object.prototype[key] && v !== Array.prototype[key]; + } + + return false; +}; + +/*! + * ignore + */ + +const MAX_ARRAY_INDEX = Math.pow(2, 32) - 1; + +exports.isArrayIndex = function(val) { + if (typeof val === 'number') { + return val >= 0 && val <= MAX_ARRAY_INDEX; + } + if (typeof val === 'string') { + if (!/^\d+$/.test(val)) { + return false; + } + val = +val; + return val >= 0 && val <= MAX_ARRAY_INDEX; + } + + return false; +}; + +/** + * Removes duplicate values from an array + * + * [1, 2, 3, 3, 5] => [1, 2, 3, 5] + * [ ObjectId("550988ba0c19d57f697dc45e"), ObjectId("550988ba0c19d57f697dc45e") ] + * => [ObjectId("550988ba0c19d57f697dc45e")] + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.array.unique = function(arr) { + const primitives = new Set(); + const ids = new Set(); + const ret = []; + + for (const item of arr) { + if (typeof item === 'number' || typeof item === 'string' || item == null) { + if (primitives.has(item)) { + continue; + } + ret.push(item); + primitives.add(item); + } else if (isBsonType(item, 'ObjectId')) { + if (ids.has(item.toString())) { + continue; + } + ret.push(item); + ids.add(item.toString()); + } else { + ret.push(item); + } + } + + return ret; +}; + +exports.buffer = {}; + +/** + * Determines if two buffers are equal. + * + * @param {Buffer} a + * @param {object} b + */ + +exports.buffer.areEqual = function(a, b) { + if (!Buffer.isBuffer(a)) { + return false; + } + if (!Buffer.isBuffer(b)) { + return false; + } + return a.equals(b); +}; + +exports.getFunctionName = getFunctionName; + +/** + * Decorate buffers + * @param {object} destination + * @param {object} source + */ + +exports.decorate = function(destination, source) { + for (const key in source) { + if (specialProperties.has(key)) { + continue; + } + destination[key] = source[key]; + } +}; + +/** + * merges to with a copy of from + * + * @param {object} to + * @param {object} fromObj + * @api private + */ + +exports.mergeClone = function(to, fromObj) { + if (isMongooseObject(fromObj)) { + fromObj = fromObj.toObject({ + transform: false, + virtuals: false, + depopulate: true, + getters: false, + flattenDecimals: false + }); + } + const keys = Object.keys(fromObj); + const len = keys.length; + let i = 0; + let key; + + while (i < len) { + key = keys[i++]; + if (specialProperties.has(key)) { + continue; + } + if (typeof to[key] === 'undefined') { + to[key] = clone(fromObj[key], { + transform: false, + virtuals: false, + depopulate: true, + getters: false, + flattenDecimals: false + }); + } else { + let val = fromObj[key]; + if (val?.valueOf && !(val instanceof Date)) { + val = val.valueOf(); + } + if (exports.isObject(val)) { + let obj = val; + if (isMongooseObject(val) && !val.isMongooseBuffer) { + obj = obj.toObject({ + transform: false, + virtuals: false, + depopulate: true, + getters: false, + flattenDecimals: false + }); + } + if (val.isMongooseBuffer) { + obj = Buffer.from(obj); + } + exports.mergeClone(to[key], obj); + } else { + to[key] = clone(val, { + flattenDecimals: false + }); + } + } + } +}; + +/** + * Executes a function on each element of an array (like _.each) + * + * @param {Array} arr + * @param {Function} fn + * @api private + */ + +exports.each = function(arr, fn) { + for (const item of arr) { + fn(item); + } +}; + +/** + * Rename an object key, while preserving its position in the object + * + * @param {object} oldObj + * @param {string|number} oldKey + * @param {string|number} newKey + * @api private + */ +exports.renameObjKey = function(oldObj, oldKey, newKey) { + const keys = Object.keys(oldObj); + return keys.reduce( + (acc, val) => { + if (val === oldKey) { + acc[newKey] = oldObj[oldKey]; + } else { + acc[val] = oldObj[val]; + } + return acc; + }, + {} + ); +}; + +/*! + * ignore + */ + +exports.getOption = function(name) { + const sources = Array.prototype.slice.call(arguments, 1); + + for (const source of sources) { + if (source == null) { + continue; + } + if (source[name] != null) { + return source[name]; + } + } + + return null; +}; + +/*! + * ignore + */ + +exports.noop = function() {}; + +exports.errorToPOJO = function errorToPOJO(error) { + const isError = error instanceof Error; + if (!isError) { + throw new Error('`error` must be `instanceof Error`.'); + } + + const ret = {}; + for (const properyName of Object.getOwnPropertyNames(error)) { + ret[properyName] = error[properyName]; + } + return ret; +}; + +/*! + * ignore + */ + +exports.warn = function warn(message) { + return process.emitWarning(message, { code: 'MONGOOSE' }); +}; + + +exports.injectTimestampsOption = function injectTimestampsOption(writeOperation, timestampsOption) { + if (timestampsOption == null) { + return; + } + writeOperation.timestamps = timestampsOption; +}; diff --git a/backend/node_modules/mongoose/lib/validOptions.js b/backend/node_modules/mongoose/lib/validOptions.js new file mode 100644 index 0000000..064661e --- /dev/null +++ b/backend/node_modules/mongoose/lib/validOptions.js @@ -0,0 +1,43 @@ + +/*! + * Valid mongoose options + */ + +'use strict'; + +const VALID_OPTIONS = Object.freeze([ + 'allowDiskUse', + 'applyPluginsToChildSchemas', + 'applyPluginsToDiscriminators', + 'autoCreate', + 'autoIndex', + 'autoSearchIndex', + 'bufferCommands', + 'bufferTimeoutMS', + 'cloneSchemas', + 'createInitialConnection', + 'debug', + 'forceRepopulate', + 'id', + 'maxTimeMS', + 'objectIdGetter', + 'overwriteModels', + 'returnDocument', + 'returnOriginal', + 'runValidators', + 'sanitizeFilter', + 'sanitizeProjection', + 'selectPopulatedPaths', + 'setDefaultsOnInsert', + 'strict', + 'strictPopulate', + 'strictQuery', + 'timestamps.createdAt.immutable', + 'toJSON', + 'toObject', + 'transactionAsyncLocalStorage', + 'translateAliases', + 'updatePipeline' +]); + +module.exports = VALID_OPTIONS; diff --git a/backend/node_modules/mongoose/lib/virtualType.js b/backend/node_modules/mongoose/lib/virtualType.js new file mode 100644 index 0000000..ac85196 --- /dev/null +++ b/backend/node_modules/mongoose/lib/virtualType.js @@ -0,0 +1,204 @@ +'use strict'; + +const modelNamesFromRefPath = require('./helpers/populate/modelNamesFromRefPath'); +const utils = require('./utils'); + +const modelSymbol = require('./helpers/symbols').modelSymbol; + +/** + * VirtualType constructor + * + * This is what mongoose uses to define virtual attributes via `Schema.prototype.virtual`. + * + * #### Example: + * + * const fullname = schema.virtual('fullname'); + * fullname instanceof mongoose.VirtualType // true + * + * @param {object} options + * @param {string|Function} [options.ref] if `ref` is not nullish, this becomes a [populated virtual](https://mongoosejs.com/docs/populate.html#populate-virtuals) + * @param {string|Function} [options.localField] the local field to populate on if this is a populated virtual. + * @param {string|Function} [options.foreignField] the foreign field to populate on if this is a populated virtual. + * @param {boolean} [options.justOne=false] by default, a populated virtual is an array. If you set `justOne`, the populated virtual will be a single doc or `null`. + * @param {boolean} [options.getters=false] if you set this to `true`, Mongoose will call any custom getters you defined on this virtual + * @param {boolean} [options.count=false] if you set this to `true`, `populate()` will set this virtual to the number of populated documents, as opposed to the documents themselves, using [`Query#countDocuments()`](https://mongoosejs.com/docs/api/query.html#Query.prototype.countDocuments()) + * @param {object|Function} [options.match=null] add an extra match condition to `populate()` + * @param {number} [options.limit=null] add a default `limit` to the `populate()` query + * @param {number} [options.skip=null] add a default `skip` to the `populate()` query + * @param {number} [options.perDocumentLimit=null] For legacy reasons, `limit` with `populate()` may give incorrect results because it only executes a single query for every document being populated. If you set `perDocumentLimit`, Mongoose will ensure correct `limit` per document by executing a separate query for each document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })` will execute 2 additional queries if `.find()` returns 2 documents. + * @param {object} [options.options=null] Additional options like `limit` and `lean`. + * @param {string} name + * @api public + */ + +function VirtualType(options, name) { + this.path = name; + this.getters = []; + this.setters = []; + this.options = Object.assign({}, options); +} + +/** + * If no getters/setters, add a default + * + * @api private + */ + +VirtualType.prototype._applyDefaultGetters = function() { + if (this.getters.length > 0 || this.setters.length > 0) { + return; + } + + const path = this.path; + const internalProperty = '$' + path; + this.getters.push(function() { + return this.$locals[internalProperty]; + }); + this.setters.push(function(v) { + this.$locals[internalProperty] = v; + }); +}; + +/*! + * ignore + */ + +VirtualType.prototype.clone = function() { + const clone = new VirtualType(this.options, this.path); + clone.getters = [].concat(this.getters); + clone.setters = [].concat(this.setters); + return clone; +}; + +/** + * Adds a custom getter to this virtual. + * + * Mongoose calls the getter function with the below 3 parameters. + * + * - `value`: the value returned by the previous getter. If there is only one getter, `value` will be `undefined`. + * - `virtual`: the virtual object you called `.get()` on. + * - `doc`: the document this virtual is attached to. Equivalent to `this`. + * + * #### Example: + * + * const virtual = schema.virtual('fullname'); + * virtual.get(function(value, virtual, doc) { + * return this.name.first + ' ' + this.name.last; + * }); + * + * @param {Function} fn + * @return {VirtualType} this + * @api public + */ + +VirtualType.prototype.get = function(fn) { + this.getters.push(fn); + return this; +}; + +/** + * Adds a custom setter to this virtual. + * + * Mongoose calls the setter function with the below 3 parameters. + * + * - `value`: the value being set. + * - `virtual`: the virtual object you're calling `.set()` on. + * - `doc`: the document this virtual is attached to. Equivalent to `this`. + * + * #### Example: + * + * const virtual = schema.virtual('fullname'); + * virtual.set(function(value, virtual, doc) { + * const parts = value.split(' '); + * this.name.first = parts[0]; + * this.name.last = parts[1]; + * }); + * + * const Model = mongoose.model('Test', schema); + * const doc = new Model(); + * // Calls the setter with `value = 'Jean-Luc Picard'` + * doc.fullname = 'Jean-Luc Picard'; + * doc.name.first; // 'Jean-Luc' + * doc.name.last; // 'Picard' + * + * @param {Function} fn + * @return {VirtualType} this + * @api public + */ + +VirtualType.prototype.set = function(fn) { + this.setters.push(fn); + return this; +}; + +/** + * Applies getters to `value`. + * + * @param {any} value + * @param {Document} doc The document this virtual is attached to + * @return {any} the value after applying all getters + * @api public + */ + +VirtualType.prototype.applyGetters = function(value, doc) { + if (utils.hasUserDefinedProperty(this.options, ['ref', 'refPath']) && + doc.$$populatedVirtuals && + Object.hasOwn(doc.$$populatedVirtuals, this.path)) { + value = doc.$$populatedVirtuals[this.path]; + } + + let v = value; + for (const getter of this.getters) { + v = getter.call(doc, v, this, doc); + } + return v; +}; + +/** + * Applies setters to `value`. + * + * @param {object} value + * @param {Document} doc + * @return {any} the value after applying all setters + * @api public + */ + +VirtualType.prototype.applySetters = function(value, doc) { + let v = value; + for (const setter of this.setters) { + v = setter.call(doc, v, this, doc); + } + return v; +}; + +/** + * Get the names of models used to populate this model given a doc + * + * @param {Document} doc + * @return {string[] | null} + * @api private + */ + +VirtualType.prototype._getModelNamesForPopulate = function _getModelNamesForPopulate(doc) { + if (this.options.refPath) { + return modelNamesFromRefPath(this.options.refPath, doc, this.path); + } + + let normalizedRef = null; + if (typeof this.options.ref === 'function' && !this.options.ref[modelSymbol]) { + normalizedRef = this.options.ref.call(doc, doc); + } else { + normalizedRef = this.options.ref; + } + if (normalizedRef != null && !Array.isArray(normalizedRef)) { + return [normalizedRef]; + } + + return normalizedRef; +}; + +/*! + * exports + */ + +module.exports = VirtualType; diff --git a/backend/node_modules/mongoose/package.json b/backend/node_modules/mongoose/package.json new file mode 100644 index 0000000..fe97e9a --- /dev/null +++ b/backend/node_modules/mongoose/package.json @@ -0,0 +1,139 @@ +{ + "name": "mongoose", + "description": "Mongoose MongoDB ODM", + "version": "9.3.3", + "author": "Guillermo Rauch ", + "keywords": [ + "mongodb", + "document", + "model", + "schema", + "database", + "odm", + "data", + "datastore", + "query", + "nosql", + "orm", + "db" + ], + "type": "commonjs", + "license": "MIT", + "dependencies": { + "kareem": "3.2.0", + "mongodb": "~7.1", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "devDependencies": { + "@ark/attest": "0.56.0", + "@eslint/js": "^9.39.3", + "@mongodb-js/mongodb-downloader": "^1.0.0", + "@types/node": "^18.16.0", + "acquit": "1.4.0", + "acquit-ignore": "0.2.2", + "acquit-require": "0.1.1", + "ajv": "8.18.0", + "c8": "11.0.0", + "cheerio": "^1.2", + "dox": "1.0.0", + "eslint": "10.0.2", + "eslint-plugin-mocha-no-only": "1.2.0", + "express": "^4.19.2", + "fs-extra": "~11.3.0", + "globals": "^17.4.0", + "glob": "^13.0.6", + "highlight.js": "11.11.1", + "linkinator": "7.x", + "lodash.isequal": "4.5.0", + "lodash.isequalwith": "4.4.0", + "markdownlint-cli2": "^0.21.0", + "marked": "15.x", + "mkdirp": "^3.0.1", + "mocha": "11.7.5", + "moment": "2.30.1", + "mongodb-client-encryption": "~7.0", + "mongodb-memory-server": "11.0.1", + "mongodb-runner": "^6.0.0", + "ncp": "^2.0.0", + "pug": "3.0.3", + "sinon": "21.0.1", + "tstyche": "^6.2.0", + "typescript": "5.9.3", + "typescript-eslint": "^8.31.1", + "uuid": "11.1.0" + }, + "directories": { + "lib": "./lib/mongoose" + }, + "scripts": { + "docs:clean": "npm run docs:clean:stable", + "docs:clean:stable": "rm -rf index.html && rm -rf ./docs/*.html && rm -rf ./docs/api && rm -rf ./docs/tutorials/*.html && rm -rf ./docs/typescript/*.html && rm -rf ./docs/*.html && rm -rf ./docs/source/_docs && rm -rf ./tmp", + "docs:clean:5x": "rm -rf index.html && rm -rf ./docs/5.x && rm -rf ./docs/source/_docs && rm -rf ./tmp", + "docs:clean:6x": "rm -rf index.html && rm -rf ./docs/6.x && rm -rf ./docs/source/_docs && rm -rf ./tmp", + "docs:copy:tmp": "mkdirp ./tmp/docs/css && mkdirp ./tmp/docs/js && mkdirp ./tmp/docs/images && mkdirp ./tmp/docs/tutorials && mkdirp ./tmp/docs/typescript && mkdirp ./tmp/docs/api && ncp ./docs/css ./tmp/docs/css --filter=.css$ && ncp ./docs/js ./tmp/docs/js --filter=.js$ && ncp ./docs/images ./tmp/docs/images && ncp ./docs/tutorials ./tmp/docs/tutorials && ncp ./docs/typescript ./tmp/docs/typescript && ncp ./docs/api ./tmp/docs/api && cp index.html ./tmp && cp docs/*.html ./tmp/docs/", + "docs:copy:tmp:5x": "rm -rf ./docs/5.x && ncp ./tmp ./docs/5.x", + "docs:copy:tmp:6x": "rm -rf ./docs/6.x && ncp ./tmp ./docs/6.x", + "docs:generate": "node ./scripts/website.js", + "docs:generate:sponsorData": "node ./scripts/loadSponsorData.js", + "docs:test": "npm run docs:generate", + "docs:view": "node ./scripts/static.js", + "docs:prepare:publish:stable": "git checkout gh-pages && git merge master && npm run docs:generate", + "docs:prepare:publish:5x": "git checkout 5.x && git merge 5.x && npm run docs:clean:stable && npm run docs:generate && npm run docs:copy:tmp && git checkout gh-pages && npm run docs:copy:tmp:5x", + "docs:prepare:publish:6x": "git checkout 6.x && git merge 6.x && npm run docs:clean:stable && env DOCS_DEPLOY=true npm run docs:generate && mv ./docs/6.x ./tmp && git checkout gh-pages && npm run docs:copy:tmp:6x", + "docs:prepare:publish:7x": "env DOCS_DEPLOY=true npm run docs:generate && git checkout gh-pages && rm -rf ./docs/7.x && mv ./tmp ./docs/7.x", + "docs:prepare:publish:8x": "env DOCS_DEPLOY=true npm run docs:generate && git checkout gh-pages && rm -rf ./docs/8.x && mv ./tmp ./docs/8.x", + "docs:check-links": "linkinator http://127.0.0.1:8089 --silent --recurse --skip cdn.carbonads.com --skip m.servedby-buysellads.com --skip \"127\\.0\\.0\\.1:8089\/docs\/\\d+.x\"", + "docs:update-mongodb-links": "node ./scripts/update-mongodb-links.js", + "lint": "eslint .", + "lint-js": "eslint . --ext .js --ext .cjs", + "lint-ts": "eslint . --ext .ts", + "lint-md": "markdownlint-cli2 \"**/*.md\" \"#node_modules\" \"#benchmarks\"", + "release": "git pull && git push origin master --tags && npm publish", + "release-5x": "git pull origin 5.x && git push origin 5.x && git push origin 5.x --tags && npm publish --tag 5x", + "release-6x": "git pull origin 6.x && git push origin 6.x && git push origin 6.x --tags && npm publish --tag 6x", + "mongo": "node ./tools/repl.js", + "publish-7x": "npm publish --tag 7x", + "create-separate-require-instance": "rm -rf ./node_modules/mongoose-separate-require-instance && node ./scripts/create-tarball && tar -xzf mongoose.tgz -C ./node_modules && mv ./node_modules/package ./node_modules/mongoose-separate-require-instance", + "test": "mocha --exit ./test/*.test.js", + "test:ci": "npm run test -- --reporter min", + "test-deno": "deno run --allow-env --allow-read --allow-net --allow-run --allow-sys --allow-write ./test/deno.mjs", + "test-deno:ci": "npm run test-deno -- --reporter min", + "test-rs": "START_REPLICA_SET=1 mocha --timeout 30000 --exit ./test/*.test.js", + "test-rs:ci": "npm run test-rs -- --reporter min", + "test:types": "tstyche", + "setup-test-encryption": "node scripts/setup-encryption-tests.js", + "test-encryption": "mocha --exit ./test/encryption/*.test.js", + "test-encryption:ci": "npm run test-encryption -- --reporter min", + "tdd": "mocha --watch --inspect --recursive ./test/*.test.js --watch-files lib/**/*.js test/**/*.js", + "test-coverage": "c8 --reporter=html --reporter=text npm test", + "test-coverage:ci": "c8 --reporter=html --reporter=text npm run test:ci", + "ts-benchmark": "cd ./benchmarks/typescript/simple && npm install && npm run benchmark | node ../../../scripts/tsc-diagnostics-check", + "ts-benchmark:local": "node ./scripts/create-tarball && cd ./benchmarks/typescript/simple && rm -rf ./node_modules && npm install && npm run benchmark | node ../../../scripts/tsc-diagnostics-check", + "attest-benchmark": "node ./benchmarks/typescript/infer.bench.mts" + }, + "main": "./index.js", + "types": "./types/index.d.ts", + "engines": { + "node": ">=20.19.0" + }, + "bugs": { + "url": "https://github.com/Automattic/mongoose/issues/new" + }, + "repository": { + "type": "git", + "url": "git://github.com/Automattic/mongoose.git" + }, + "homepage": "https://mongoosejs.com", + "config": { + "mongodbMemoryServer": { + "disablePostinstall": true + } + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } +} diff --git a/backend/node_modules/mongoose/tstyche.config.json b/backend/node_modules/mongoose/tstyche.config.json new file mode 100644 index 0000000..9769cf4 --- /dev/null +++ b/backend/node_modules/mongoose/tstyche.config.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://tstyche.org/schemas/config.json", + "testFileMatch": [ + "test/types/*.test.*" + ] +} diff --git a/backend/node_modules/mongoose/types/aggregate.d.ts b/backend/node_modules/mongoose/types/aggregate.d.ts new file mode 100644 index 0000000..bd3aae8 --- /dev/null +++ b/backend/node_modules/mongoose/types/aggregate.d.ts @@ -0,0 +1,185 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + /** Extract generic type from Aggregate class */ + type AggregateExtract

= P extends Aggregate ? T : never; + + interface AggregateOptions extends Omit, SessionOption { + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + [key: string]: any; + } + + class Aggregate implements SessionOperation { + /** + * Returns an asyncIterator for use with [`for/await/of` loops](https://thecodebarbarian.com/getting-started-with-async-iterators-in-node-js) + * You do not need to call this function explicitly, the JavaScript runtime + * will call it for you. + */ + [Symbol.asyncIterator](): AsyncIterableIterator>; + + // Returns a string representation of this aggregation. + [Symbol.toStringTag]: string; + + options: AggregateOptions; + + /** + * Sets an option on this aggregation. This function will be deprecated in a + * future release. + * + * @deprecated + */ + addCursorFlag(flag: CursorFlag, value: boolean): this; + + /** + * Appends a new $addFields operator to this aggregate pipeline. + * Requires MongoDB v3.4+ to work + */ + addFields(arg: PipelineStage.AddFields['$addFields']): this; + + /** Sets the allowDiskUse option for the aggregation query */ + allowDiskUse(value: boolean): this; + + /** Appends new operators to this aggregate pipeline */ + append(...args: PipelineStage[]): this; + + /** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * Like [`.then()`](#query_Query-then), but only takes a rejection handler. + */ + catch: Promise['catch']; + + /** Set the collation. */ + collation(options: mongodb.CollationOptions): this; + + /** Appends a new $count operator to this aggregate pipeline. */ + count(fieldName: PipelineStage.Count['$count']): this; + + /** Appends a new $densify operator to this aggregate pipeline */ + densify(arg: PipelineStage.Densify['$densify']): this; + + /** + * Sets the cursor option for the aggregation query + */ + cursor(options?: Record): Cursor; + + + /** Executes the aggregate pipeline on the currently bound Model. */ + exec(): Promise; + + /** Execute the aggregation with explain */ + explain(verbosity: mongodb.ExplainVerbosityLike): Promise; + explain(): Promise; + + /** Combines multiple aggregation pipelines. */ + facet(options: PipelineStage.Facet['$facet']): this; + + /** Appends a new $fill operator to this aggregate pipeline */ + fill(arg: PipelineStage.Fill['$fill']): this; + + /** + * Executes the aggregation returning a `Promise` which will be + * resolved with `.finally()` chained. + */ + finally: Promise['finally']; + + /** Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection. */ + graphLookup(options: PipelineStage.GraphLookup['$graphLookup']): this; + + /** Appends new custom $group operator to this aggregate pipeline. */ + group(arg: PipelineStage.Group['$group']): this; + + /** Sets the hint option for the aggregation query */ + hint(value: Record | string): this; + + /** + * Appends a new $limit operator to this aggregate pipeline. + * @param num maximum number of records to pass to the next stage + */ + limit(num: PipelineStage.Limit['$limit']): this; + + /** Appends new custom $lookup operator to this aggregate pipeline. */ + lookup(options: PipelineStage.Lookup['$lookup']): this; + + /** + * Appends a new custom $match operator to this aggregate pipeline. + * @param arg $match operator contents + */ + match(arg: PipelineStage.Match['$match']): this; + + /** + * Binds this aggregate to a model. + * @param model the model to which the aggregate is to be bound + */ + model(model: Model): this; + + /** + * Returns the current model bound to this aggregate object + */ + model(): Model; + + /** Appends a new $geoNear operator to this aggregate pipeline. */ + near(arg: PipelineStage.GeoNear['$geoNear']): this; + + /** Returns the current pipeline */ + pipeline(): PipelineStage[]; + + /** Returns the current pipeline typed for use in `$unionWith` */ + pipelineForUnionWith(): PipelineStage.UnionWithPipelineStage[]; + + /** Appends a new $project operator to this aggregate pipeline. */ + project(arg: PipelineStage.Project['$project'] | string): this; + + /** Sets the readPreference option for the aggregation query. */ + read(pref: mongodb.ReadPreferenceLike): this; + + /** Sets the readConcern level for the aggregation query. */ + readConcern(level: string): this; + + /** Appends a new $redact operator to this aggregate pipeline. */ + redact(expression: PipelineStage.Redact['$redact'], thenExpr: '$$DESCEND' | '$$PRUNE' | '$$KEEP' | AnyObject, elseExpr: '$$DESCEND' | '$$PRUNE' | '$$KEEP' | AnyObject): this; + + /** Appends a new $replaceRoot operator to this aggregate pipeline. */ + replaceRoot(newRoot: PipelineStage.ReplaceRoot['$replaceRoot']['newRoot'] | string): this; + + /** + * Helper for [Atlas Text Search](https://www.mongodb.com/docs/atlas/atlas-search/tutorial/)'s + * `$search` stage. + */ + search(options: PipelineStage.Search['$search']): this; + + /** Lets you set arbitrary options, for middlewares or plugins. */ + option(value: AggregateOptions): this; + + /** Appends new custom $sample operator to this aggregate pipeline. */ + sample(arg: PipelineStage.Sample['$sample']['size']): this; + + /** Sets the session for this aggregation. Useful for [transactions](/docs/transactions.html). */ + session(session: mongodb.ClientSession | null): this; + + /** + * Appends a new $skip operator to this aggregate pipeline. + * @param num number of records to skip before next stage + */ + skip(num: PipelineStage.Skip['$skip']): this; + + /** Appends a new $sort operator to this aggregate pipeline. */ + sort(arg: string | Record | PipelineStage.Sort['$sort']): this; + + /** Provides promise for aggregate. */ + then: Promise['then']; + + /** + * Appends a new $sortByCount operator to this aggregate pipeline. Accepts either a string field name + * or a pipeline object. + */ + sortByCount(arg: string | PipelineStage.SortByCount['$sortByCount']): this; + + /** Appends new $unionWith operator to this aggregate pipeline. */ + unionWith(options: PipelineStage.UnionWith['$unionWith']): this; + + /** Appends new custom $unwind operator(s) to this aggregate pipeline. */ + unwind(...args: PipelineStage.Unwind['$unwind'][]): this; + } +} diff --git a/backend/node_modules/mongoose/types/augmentations.d.ts b/backend/node_modules/mongoose/types/augmentations.d.ts new file mode 100644 index 0000000..82aca58 --- /dev/null +++ b/backend/node_modules/mongoose/types/augmentations.d.ts @@ -0,0 +1,9 @@ +// this import is required so that types get merged instead of completely overwritten +import 'bson'; + +declare module 'bson' { + interface ObjectId { + /** Mongoose automatically adds a conveniency "_id" getter on the base ObjectId class */ + _id: this; + } +} diff --git a/backend/node_modules/mongoose/types/callback.d.ts b/backend/node_modules/mongoose/types/callback.d.ts new file mode 100644 index 0000000..370379a --- /dev/null +++ b/backend/node_modules/mongoose/types/callback.d.ts @@ -0,0 +1,8 @@ +declare module 'mongoose' { + type CallbackError = NativeError | null; + + type Callback = (error: CallbackError, result: T) => void; + + type CallbackWithoutResult = (error: CallbackError) => void; + type CallbackWithoutResultAndOptionalError = (error?: CallbackError) => void; +} diff --git a/backend/node_modules/mongoose/types/collection.d.ts b/backend/node_modules/mongoose/types/collection.d.ts new file mode 100644 index 0000000..7b61079 --- /dev/null +++ b/backend/node_modules/mongoose/types/collection.d.ts @@ -0,0 +1,49 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + export class BaseCollection extends mongodb.Collection { + /** + * Collection constructor + * @param name name of the collection + * @param conn A MongooseConnection instance + * @param opts optional collection options + */ + constructor(name: string, conn: Connection, opts?: any); + + /* + * Abstract methods. Some of these are already defined on the + * mongodb.Collection interface so they've been commented out. + */ + ensureIndex(...args: any[]): any; + findAndModify(...args: any[]): any; + getIndexes(...args: any[]): any; + + /** Formatter for debug print args */ + $format(arg: any, color?: boolean, shell?: boolean): string; + /** Debug print helper */ + $print(name: string, i: string | number, args: any[], color?: boolean, shell?: boolean): void; + + /** The collection name */ + get collectionName(): string; + /** The Connection instance */ + conn: Connection; + /** The collection name */ + name: string; + } + + /* + * section drivers/node-mongodb-native/collection.js + */ + class Collection extends BaseCollection { + /** + * Collection constructor + * @param name name of the collection + * @param conn A MongooseConnection instance + * @param opts optional collection options + */ + constructor(name: string, conn: Connection, opts?: any); + + /** Retrieves information about this collections indexes. */ + getIndexes(): ReturnType['indexInformation']>; + } +} diff --git a/backend/node_modules/mongoose/types/connection.d.ts b/backend/node_modules/mongoose/types/connection.d.ts new file mode 100644 index 0000000..e18e337 --- /dev/null +++ b/backend/node_modules/mongoose/types/connection.d.ts @@ -0,0 +1,297 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + import events = require('events'); + + /** The Mongoose module's default connection. Equivalent to `mongoose.connections[0]`, see [`connections`](#mongoose_Mongoose-connections). */ + const connection: Connection; + + /** An array containing all connections associated with this Mongoose instance. */ + const connections: Connection[]; + + /** Opens Mongoose's default connection to MongoDB, see [connections docs](https://mongoosejs.com/docs/connections.html) */ + function connect(uri: string, options?: ConnectOptions): Promise; + + /** Creates a Connection instance. */ + function createConnection(uri: string, options?: ConnectOptions): Connection; + function createConnection(): Connection; + + function disconnect(): Promise; + + /** + * Connection ready state + * + * - 0 = disconnected + * - 1 = connected + * - 2 = connecting + * - 3 = disconnecting + * - 99 = uninitialized + */ + enum ConnectionStates { + disconnected = 0, + connected = 1, + connecting = 2, + disconnecting = 3, + uninitialized = 99, + } + + /** Expose connection states for user-land */ + const STATES: typeof ConnectionStates; + + interface ConnectOptions extends mongodb.MongoClientOptions { + /** Set to false to [disable buffering](http://mongoosejs.com/docs/faq.html#callback_never_executes) on all models associated with this connection. */ + bufferCommands?: boolean; + /** The name of the database you want to use. If not provided, Mongoose uses the database name from connection string. */ + dbName?: string; + /** username for authentication, equivalent to `options.auth.user`. Maintained for backwards compatibility. */ + user?: string; + /** password for authentication, equivalent to `options.auth.password`. Maintained for backwards compatibility. */ + pass?: string; + /** Set to false to disable automatic index creation for all models associated with this connection. */ + autoIndex?: boolean; + /** Set to `false` to disable Mongoose automatically calling `createCollection()` on every model created on this connection. */ + autoCreate?: boolean; + /** + * Sanitizes query filters against [query selector injection attacks]( + * https://thecodebarbarian.com/2014/09/04/defending-against-query-selector-injection-attacks.html + * ) by wrapping any nested objects that have a property whose name starts with $ in a $eq. + */ + sanitizeFilter?: boolean; + } + + export type AnyConnectionBulkWriteModel = Omit, 'namespace'> + | Omit, 'namespace'> + | Omit, 'namespace'> + | Omit, 'namespace'> + | Omit, 'namespace'> + | Omit, 'namespace'>; + + export type ConnectionBulkWriteModel = Record> = { + [ModelName in keyof SchemaMap]: AnyConnectionBulkWriteModel & { + model: ModelName; + }; + }[keyof SchemaMap]; + + class Connection extends events.EventEmitter implements SessionStarter { + /** Runs a [db-level aggregate()](https://www.mongodb.com/docs/manual/reference/method/db.aggregate/) on this connection's underlying `db` */ + aggregate(pipeline?: PipelineStage[] | null, options?: AggregateOptions): Aggregate>; + + /** Returns a promise that resolves when this connection successfully connects to MongoDB */ + asPromise(): Promise; + + /** The Mongoose instance this connection is associated with */ + base: Mongoose; + + bulkWrite>( + ops: Array>, + options: mongodb.ClientBulkWriteOptions & { ordered: false } + ): Promise } }>; + bulkWrite>( + ops: Array>, + options?: mongodb.ClientBulkWriteOptions + ): Promise; + + /** Closes the connection */ + close(force?: boolean): Promise; + + /** Closes and destroys the connection. Connection once destroyed cannot be reopened */ + destroy(force?: boolean): Promise; + + /** Retrieves a collection, creating it if not cached. */ + collection(name: string, options?: mongodb.CreateCollectionOptions): Collection; + + /** A hash of the collections associated with this connection */ + readonly collections: { [index: string]: Collection }; + + /** A hash of the global options that are associated with this connection */ + readonly config: any; + + /** The mongodb.Db instance, set when the connection is opened */ + readonly db: mongodb.Db | undefined; + + /** + * Helper for `createCollection()`. Will explicitly create the given collection + * with specified options. Used to create [capped collections](https://www.mongodb.com/docs/manual/core/capped-collections/) + * and [views](https://www.mongodb.com/docs/manual/core/views/) from mongoose. + */ + createCollection(name: string, options?: mongodb.CreateCollectionOptions): Promise>; + + /** + * https://mongoosejs.com/docs/api/connection.html#Connection.prototype.createCollections() + */ + createCollections(continueOnError?: boolean): Promise>>; + + /** + * Removes the model named `name` from this connection, if it exists. You can + * use this function to clean up any models you created in your tests to + * prevent OverwriteModelErrors. + */ + deleteModel(name: string | RegExp): this; + + /** + * Helper for `dropCollection()`. Will delete the given collection, including + * all documents and indexes. + */ + dropCollection(collection: string): Promise; + + /** + * Helper for `dropDatabase()`. Deletes the given database, including all + * collections, documents, and indexes. + */ + dropDatabase(): Promise; + + /** Gets the value of the option `key`. */ + get(key: string): any; + + /** + * Returns the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance + * that this connection uses to talk to MongoDB. + */ + getClient(): mongodb.MongoClient; + + /** + * The host name portion of the URI. If multiple hosts, such as a replica set, + * this will contain the first host name in the URI + */ + readonly host: string; + + /** + * A number identifier for this connection. Used for debugging when + * you have [multiple connections](/docs/connections.html#multiple_connections). + */ + readonly id: number; + + /** + * Helper for MongoDB Node driver's `listCollections()`. + * Returns an array of collection names. + */ + listCollections(): Promise[]>; + + /** + * Helper for MongoDB Node driver's `listDatabases()`. + * Returns an array of database names. + */ + listDatabases(): Promise; + + /** + * A [POJO](https://masteringjs.io/tutorials/fundamentals/pojo) containing + * a map from model names to models. Contains all models that have been + * added to this connection using [`Connection#model()`](/docs/api/connection.html#connection_Connection-model). + */ + readonly models: Readonly<{ [index: string]: Model }>; + + /** Defines or retrieves a model. */ + model( + name: string, + schema?: TSchema, + collection?: string, + options?: CompileModelOptions + ): Model< + InferSchemaType, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + // If first schema generic param is set, that means we have an explicit raw doc type, + // so user should also specify a hydrated doc type if the auto inferred one isn't correct. + IsItRecordAndNotAny> extends true + ? ObtainSchemaGeneric + : HydratedDocument< + InferSchemaType, + ObtainSchemaGeneric & ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + InferSchemaType, + ObtainSchemaGeneric + >, + TSchema, + ObtainSchemaGeneric + > & ObtainSchemaGeneric; + model( + name: string, + schema?: Schema, + collection?: string, + options?: CompileModelOptions + ): U; + model(name: string, schema?: Schema, collection?: string, options?: CompileModelOptions): Model; + + /** Returns an array of model names created on this connection. */ + modelNames(): Array; + + /** The name of the database this connection points to. */ + readonly name: string; + + /** Opens the connection with a URI using `MongoClient.connect()`. */ + openUri(uri: string, options?: ConnectOptions): Promise; + + /** The password specified in the URI */ + readonly pass: string; + + /** + * The port portion of the URI. If multiple hosts, such as a replica set, + * this will contain the port from the first host name in the URI. + */ + readonly port: number; + + /** Declares a plugin executed on all schemas you pass to `conn.model()` */ + plugin(fn: (schema: S, opts?: any) => void, opts?: O): Connection; + + /** The plugins that will be applied to all models created on this connection. */ + plugins: Array; + + /** + * Connection ready state + * + * - 0 = disconnected + * - 1 = connected + * - 2 = connecting + * - 3 = disconnecting + * - 99 = uninitialized + */ + readonly readyState: ConnectionStates; + + /** Sets the value of the option `key`. */ + set(key: string, value: any): any; + + /** + * Set the [MongoDB driver `MongoClient`](https://mongodb.github.io/node-mongodb-native/7.0/classes/MongoClient.html) instance + * that this connection uses to talk to MongoDB. This is useful if you already have a MongoClient instance, and want to + * reuse it. + */ + setClient(client: mongodb.MongoClient): this; + + /** + * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + */ + startSession(options?: ClientSessionOptions): Promise; + + /** + * Makes the indexes in MongoDB match the indexes defined in every model's + * schema. This function will drop any indexes that are not defined in + * the model's schema except the `_id` index, and build any indexes that + * are in your schema but not in MongoDB. + */ + syncIndexes(options?: SyncIndexesOptions): Promise; + + /** + * _Requires MongoDB >= 3.6.0._ Executes the wrapped async function + * in a transaction. Mongoose will commit the transaction if the + * async function executes successfully and attempt to retry if + * there was a retryable error. + */ + transaction(fn: (session: mongodb.ClientSession) => Promise, options?: mongodb.TransactionOptions): Promise; + + /** Switches to a different database using the same connection pool. */ + useDb(name: string, options?: { useCache?: boolean }): Connection; + + /** The username specified in the URI */ + readonly user: string; + + /** Watches the entire underlying database for changes. Similar to [`Model.watch()`](/docs/api/model.html#model_Model-watch). */ + watch(pipeline?: Array, options?: mongodb.ChangeStreamOptions): mongodb.ChangeStream; + + withSession(executor: (session: ClientSession) => Promise): Promise; + } + + export class BaseConnection extends Connection {} +} diff --git a/backend/node_modules/mongoose/types/cursor.d.ts b/backend/node_modules/mongoose/types/cursor.d.ts new file mode 100644 index 0000000..ced66e5 --- /dev/null +++ b/backend/node_modules/mongoose/types/cursor.d.ts @@ -0,0 +1,67 @@ +declare module 'mongoose' { + + import stream = require('stream'); + + type CursorFlag = 'tailable' | 'oplogReplay' | 'noCursorTimeout' | 'awaitData' | 'partial'; + + interface EachAsyncOptions { + parallel?: number; + batchSize?: number; + continueOnError?: boolean; + signal?: AbortSignal; + } + + class Cursor extends stream.Readable { + [Symbol.asyncIterator](): Cursor, Options, IteratorResult>; + + [Symbol.asyncDispose](): Promise; + + /** + * Adds a [cursor flag](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html#addCursorFlag). + * Useful for setting the `noCursorTimeout` and `tailable` flags. + */ + addCursorFlag(flag: CursorFlag, value: boolean): this; + + /** + * Marks this cursor as closed. Will stop streaming and subsequent calls to + * `next()` will error. + */ + close(): Promise; + + /** + * Destroy this cursor, closing the underlying cursor. Will stop streaming + * and subsequent calls to `next()` will error. + */ + destroy(): this; + + /** + * Rewind this cursor to its uninitialized state. Any options that are present on the cursor will + * remain in effect. Iterating this cursor will cause new queries to be sent to the server, even + * if the resultant data has already been retrieved by this cursor. + */ + rewind(): this; + + /** + * Execute `fn` for every document(s) in the cursor. If batchSize is provided + * `fn` will be executed for each batch of documents. If `fn` returns a promise, + * will wait for the promise to resolve before iterating on to the next one. + * Returns a promise that resolves when done. + */ + eachAsync(fn: (doc: DocType[], i: number) => any, options: EachAsyncOptions & { batchSize: number }): Promise; + eachAsync(fn: (doc: DocType, i: number) => any, options?: EachAsyncOptions): Promise; + + /** + * Registers a transform function which subsequently maps documents retrieved + * via the streams interface or `.next()` + */ + map(fn: (res: DocType) => ResultType): Cursor; + + /** + * Get the next document from this cursor. Will return `null` when there are + * no documents left. + */ + next(): Promise; + + options: Options; + } +} diff --git a/backend/node_modules/mongoose/types/document.d.ts b/backend/node_modules/mongoose/types/document.d.ts new file mode 100644 index 0000000..bfe8c83 --- /dev/null +++ b/backend/node_modules/mongoose/types/document.d.ts @@ -0,0 +1,291 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + /** A list of paths to skip. If set, Mongoose will validate every modified path that is not in this list. */ + type pathsToSkip = string[] | string; + + interface ValidateOptions { + /** List of paths to skip. If set, Mongoose will validate every modified path that is not in this list. */ + pathsToSkip?: pathsToSkip; + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + } + + interface DocumentSetOptions { + merge?: boolean; + + [key: string]: any; + } + + class ModifiedPathsSnapshot {} + + /** + * Generic types for Document: + * * T - the type of _id + * * TQueryHelpers - Object with any helpers that should be mixed into the Query type + * * DocType - the type of the actual Document created + */ + class Document, TSchemaOptions = {}> { + constructor(doc?: any); + + /** This documents _id. */ + _id: T; + + /** Assert that a given path or paths is populated. Throws an error if not populated. */ + $assertPopulated(path: string | string[], values?: Partial): Omit & Paths; + + /** Clear the document's modified paths. */ + $clearModifiedPaths(): this; + + /** Returns a deep clone of this document */ + $clone(): this; + + /** + * Creates a snapshot of this document's internal change tracking state. You can later + * reset this document's change tracking state using `$restoreModifiedPathsSnapshot()`. + */ + $createModifiedPathsSnapshot(): ModifiedPathsSnapshot; + + /* Get all subdocs (by bfs) */ + $getAllSubdocs(): Document[]; + + /** Don't run validation on this path or persist changes to this path. */ + $ignore(path: string): void; + + /** Checks if a path is set to its default. If no path set, checks if any path is set to its default. */ + $isDefault(path?: string): boolean; + + /** Getter/setter, determines whether the document was removed or not. */ + $isDeleted(val?: boolean): boolean; + + /** Returns an array of all populated documents associated with the query */ + $getPopulatedDocs(): Document[]; + + /** + * Increments the numeric value at `path` by the given `val`. + * When you call `save()` on this document, Mongoose will send a + * `$inc` as opposed to a `$set`. + */ + $inc(path: string | string[], val?: number): this; + + /** + * Returns true if the given path is nullish or only contains empty objects. + * Useful for determining whether this subdoc will get stripped out by the + * [minimize option](/docs/guide.html#minimize). + */ + $isEmpty(path: string): boolean; + + /** Checks if a path is invalid */ + $isValid(path: string): boolean; + + /** + * Empty object that you can use for storing properties on the document. This + * is handy for passing data to middleware without conflicting with Mongoose + * internals. + */ + $locals: Record; + + /** Marks a path as valid, removing existing validation errors. */ + $markValid(path: string): void; + + /** Returns the model with the given name on this document's associated connection. */ + $model>(name: string): ModelType; + $model>(): ModelType; + + /** + * A string containing the current operation that Mongoose is executing + * on this document. Can be `null`, `'save'`, `'validate'`, or `'remove'`. + */ + $op: 'save' | 'validate' | 'remove' | null; + + /** + * Restore this document's change tracking state to the given snapshot. + * Note that `$restoreModifiedPathsSnapshot()` does **not** modify the document's + * properties, just resets the change tracking state. + */ + $restoreModifiedPathsSnapshot(snapshot: ModifiedPathsSnapshot): this; + + /** + * Getter/setter around the session associated with this document. Used to + * automatically set `session` if you `save()` a doc that you got from a + * query with an associated session. + */ + $session(session?: ClientSession | null): ClientSession | null; + + /** Alias for `set()`, used internally to avoid conflicts */ + $set(path: string | Record, val: any, type: any, options?: DocumentSetOptions): this; + $set(path: string | Record, val: any, options?: DocumentSetOptions): this; + $set(value: string | Record): this; + + /** Set this property to add additional query filters when Mongoose saves this document and `isNew` is false. */ + $where: Record; + + /** If this is a discriminator model, `baseModelName` is the name of the base model. */ + baseModelName?: string; + + /** Collection the model uses. */ + collection: Collection; + + /** Connection the model uses. */ + db: Connection; + + /** Removes this document from the db. */ + deleteOne(options?: QueryOptions): QueryWithHelpers< + mongodb.DeleteResult, + this, + TQueryHelpers, + DocType, + 'deleteOne' + >; + + /** + * Takes a populated field and returns it to its unpopulated state. If called with + * no arguments, then all populated fields are returned to their unpopulated state. + */ + depopulate(path?: string | string[]): MergeType; + + /** + * Returns the list of paths that have been directly modified. A direct + * modified path is a path that you explicitly set, whether via `doc.foo = 'bar'`, + * `Object.assign(doc, { foo: 'bar' })`, or `doc.set('foo', 'bar')`. + */ + directModifiedPaths(): Array; + + /** + * Returns true if this document is equal to another document. + * + * Documents are considered equal when they have matching `_id`s, unless neither + * document has an `_id`, in which case this function falls back to using + * `deepEqual()`. + */ + equals(doc: Document): boolean; + + /** Returns the current validation errors. */ + errors?: Error.ValidationError; + + /** Returns the value of a path. */ + get(path: T, type?: any, options?: any): DocType[T]; + get(path: string, type?: any, options?: any): any; + + /** + * Returns the changes that happened to the document + * in the format that will be sent to MongoDB. + */ + getChanges(): UpdateQuery; + + /** Signal that we desire an increment of this documents version. */ + increment(): this; + + /** + * Initializes the document without setters or marking anything modified. + * Called internally after a document is returned from mongodb. Normally, + * you do **not** need to call this function on your own. + */ + init(obj: AnyObject, opts?: AnyObject): this; + + /** Marks a path as invalid, causing validation to fail. */ + invalidate(path: T, errorMsg: string | NativeError, value?: any, kind?: string): NativeError | null; + invalidate(path: string, errorMsg: string | NativeError, value?: any, kind?: string): NativeError | null; + + /** Returns true if `path` was directly set and modified, else false. */ + isDirectModified(path: T | Array): boolean; + isDirectModified(path: string | Array): boolean; + + /** Checks if `path` was explicitly selected. If no projection, always returns true. */ + isDirectSelected(path: T): boolean; + isDirectSelected(path: string): boolean; + + /** Checks if `path` is in the `init` state, that is, it was set by `Document#init()` and not modified since. */ + isInit(path: T): boolean; + isInit(path: string): boolean; + + /** + * Returns true if any of the given paths are modified, else false. If no arguments, returns `true` if any path + * in this document is modified. + */ + isModified(path?: T | Array, options?: { ignoreAtomics?: boolean } | null): boolean; + isModified(path?: string | Array, options?: { ignoreAtomics?: boolean } | null): boolean; + + /** Boolean flag specifying if the document is new. */ + isNew: boolean; + + /** Checks if `path` was selected in the source query which initialized this document. */ + isSelected(path: T): boolean; + isSelected(path: string): boolean; + + /** Marks the path as having pending changes to write to the db. */ + markModified(path: T, scope?: any): void; + markModified(path: string, scope?: any): void; + + /** Returns the model with the given name on this document's associated connection. */ + model>(name: string): ModelType; + model>(): ModelType; + + /** Returns the list of paths that have been modified. */ + modifiedPaths(options?: { includeChildren?: boolean }): Array; + + /** + * Overwrite all values in this document with the values of `obj`, except + * for immutable properties. Behaves similarly to `set()`, except for it + * unsets all properties that aren't in `obj`. + */ + overwrite(obj: AnyObject): this; + + /** + * If this document is a subdocument or populated document, returns the + * document's parent. Returns undefined otherwise. + */ + $parent(): Document | undefined; + + /** Populates document references. */ + populate(path: string | PopulateOptions | (string | PopulateOptions)[]): Promise>; + populate(path: string, select?: string | AnyObject, model?: Model, match?: AnyObject, options?: PopulateOptions): Promise>; + + /** Gets _id(s) used during population of the given `path`. If the path was not populated, returns `undefined`. */ + populated(path: string): any; + + /** Sends a replaceOne command with this document `_id` as the query selector. */ + replaceOne(replacement?: AnyObject, options?: QueryOptions | null): Query; + + /** Saves this document by inserting a new document into the database if [document.isNew](/docs/api/document.html#document_Document-isNew) is `true`, or sends an [updateOne](/docs/api/document.html#document_Document-updateOne) operation with just the modified paths if `isNew` is `false`. */ + save(options?: SaveOptions): Promise; + + /** The document's schema. */ + schema: Schema; + + /** Sets the value of a path, or many paths. */ + set(path: T, val: DocType[T], type: any, options?: DocumentSetOptions): this; + set(path: string | Record, val: any, type: any, options?: DocumentSetOptions): this; + set(path: string | Record, val: any, options?: DocumentSetOptions): this; + set(value: string | Record): this; + + toBSON(): Require_id; + + /** The return value of this method is used in calls to JSON.stringify(doc). */ + toJSON(options: O): ToObjectReturnType; + toJSON(options?: ToObjectOptions): Default__v, TSchemaOptions>; + toJSON(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; + + /** Converts this document into a plain-old JavaScript object ([POJO](https://masteringjs.io/tutorials/fundamentals/pojo)). */ + toObject(options: O): ToObjectReturnType; + toObject(options?: ToObjectOptions): Default__v, TSchemaOptions>; + toObject(options?: ToObjectOptions): Default__v, ResolveSchemaOptions>; + + /** Clears the modified state on the specified path. */ + unmarkModified(path: T): void; + unmarkModified(path: string): void; + + /** Sends an updateOne command with this document `_id` as the query selector. */ + updateOne(update?: UpdateQuery | UpdateWithAggregationPipeline, options?: QueryOptions | null): Query; + + /** Executes registered validation rules for this document. */ + validate(pathsToValidate?: T | T[], options?: Omit & AnyObject): Promise; + validate(pathsToValidate?: pathsToValidate, options?: Omit & AnyObject): Promise; + validate(options: ValidateOptions): Promise; + + /** Executes registered validation rules (skipping asynchronous validators) for this document. */ + validateSync(options: ValidateOptions & { [k: string]: any }): Error.ValidationError | null; + validateSync(pathsToValidate?: T | T[], options?: Omit & AnyObject): Error.ValidationError | null; + validateSync(pathsToValidate?: pathsToValidate, options?: Omit & AnyObject): Error.ValidationError | null; + } +} diff --git a/backend/node_modules/mongoose/types/error.d.ts b/backend/node_modules/mongoose/types/error.d.ts new file mode 100644 index 0000000..cf34cb6 --- /dev/null +++ b/backend/node_modules/mongoose/types/error.d.ts @@ -0,0 +1,143 @@ +declare class NativeError extends global.Error { } + +declare module 'mongoose' { + import mongodb = require('mongodb'); + + type CastError = Error.CastError; + type SyncIndexesError = Error.SyncIndexesError; + + export class MongooseError extends global.Error { + constructor(msg: string); + + /** The type of error. "MongooseError" for generic errors. */ + name: string; + + static messages: any; + + static Messages: any; + } + + class Error extends MongooseError { } + + namespace Error { + + export class CastError extends MongooseError { + name: 'CastError'; + stringValue: string; + kind: string; + value: any; + path: string; + reason?: NativeError | null; + + constructor(type: string, value: any, path: string, reason?: NativeError, schemaType?: SchemaType); + } + export class SyncIndexesError extends MongooseError { + name: 'SyncIndexesError'; + errors?: Record; + + constructor(type: string, value: any, path: string, reason?: NativeError, schemaType?: SchemaType); + } + + export class DivergentArrayError extends MongooseError { + name: 'DivergentArrayError'; + } + + export class MissingSchemaError extends MongooseError { + name: 'MissingSchemaError'; + } + + export class DocumentNotFoundError extends MongooseError { + name: 'DocumentNotFoundError'; + result: any; + numAffected: number; + filter: any; + query: any; + } + + export class ObjectExpectedError extends MongooseError { + name: 'ObjectExpectedError'; + path: string; + } + + export class ObjectParameterError extends MongooseError { + name: 'ObjectParameterError'; + } + + export class OverwriteModelError extends MongooseError { + name: 'OverwriteModelError'; + } + + export class ParallelSaveError extends MongooseError { + name: 'ParallelSaveError'; + } + + export class ParallelValidateError extends MongooseError { + name: 'ParallelValidateError'; + } + + export class MongooseServerSelectionError extends MongooseError { + name: 'MongooseServerSelectionError'; + } + + export class StrictModeError extends MongooseError { + name: 'StrictModeError'; + isImmutableError: boolean; + path: string; + } + + export class ValidationError extends MongooseError { + name: 'ValidationError'; + + errors: { [path: string]: ValidatorError | CastError }; + addError: (path: string, error: ValidatorError | CastError) => void; + + /** Index of the document in insertMany() that failed validation (only set for unordered insertMany) */ + index?: number; + + constructor(instance?: MongooseError); + } + + export class ValidatorError extends MongooseError { + name: 'ValidatorError'; + properties: { + message: string, + type?: string, + path?: string, + value?: any, + reason?: any + }; + kind: string; + path: string; + value: any; + reason?: MongooseError | null; + + constructor(properties: { + message?: string, + type?: string, + path?: string, + value?: any, + reason?: any + }); + } + + export class VersionError extends MongooseError { + name: 'VersionError'; + version: number; + modifiedPaths: Array; + + constructor(doc: Document, currentVersion: number, modifiedPaths: Array); + } + + export class StrictPopulateError extends MongooseError { + name: 'StrictPopulateError'; + path: string; + } + + export class MongooseBulkSaveIncompleteError extends MongooseError { + name: 'MongooseBulkSaveIncompleteError'; + modelName: string; + bulkWriteResult: mongodb.BulkWriteResult; + numDocumentsNotUpdated: number; + } + } +} diff --git a/backend/node_modules/mongoose/types/expressions.d.ts b/backend/node_modules/mongoose/types/expressions.d.ts new file mode 100644 index 0000000..998e5f0 --- /dev/null +++ b/backend/node_modules/mongoose/types/expressions.d.ts @@ -0,0 +1,3053 @@ +declare module 'mongoose' { + + /** + * [Expressions reference](https://www.mongodb.com/docs/manual/meta/aggregation-quick-reference/#expressions) + */ + type AggregationVariables = + SpecialPathVariables | + '$$NOW' | + '$$CLUSTER_TIME' | + '$$DESCEND' | + '$$PRUNE' | + '$$KEEP'; + + type SpecialPathVariables = + '$$ROOT' | + '$$CURRENT' | + '$$REMOVE'; + + export namespace Expression { + export interface Abs { + /** + * Returns the absolute value of a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/abs/#mongodb-expression-exp.-abs + */ + $abs: Path | ArithmeticExpressionOperator; + } + + export interface Add { + /** + * Adds numbers to return the sum, or adds numbers and a date to return a new date. If adding numbers and a date, treats the numbers as milliseconds. Accepts any number of argument expressions, but at most, one expression can resolve to a date. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/add/#mongodb-expression-exp.-add + */ + $add: Expression[]; + } + + export interface Ceil { + /** + * Returns the smallest integer greater than or equal to the specified number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ceil/#mongodb-expression-exp.-ceil + */ + $ceil: NumberExpression; + } + + export interface Divide { + /** + * Returns the result of dividing the first number by the second. Accepts two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/divide/#mongodb-expression-exp.-divide + */ + $divide: NumberExpression[]; + } + + export interface Exp { + /** + * Raises e to the specified exponent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/exp/#mongodb-expression-exp.-exp + */ + $exp: NumberExpression; + } + + export interface Floor { + /** + * Returns the largest integer less than or equal to the specified number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/floor/#mongodb-expression-exp.-floor + */ + $floor: NumberExpression; + } + + export interface Ln { + /** + * Calculates the natural log of a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ln/#mongodb-expression-exp.-ln + */ + $ln: NumberExpression; + } + + export interface Log { + /** + * Calculates the log of a number in the specified base. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/log/#mongodb-expression-exp.-log + */ + $log: [NumberExpression, NumberExpression]; + } + + export interface Log10 { + /** + * Calculates the log base 10 of a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/log10/#mongodb-expression-exp.-log10 + */ + $log10: NumberExpression; + } + + export interface Mod { + /** + * Returns the remainder of the first number divided by the second. Accepts two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/mod/#mongodb-expression-exp.-mod + */ + $mod: [NumberExpression, NumberExpression]; + } + export interface Multiply { + /** + * Multiplies numbers to return the product. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/multiply/#mongodb-expression-exp.-multiply + */ + $multiply: NumberExpression[]; + } + + export interface Pow { + /** + * Raises a number to the specified exponent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/pow/#mongodb-expression-exp.-pow + */ + $pow: [NumberExpression, NumberExpression]; + } + + export interface Round { + /** + * Rounds a number to to a whole integer or to a specified decimal place. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/round/#mongodb-expression-exp.-round + */ + $round: [NumberExpression, NumberExpression?]; + } + + export interface Sqrt { + /** + * Calculates the square root. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sqrt/#mongodb-expression-exp.-sqrt + */ + $sqrt: NumberExpression; + } + + export interface Subtract { + /** + * Returns the result of subtracting the second value from the first. If the two values are numbers, return the difference. If the two values are dates, return the difference in milliseconds. If the two values are a date and a number in milliseconds, return the resulting date. Accepts two argument expressions. If the two values are a date and a number, specify the date argument first as it is not meaningful to subtract a date from a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/#mongodb-expression-exp.-subtract + */ + $subtract: (NumberExpression | DateExpression)[]; + } + + export interface Trunc { + /** + * Truncates a number to a whole integer or to a specified decimal place. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/trunc/#mongodb-expression-exp.-trunc + */ + $trunc: [NumberExpression, NumberExpression?]; + } + + export interface Sin { + /** + * Returns the sine of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sin/#mongodb-expression-exp.-sin + */ + $sin: NumberExpression; + } + + export interface Cos { + /** + * Returns the cosine of a value that is measured in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/cos/#mongodb-expression-exp.-cos + */ + $cos: NumberExpression; + } + + export interface Tan { + /** + * Returns the tangent of a value that is measured in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tan/#mongodb-expression-exp.-tan + */ + $tan: NumberExpression; + } + + export interface Asin { + /** + * Returns the inverse sin (arc sine) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/asin/#mongodb-expression-exp.-asin + */ + $asin: NumberExpression; + } + + export interface Acos { + /** + * Returns the inverse cosine (arc cosine) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/acos/#mongodb-expression-exp.-acos + */ + $acos: NumberExpression; + } + + export interface Atan { + /** + * Returns the inverse tangent (arc tangent) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan/#mongodb-expression-exp.-atan + */ + $atan: NumberExpression; + } + + export interface Atan2 { + /** + * Returns the inverse tangent (arc tangent) of y / x in radians, where y and x are the first and second values passed to the expression respectively. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan2/#mongodb-expression-exp.-atan2 + */ + $atan2: NumberExpression; + } + + export interface Asinh { + /** + * Returns the inverse hyperbolic sine (hyperbolic arc sine) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/asinh/#mongodb-expression-exp.-asinh + */ + $asinh: NumberExpression; + } + + export interface Acosh { + /** + * Returns the inverse hyperbolic cosine (hyperbolic arc cosine) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/acosh/#mongodb-expression-exp.-acosh + */ + $acosh: NumberExpression; + } + + export interface Atanh { + + /** + * Returns the inverse hyperbolic tangent (hyperbolic arc tangent) of a value in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/atanh/#mongodb-expression-exp.-atanh + */ + $atanh: NumberExpression; + } + + export interface Sinh { + /** + * Returns the hyperbolic sine of a value that is measured in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sinh/#mongodb-expression-exp.-sinh + */ + $sinh: NumberExpression; + } + + export interface Cosh { + /** + * Returns the hyperbolic cosine of a value that is measured in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/cosh/#mongodb-expression-exp.-cosh + */ + $cosh: NumberExpression; + } + + export interface Tanh { + /** + * Returns the hyperbolic tangent of a value that is measured in radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tanh/#mongodb-expression-exp.-tanh + */ + $tanh: NumberExpression; + } + + export interface DegreesToRadians { + /** + * Converts a value from degrees to radians. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/degreesToRadians/#mongodb-expression-exp.-degreesToRadians + */ + $degreesToRadians: NumberExpression; + } + + export interface RadiansToDegrees { + /** + * Converts a value from radians to degrees. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/radiansToDegrees/#mongodb-expression-exp.-radiansToDegrees + */ + $radiansToDegrees: NumberExpression; + } + + export interface Meta { + /** + * Access available per-document metadata related to the aggregation operation. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/meta/#mongodb-expression-exp.-meta + */ + $meta: 'textScore' | 'indexKey'; + } + + export interface DateAdd { + /** + * Adds a number of time units to a date object. + * + * @version 5.0.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/#mongodb-expression-exp.-dateAdd + */ + $dateAdd: { + /** + * The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + startDate: DateExpression; + /** + * The unit used to measure the amount of time added to the startDate. The unit is an expression that resolves to one of the following strings: + * - year + * - quarter + * - week + * - month + * - day + * - hour + * - minute + * - second + * - millisecond + */ + unit: StringExpression; + /** + * The number of units added to the startDate. The amount is an expression that resolves to an integer or long. The amount can also resolve to an integral decimal or a double if that value can be converted to a long without loss of precision. + */ + amount: NumberExpression; + /** + * The timezone to carry out the operation. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface DateDiff { + /** + * Returns the difference between two dates. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/#mongodb-expression-exp.-dateDiff + */ + $dateDiff: { + /** + * The start of the time period. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + startDate: DateExpression; + /** + * The end of the time period. The endDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + endDate: DateExpression; + /** + * The time measurement unit between the startDate and endDate. It is an expression that resolves to a string: + * - year + * - quarter + * - week + * - month + * - day + * - hour + * - minute + * - second + * - millisecond + */ + unit: StringExpression; + /** + * The timezone to carry out the operation. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + /** + * Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string: + * - monday (or mon) + * - tuesday (or tue) + * - wednesday (or wed) + * - thursday (or thu) + * - friday (or fri) + * - saturday (or sat) + * - sunday (or sun) + */ + startOfWeek?: StringExpression; + } + } + + // TODO: Can be done better + export interface DateFromParts { + /** + * Constructs a BSON Date object given the date's constituent parts. + * + * @version 3.6 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromParts/#mongodb-expression-exp.-dateFromParts + */ + $dateFromParts: { + /** + * ISO Week Date Year. Can be any expression that evaluates to a number. + * + * Value range: 1-9999 + * + * If the number specified is outside this range, $dateFromParts errors. Starting in MongoDB 4.4, the lower bound for this value is 1. In previous versions of MongoDB, the lower bound was 0. + */ + isoWeekYear?: NumberExpression; + /** + * Week of year. Can be any expression that evaluates to a number. + * + * Defaults to 1. + * + * Value range: 1-53 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + isoWeek?: NumberExpression; + /** + * Day of week (Monday 1 - Sunday 7). Can be any expression that evaluates to a number. + * + * Defaults to 1. + * + * Value range: 1-7 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + isoDayOfWeek?: NumberExpression; + /** + * Calendar year. Can be any expression that evaluates to a number. + * + * Value range: 1-9999 + * + * If the number specified is outside this range, $dateFromParts errors. Starting in MongoDB 4.4, the lower bound for this value is 1. In previous versions of MongoDB, the lower bound was 0. + */ + year?: NumberExpression; + /** + * Month. Can be any expression that evaluates to a number. + * + * Defaults to 1. + * + * Value range: 1-12 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + month?: NumberExpression; + /** + * Day of month. Can be any expression that evaluates to a number. + * + * Defaults to 1. + * + * Value range: 1-31 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + day?: NumberExpression; + /** + * Hour. Can be any expression that evaluates to a number. + * + * Defaults to 0. + * + * Value range: 0-23 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + hour?: NumberExpression; + /** + * Minute. Can be any expression that evaluates to a number. + * + * Defaults to 0. + * + * Value range: 0-59 Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + minute?: NumberExpression; + /** + * Second. Can be any expression that evaluates to a number. + * + * Defaults to 0. + * + * Value range: 0-59 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + second?: NumberExpression; + /** + * Millisecond. Can be any expression that evaluates to a number. + * + * Defaults to 0. + * + * Value range: 0-999 + * + * Starting in MongoDB 4.0, if the number specified is outside this range, $dateFromParts incorporates the difference in the date calculation. See Value Range for examples. + */ + millisecond?: NumberExpression; + /** + * The timezone to carry out the operation. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface DateFromString { + /** + * Converts a date/time string to a date object. + * + * @version 3.6 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/#mongodb-expression-exp.-dateFromString + */ + $dateFromString: { + dateString: StringExpression; + /** + * The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. For a list of specifiers available, see Format Specifiers. + * + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format. + * @version 4.0 + */ + format?: FormatString; + /** + * The time zone to use to format the date. + * + * Note: If the dateString argument is formatted like '2017-02-08T12:10:40.787Z', in which the 'Z' at the end indicates Zulu time (UTC time zone), you cannot specify the timezone argument. + */ + timezone?: tzExpression; + /** + * Optional. If $dateFromString encounters an error while parsing the given dateString, it outputs the result value of the provided onError expression. This result value can be of any type. + * + * If you do not specify onError, $dateFromString throws an error if it cannot parse dateString. + */ + onError?: Expression; + /** + * Optional. If the dateString provided to $dateFromString is null or missing, it outputs the result value of the provided onNull expression. This result value can be of any type. + * + * If you do not specify onNull and dateString is null or missing, then $dateFromString outputs null. + */ + onNull?: Expression; + }; + } + + export interface DateSubtract { + /** + * Subtracts a number of time units from a date object. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/#mongodb-expression-exp.-dateSubtract + */ + $dateSubtract: { + /** + * The beginning date, in UTC, for the subtraction operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + startDate: DateExpression; + /** + * The unit of time, specified as an expression that must resolve to one of these strings: + * - year + * - quarter + * - week + * - month + * - day + * - hour + * - minute + * - second + * - millisecond + * + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + */ + unit: StringExpression; + /** + * The number of units subtracted from the startDate. The amount is an expression that resolves to an integer or long. The amount can also resolve to an integral decimal and or a double if that value can be converted to a long without loss of precision. + */ + amount: NumberExpression; + /** + * The timezone to carry out the operation. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface DateToParts { + /** + * Returns a document containing the constituent parts of a date. + * + * @version 3.6 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToParts/#mongodb-expression-exp.-dateToParts + */ + $dateToParts: { + /** + * The input date for which to return parts. can be any expression that resolves to a Date, a Timestamp, or an ObjectID. For more information on expressions, see Expressions. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * + * @version 3.6 + */ + timezone?: tzExpression; + /** + * If set to true, modifies the output document to use ISO week date fields. Defaults to false. + */ + iso8601?: boolean; + }; + } + + export interface DateToString { + /** + * Returns the date as a formatted string. + * + * @version 3.6 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToString/#mongodb-expression-exp.-dateToString + */ + $dateToString: { + /** + * The date to convert to string. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The date format specification. can be any string literal, containing 0 or more format specifiers. For a list of specifiers available, see Format Specifiers. + * + * If unspecified, $dateToString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format. + * + * Changed in version 4.0: The format field is optional if featureCompatibilityVersion (fCV) is set to "4.0" or greater. For more information on fCV, see setFeatureCompatibilityVersion. + */ + format?: FormatString; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * + * @version 3.6 + */ + timezone?: tzExpression; + /** + * The value to return if the date is null or missing. The arguments can be any valid expression. + * + * If unspecified, $dateToString returns null if the date is null or missing. + * + * Changed in version 4.0: Requires featureCompatibilityVersion (fCV) set to "4.0" or greater. For more information on fCV, see setFeatureCompatibilityVersion. + */ + onNull?: Expression; + }; + } + + export interface DateTrunc { + /** + * Truncates a date. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/#mongodb-expression-exp.-dateTrunc + */ + $dateTrunc: { + /** + * The date to truncate, specified in UTC. The date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The unit of time, specified as an expression that must resolve to one of these strings: + * - year + * - quarter + * - week + * - month + * - day + * - hour + * - minute + * - second + * - millisecond + * + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + */ + unit: StringExpression; + /** + * The numeric time value, specified as an expression that must resolve to a positive non-zero number. Defaults to 1. + * + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + */ + binSize?: NumberExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + /** + * Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string: + * - monday (or mon) + * - tuesday (or tue) + * - wednesday (or wed) + * - thursday (or thu) + * - friday (or fri) + * - saturday (or sat) + * - sunday (or sun) + */ + startOfWeek?: StringExpression; + } + } + + export interface DayOfMonth { + /** + * Returns the day of the month for a date as a number between 1 and 31. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfMonth/#mongodb-expression-exp.-dayOfMonth + */ + $dayOfMonth: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface DayOfWeek { + /** + * Returns the day of the week for a date as a number between 1 (Sunday) and 7 (Saturday). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfWeek/#mongodb-expression-exp.-dayOfWeek + */ + $dayOfWeek: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface DayOfYear { + /** + * Returns the day of the year for a date as a number between 1 and 366 (leap year). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfYear/#mongodb-expression-exp.-dayOfYear + */ + $dayOfYear: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Hour { + /** + * Returns the hour for a date as a number between 0 and 23. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/hour/#mongodb-expression-exp.-hour + */ + $hour: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface IsoDayOfWeek { + /** + * Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) to 7 (for Sunday). + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoDayOfWeek/#mongodb-expression-exp.-isoDayOfWeek + */ + $isoDayOfWeek: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface IsoWeek { + /** + * Returns the week number in ISO 8601 format, ranging from 1 to 53. Week numbers start at 1 with the week (Monday through Sunday) that contains the year's first Thursday. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeek/#mongodb-expression-exp.-isoWeek + */ + $isoWeek: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface IsoWeekYear { + /** + * Returns the year number in ISO 8601 format. The year starts with the Monday of week 1 (ISO 8601) and ends with the Sunday of the last week (ISO 8601). + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeekYear/#mongodb-expression-exp.-isoWeekYear + */ + $isoWeekYear: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Millisecond { + /** + * Returns the milliseconds of a date as a number between 0 and 999. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/millisecond/#mongodb-expression-exp.-millisecond + */ + $millisecond: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Minute { + /** + * Returns the minute for a date as a number between 0 and 59. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minute/#mongodb-expression-exp.-minute + */ + $minute: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Month { + /** + * Returns the month for a date as a number between 1 (January) and 12 (December). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/month/#mongodb-expression-exp.-month + */ + $month: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Second { + /** + * Returns the seconds for a date as a number between 0 and 60 (leap seconds). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/second/#mongodb-expression-exp.-second + */ + $second: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface ToDate { + /** + * Converts value to a Date. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDate/#mongodb-expression-exp.-toDate + */ + $toDate: Expression; + } + + export interface Week { + /** + * Returns the week number for a date as a number between 0 (the partial week that precedes the first Sunday of the year) and 53 (leap year). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/week/#mongodb-expression-exp.-week + */ + $week: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface Year { + /** + * Returns the year for a date as a number (e.g. 2014). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/year/#mongodb-expression-exp.-year + */ + $year: DateExpression | { + /** + * The date to which the operator is applied. must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + */ + date: DateExpression; + /** + * The timezone of the operation result. must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + timezone?: tzExpression; + }; + } + + export interface And { + /** + * Returns true only when all its expressions evaluate to true. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/and/#mongodb-expression-exp.-and + */ + $and: (Expression | Record)[]; + } + + export interface Not { + /** + * Returns the boolean value that is the opposite of its argument expression. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/not/#mongodb-expression-exp.-not + */ + $not: [Expression]; + } + + export interface Or { + /** + * Returns true when any of its expressions evaluates to true. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/#mongodb-expression-exp.-or + */ + $or: (Expression | Record)[]; + } + + export interface Cmp { + /** + * Returns 0 if the two values are equivalent, 1 if the first value is greater than the second, and -1 if the first value is less than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/cmp/#mongodb-expression-exp.-cmp + */ + $cmp: [Record | Expression, Record | Expression]; + } + + export interface Eq { + /** + * Returns true if the values are equivalent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/eq/#mongodb-expression-exp.-eq + */ + $eq: AnyExpression | [AnyExpression, AnyExpression]; + } + + export interface Gt { + /** + * Returns true if the first value is greater than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/gt/#mongodb-expression-exp.-gt + */ + $gt: NumberExpression | [NumberExpression, NumberExpression]; + } + + export interface Gte { + /** + * Returns true if the first value is greater than or equal to the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/gte/#mongodb-expression-exp.-gte + */ + $gte: NumberExpression | [NumberExpression, NumberExpression]; + } + + export interface Lt { + /** + * Returns true if the first value is less than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lt/#mongodb-expression-exp.-lt + */ + $lt: NumberExpression | [NumberExpression, NumberExpression]; + } + + export interface Lte { + /** + * Returns true if the first value is less than or equal to the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lte/#mongodb-expression-exp.-lte + */ + $lte: NumberExpression | [NumberExpression, NumberExpression]; + } + + export interface Ne { + /** + * Returns true if the values are not equivalent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ne/#mongodb-expression-exp.-ne + */ + $ne: Expression | [Expression, Expression | NullExpression] | null; + } + + export interface Cond { + /** + * A ternary operator that evaluates one expression, and depending on the result, returns the value of one of the other two expressions. Accepts either three expressions in an ordered list or three named parameters. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/cond/#mongodb-expression-exp.-cond + */ + $cond: { if: Expression, then: AnyExpression, else: AnyExpression } | [BooleanExpression, AnyExpression, AnyExpression]; + } + + export interface IfNull { + /** + * Returns either the non-null result of the first expression or the result of the second expression if the first expression results in a null result. Null result encompasses instances of undefined values or missing fields. Accepts two expressions as arguments. The result of the second expression can be null. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ifNull/#mongodb-expression-exp.-ifNull + */ + $ifNull: Expression[]; + } + + export interface Switch { + /** + * Evaluates a series of case expressions. When it finds an expression which evaluates to true, $switch executes a specified expression and breaks out of the control flow. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/switch/#mongodb-expression-exp.-switch + */ + $switch: { + /** + * An array of control branch documents. Each branch is a document with the following fields: + * - $case + * - $then + */ + branches: { case: Expression, then: Expression }[]; + /** + * The path to take if no branch case expression evaluates to true. + * + * Although optional, if default is unspecified and no branch case evaluates to true, $switch returns an error. + */ + default: Expression; + }; + } + + export interface ArrayElemAt { + /** + * Returns the element at the specified array index. + * + * @version 3.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayElemAt/#mongodb-expression-exp.-arrayElemAt + */ + $arrayElemAt: [ArrayExpression, NumberExpression]; + } + + export interface ArrayToObject { + /** + * Converts an array of key value pairs to a document. + * + * @version 3.4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayToObject/#mongodb-expression-exp.-arrayToObject + */ + $arrayToObject: ArrayExpression; + } + + export interface ConcatArrays { + /** + * Concatenates arrays to return the concatenated array. + * + * @version 3.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/concatArrays/#mongodb-expression-exp.-concatArrays + */ + $concatArrays: Expression[]; + } + + export interface Filter { + /** + * Selects a subset of the array to return an array with only the elements that match the filter condition. + * + * @version 3.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#mongodb-expression-exp.-filter + */ + $filter: { + /** + * An expression that resolves to an array. + */ + input: ArrayExpression; + /** + * A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + */ + as?: string; + /** + * An expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as. + */ + cond: BooleanExpression; + /** + * A number expression that restricts the number of matching array elements that $filter returns. You cannot specify a limit less than 1. The matching array elements are returned in the order they appear in the input array. + * + * If the specified limit is greater than the number of matching array elements, $filter returns all matching array elements. + * If the limit is null, $filter returns all matching array elements. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#using-the-limit-field + */ + limit?: NumberExpression; + } + } + + export interface First { + /** + * Returns the first array element. Distinct from $first accumulator. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/#mongodb-expression-exp.-first + */ + $first: Expression; + } + + export interface FirstN { + /** + * $firstN can be used as an aggregation accumulator or array operator. + * As an aggregation accumulator, it returns an aggregation of the first n elements within a group. + * As an array operator, it returns the specified number of elements from the beginning of an array. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#mongodb-expression-exp.-first + */ + $firstN: { + input: Expression + n: Expression, + }; + } + + export interface In { + /** + * Returns a boolean indicating whether a specified value is in an array. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/in/#mongodb-expression-exp.-in + */ + $in: [Expression, ArrayExpression]; + } + + export interface IndexOfArray { + /** + * Searches an array for an occurrence of a specified value and returns the array index of the first occurrence. If the substring is not found, returns -1. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfArray/#mongodb-expression-exp.-indexOfArray + */ + $indexOfArray: [ArrayExpression, Expression] | [ArrayExpression, Expression, NumberExpression] | [ArrayExpression, Expression, NumberExpression, NumberExpression]; + } + + export interface IsArray { + /** + * Determines if the operand is an array. Returns a boolean. + * + * @version 3.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isArray/#mongodb-expression-exp.-isArray + */ + $isArray: [Expression]; + } + + export interface Last { + /** + * Returns the last array element. Distinct from $last accumulator. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/#mongodb-expression-exp.-last + */ + $last: Expression; + } + + export interface LastN { + /** + * $lastN can be used as an aggregation accumulator or array operator. + * As an aggregation accumulator, it an aggregation of the last n elements within a group. + * As an array operator, it returns the specified number of elements from the end of an array. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#mongodb-group-grp.-lastN + */ + $lastN: { + input: Expression + n: Expression, + }; + } + + export interface LinearFill { + /** + * Fills null and missing fields in a window using linear interpolation based on surrounding field values. + * + * @version 5.3 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill + */ + $linearFill: Expression + } + + export interface Locf { + /** + * Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf + */ + $locf: Expression + } + + export interface Map { + /** + * Applies a subexpression to each element of an array and returns the array of resulting values in order. Accepts named parameters. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/#mongodb-expression-exp.-map + */ + $map: { + /** + * An expression that resolves to an array. + */ + input: ArrayExpression; + /** + * A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + */ + as?: string; + /** + * An expression that is applied to each element of the input array. The expression references each element individually with the variable name specified in as. + */ + in: Expression; + }; + } + + export interface ObjectToArray { + /** + * Converts a document to an array of documents representing key-value pairs. + * + * @version 3.4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/objectToArray/#mongodb-expression-exp.-objectToArray + */ + $objectToArray: ObjectExpression; + } + + export interface Range { + /** + * Outputs an array containing a sequence of integers according to user-defined inputs. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/range/#mongodb-expression-exp.-range + */ + $range: [NumberExpression, NumberExpression] | [NumberExpression, NumberExpression, NumberExpression]; + } + + export interface Reduce { + /** + * Applies an expression to each element in an array and combines them into a single value. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#mongodb-expression-exp.-reduce + */ + $reduce: { + /** + * Can be any valid expression that resolves to an array. For more information on expressions, see Expressions. + * + * If the argument resolves to a value of null or refers to a missing field, $reduce returns null. + * + * If the argument does not resolve to an array or null nor refers to a missing field, $reduce returns an error. + */ + input: ArrayExpression; + /** + * The initial cumulative value set before in is applied to the first element of the input array. + */ + initialValue: Expression; + /** + * A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + * + * During evaluation of the in expression, two variables will be available: + * - `value` is the variable that represents the cumulative value of the expression. + * - `this` is the variable that refers to the element being processed. + */ + in: Expression; + }; + } + + export interface ReverseArray { + /** + * Returns an array with the elements in reverse order. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/reverseArray/#mongodb-expression-exp.-reverseArray + */ + $reverseArray: ArrayExpression; + } + + export interface Size { + /** + * Returns the number of elements in the array. Accepts a single expression as argument. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/size/#mongodb-expression-exp.-size + */ + $size: ArrayExpression; + } + + export interface Slice { + /** + * Returns a subset of an array. + * + * @version 3.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/#mongodb-expression-exp.-slice + */ + $slice: [ArrayExpression, NumberExpression] | [ArrayExpression, NumberExpression, NumberExpression]; + } + + export interface Zip { + /** + * Merge two arrays together. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/zip/#mongodb-expression-exp.-zip + */ + $zip: { + /** + * An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + * + * If any of the inputs arrays resolves to a value of null or refers to a missing field, $zip returns null. + * + * If any of the inputs arrays does not resolve to an array or null nor refers to a missing field, $zip returns an error. + */ + inputs: ArrayExpression[]; + /** + * A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. + * + * The default value is false: the shortest array length determines the number of arrays in the output array. + */ + useLongestLength?: boolean; + /** + * An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + * + * If useLongestLength: true but defaults is empty or not specified, $zip uses null as the default value. + * + * If specifying a non-empty defaults, you must specify a default for each input array or else $zip will return an error. + */ + defaults?: ArrayExpression; + }; + } + + export interface Concat { + /** + * Concatenates any number of strings. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/concat/#mongodb-expression-exp.-concat + */ + $concat: StringExpression[]; + } + + export interface IndexOfBytes { + /** + * Searches a string for an occurrence of a substring and returns the UTF-8 byte index of the first occurrence. If the substring is not found, returns -1. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfBytes/#mongodb-expression-exp.-indexOfBytes + */ + $indexOfBytes: [StringExpression, StringExpression] | [StringExpression, StringExpression, NumberExpression] | [StringExpression, StringExpression, NumberExpression, NumberExpression]; + } + + export interface IndexOfCP { + /** + * Searches a string for an occurrence of a substring and returns the UTF-8 code point index of the first occurrence. If the substring is not found, returns -1 + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfCP/#mongodb-expression-exp.-indexOfCP + */ + $indexOfCP: [StringExpression, StringExpression] | [StringExpression, StringExpression, NumberExpression] | [StringExpression, StringExpression, NumberExpression, NumberExpression]; + } + + export interface Ltrim { + /** + * Removes whitespace or the specified characters from the beginning of a string. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ltrim/#mongodb-expression-exp.-ltrim + */ + $ltrim: { + /** + * The string to trim. The argument can be any valid expression that resolves to a string. For more information on expressions, see Expressions. + */ + input: StringExpression; + /** + * The character(s) to trim from the beginning of the input. + * + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * + * If unspecified, $ltrim removes whitespace characters, including the null character. For the list of whitespace characters, see Whitespace Characters. + */ + chars?: StringExpression; + }; + } + + export interface RegexFind { + /** + * Applies a regular expression (regex) to a string and returns information on the first matched substring. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFind/#mongodb-expression-exp.-regexFind + */ + $regexFind: { + /** + * The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + */ + input: Expression; // TODO: Resolving to string, which ones? + /** + * The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options): + * - "pattern" + * - /pattern/ + * - /pattern/options + * + * Alternatively, you can also specify the regex options with the options field. To specify the s or x options, you must use the options field. + * + * You cannot specify options in both the regex and the options field. + */ + regex: RegExp | string; + /** + * The following are available for use with regular expression. + * + * Note: You cannot specify options in both the regex and the options field. + * + * Option Description + * + * `i` Case insensitivity to match both upper and lower cases. You can specify the option in the options field or as part of the regex field. + * + * `m` For patterns that include anchors (i.e. ^ for the start, $ for the end), match at the beginning or end of each line for strings with multiline values. Without this option, these anchors match at beginning or end of the string. + * If the pattern contains no anchors or if the string value has no newline characters (e.g. \n), the m option has no effect. + * + * `x` "Extended" capability to ignore all white space characters in the pattern unless escaped or included in a character class. + * Additionally, it ignores characters in-between and including an un-escaped hash/pound (#) character and the next new line, so that you may include comments in complicated patterns. This only applies to data characters; white space characters may never appear within special character sequences in a pattern. + * The x option does not affect the handling of the VT character (i.e. code 11). + * You can specify the option only in the options field. + * + * `s` Allows the dot character (i.e. .) to match all characters including newline characters. + * You can specify the option only in the options field. + */ + options?: RegexOptions; + }; + } + + export interface RegexFindAll { + /** + * Applies a regular expression (regex) to a string and returns information on the all matched substrings. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/#mongodb-expression-exp.-regexFindAll + */ + $regexFindAll: { + /** + * The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + */ + input: Expression; // TODO: Resolving to string, which ones? + /** + * The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options): + * - "pattern" + * - /pattern/ + * - /pattern/options + * + * Alternatively, you can also specify the regex options with the options field. To specify the s or x options, you must use the options field. + * + * You cannot specify options in both the regex and the options field. + */ + regex: RegExp | string; + /** + * The following are available for use with regular expression. + * + * Note: You cannot specify options in both the regex and the options field. + * + * Option Description + * + * `i` Case insensitivity to match both upper and lower cases. You can specify the option in the options field or as part of the regex field. + * + * `m` For patterns that include anchors (i.e. ^ for the start, $ for the end), match at the beginning or end of each line for strings with multiline values. Without this option, these anchors match at beginning or end of the string. + * If the pattern contains no anchors or if the string value has no newline characters (e.g. \n), the m option has no effect. + * + * `x` "Extended" capability to ignore all white space characters in the pattern unless escaped or included in a character class. + * Additionally, it ignores characters in-between and including an un-escaped hash/pound (#) character and the next new line, so that you may include comments in complicated patterns. This only applies to data characters; white space characters may never appear within special character sequences in a pattern. + * The x option does not affect the handling of the VT character (i.e. code 11). + * You can specify the option only in the options field. + * + * `s` Allows the dot character (i.e. .) to match all characters including newline characters. + * You can specify the option only in the options field. + */ + options?: RegexOptions; + }; + } + + export interface RegexMatch { + /** + * Applies a regular expression (regex) to a string and returns a boolean that indicates if a match is found or not. + * + * @version 4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/#mongodb-expression-exp.-regexMatch + */ + $regexMatch: { + /** + * The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + */ + input: Expression; // TODO: Resolving to string, which ones? + /** + * The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options): + * - "pattern" + * - /pattern/ + * - /pattern/options + * + * Alternatively, you can also specify the regex options with the options field. To specify the s or x options, you must use the options field. + * + * You cannot specify options in both the regex and the options field. + */ + regex: RegExp | string; + /** + * The following are available for use with regular expression. + * + * Note: You cannot specify options in both the regex and the options field. + * + * Option Description + * + * `i` Case insensitivity to match both upper and lower cases. You can specify the option in the options field or as part of the regex field. + * + * `m` For patterns that include anchors (i.e. ^ for the start, $ for the end), match at the beginning or end of each line for strings with multiline values. Without this option, these anchors match at beginning or end of the string. + * If the pattern contains no anchors or if the string value has no newline characters (e.g. \n), the m option has no effect. + * + * `x` "Extended" capability to ignore all white space characters in the pattern unless escaped or included in a character class. + * Additionally, it ignores characters in-between and including an un-escaped hash/pound (#) character and the next new line, so that you may include comments in complicated patterns. This only applies to data characters; white space characters may never appear within special character sequences in a pattern. + * The x option does not affect the handling of the VT character (i.e. code 11). + * You can specify the option only in the options field. + * + * `s` Allows the dot character (i.e. .) to match all characters including newline characters. + * You can specify the option only in the options field. + */ + options?: RegexOptions; + }; + } + + export interface ReplaceOne { + /** + * Replaces the first instance of a matched string in a given input. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceOne/#mongodb-expression-exp.-replaceOne + */ + $replaceOne: { + /** + * The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceOne returns null. + */ + input: StringExpression; + /** + * The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceOne returns null. + */ + find: StringExpression; + /** + * The string to use to replace the first matched instance of find in input. Can be any valid expression that resolves to a string or a null. + */ + replacement: StringExpression; + }; + } + + export interface ReplaceAll { + /** + * Replaces all instances of a matched string in a given input. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceAll/#mongodb-expression-exp.-replaceAll + */ + $replaceAll: { + /** + * The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + */ + input: StringExpression; + /** + * The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + */ + find: StringExpression; + /** + * The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. + */ + replacement: StringExpression; + }; + } + + export interface Rtrim { + /** + * Removes whitespace or the specified characters from the end of a string. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rtrim/#mongodb-expression-exp.-rtrim + */ + $rtrim: { + /** + * The string to trim. The argument can be any valid expression that resolves to a string. For more information on expressions, see Expressions. + */ + input: StringExpression; + /** + * The character(s) to trim from the beginning of the input. + * + * The argument can be any valid expression that resolves to a string. The $rtrim operator breaks down the string into individual UTF code point to trim from input. + * + * If unspecified, $rtrim removes whitespace characters, including the null character. For the list of whitespace characters, see Whitespace Characters. + */ + chars?: StringExpression; + }; + } + + export interface Split { + /** + * Splits a string into substrings based on a delimiter. Returns an array of substrings. If the delimiter is not found within the string, returns an array containing the original string. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/split/#mongodb-expression-exp.-split + */ + $split: [StringExpression, StringExpression]; + } + + export interface StrLenBytes { + /** + * Returns the number of UTF-8 encoded bytes in a string. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenBytes/#mongodb-expression-exp.-strLenBytes + */ + $strLenBytes: StringExpression; + } + + export interface StrLenCP { + /** + * Returns the number of UTF-8 code points in a string. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenCP/#mongodb-expression-exp.-strLenCP + */ + $strLenCP: StringExpression; + } + + export interface Strcasecmp { + /** + * Performs case-insensitive string comparison and returns: 0 if two strings are equivalent, 1 if the first string is greater than the second, and -1 if the first string is less than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strcasecmp/#mongodb-expression-exp.-strcasecmp + */ + $strcasecmp: [StringExpression, StringExpression]; + } + + export interface Substr { + /** + * Deprecated. Use $substrBytes or $substrCP. + * + * @deprecated 3.4 + * @alias {Expression.SubstrBytes} + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substr/#mongodb-expression-exp.-substr + */ + $substr: [StringExpression, number, number]; + } + + export interface SubstrBytes { + /** + * Returns the substring of a string. Starts with the character at the specified UTF-8 byte index (zero-based) in the string and continues for the specified number of bytes. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrBytes/#mongodb-expression-exp.-substrBytes + */ + $substrBytes: [StringExpression, number, number]; + } + + export interface SubstrCP { + /** + * Returns the substring of a string. Starts with the character at the specified UTF-8 code point (CP) index (zero-based) in the string and continues for the number of code points specified. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrCP/#mongodb-expression-exp.-substrCP + */ + $substrCP: [StringExpression, number, number]; + } + + export interface ToLower { + /** + * Converts a string to lowercase. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLower/#mongodb-expression-exp.-toLower + */ + $toLower: StringExpression; + } + + export interface ToString { + /** + * Converts value to a string. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toString/#mongodb-expression-exp.-toString + */ + $toString: Expression; + } + + export interface Trim { + /** + * Removes whitespace or the specified characters from the beginning and end of a string. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/trim/#mongodb-expression-exp.-trim + */ + $trim: { + /** + * The string to trim. The argument can be any valid expression that resolves to a string. For more information on expressions, see Expressions. + */ + input: StringExpression; + /** + * The character(s) to trim from the beginning of the input. + * + * The argument can be any valid expression that resolves to a string. The $trim operator breaks down the string into individual UTF code point to trim from input. + * + * If unspecified, $trim removes whitespace characters, including the null character. For the list of whitespace characters, see Whitespace Characters. + */ + chars?: StringExpression; + }; + } + + export interface ToUpper { + /** + * Converts a string to uppercase. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toUpper/#mongodb-expression-exp.-toUpper + */ + $toUpper: StringExpression; + } + + export interface Literal { + + /** + * Returns a value without parsing. Use for values that the aggregation pipeline may interpret as an + * expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/literal/#mongodb-expression-exp.-literal + */ + $literal: any; + } + + export interface GetField { + + /** + * Returns the value of a specified field from a document. If you don't specify an object, $getField returns + * the value of the field from $$CURRENT. + * + * @version 4.4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/#mongodb-expression-exp.-getField + */ + $getField: { + /** + * Field in the input object for which you want to return a value. field can be any valid expression that + * resolves to a string constant. + */ + field: StringExpression; + /** + * A valid expression that contains the field for which you want to return a value. input must resolve to an + * object, missing, null, or undefined. If omitted, defaults to the document currently being processed in the + * pipeline ($$CURRENT). + */ + input?: ObjectExpression | SpecialPathVariables | NullExpression; + } + } + + export interface Rand { + + /** + * Returns a random float between 0 and 1 each time it is called. + * + * @version 4.4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rand/#mongodb-expression-exp.-rand + */ + $rand: Record; + } + + export interface SampleRate { + + /** + * Matches a random selection of input documents. The number of documents selected approximates the sample + * rate expressed as a percentage of the total number of documents. + * + * @version 4.4.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sampleRate/#mongodb-expression-exp.-sampleRate + */ + $sampleRate: number; + } + + export interface MergeObjects { + + /** + * Combines multiple documents into a single document. + * + * @version 3.6 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/#mongodb-expression-exp.-mergeObjects + */ + $mergeObjects: ObjectExpression | ObjectExpression[] | ArrayExpression | Record; + } + + export interface SetField { + + /** + * Adds, updates, or removes a specified field in a document. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#mongodb-expression-exp.-setField + */ + $setField: { + /** + * Field in the input object that you want to add, update, or remove. field can be any valid expression that + * resolves to a string constant. + */ + field: StringExpression; + /** + * Document that contains the field that you want to add or update. input must resolve to an object, missing, + * null, or undefined + */ + input?: ObjectExpression | NullExpression; + /** + * The value that you want to assign to field. value can be any valid expression. + */ + value?: Expression | SpecialPathVariables; + } + } + + export interface UnsetField { + + /** + * Removes a specified field in a document. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/#mongodb-expression-exp.-unsetField + */ + $unsetField: { + /** + * Field in the input object that you want to add, update, or remove. field can be any valid expression that + * resolves to a string constant. + */ + field: StringExpression; + /** + * Document that contains the field that you want to add or update. input must resolve to an object, missing, + * null, or undefined. + */ + input?: ObjectExpression | SpecialPathVariables | NullExpression; + } + } + + export interface Let { + + /** + * Binds variables for use in the specified expression, and returns the result of the expression. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/#mongodb-expression-exp.-let + */ + $let: { + /** + * Assignment block for the variables accessible in the in expression. To assign a variable, specify a + * string for the variable name and assign a valid expression for the value. + */ + vars: { [key: string]: Expression; }; + /** + * The expression to evaluate. + */ + in: Expression; + } + } + + export interface AllElementsTrue { + /** + * Evaluates an array as a set and returns true if no element in the array is false. Otherwise, returns false. An + * empty array returns true. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/allElementsTrue/#mongodb-expression-exp.-allElementsTrue + */ + $allElementsTrue: ArrayExpression; + } + + export interface AnyElementsTrue { + /** + * Evaluates an array as a set and returns true if any of the elements are true and false otherwise. An empty + * array returns false. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/anyElementsTrue/#mongodb-expression-exp.-anyElementsTrue + */ + $anyElementTrue: ArrayExpression; + } + + export interface SetDifference { + /** + * Takes two sets and returns an array containing the elements that only exist in the first set; i.e. performs a + * relative complement of the second set relative to the first. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setDifference/#mongodb-expression-exp.-setDifference + */ + $setDifference: [ArrayExpression, ArrayExpression]; + } + + export interface SetEquals { + /** + * Compares two or more arrays and returns true if they have the same distinct elements and false otherwise. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setEquals/#mongodb-expression-exp.-setEquals + */ + $setEquals: ArrayExpression[]; + } + + export interface SetIntersection { + /** + * Takes two or more arrays and returns an array that contains the elements that appear in every input array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIntersection/#mongodb-expression-exp.-setIntersection + */ + $setIntersection: ArrayExpression[]; + } + + export interface SetIsSubset { + /** + * Takes two arrays and returns true when the first array is a subset of the second, including when the first + * array equals the second array, and false otherwise. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIsSubset/#mongodb-expression-exp.-setIsSubset + */ + $setIsSubset: [ArrayExpression, ArrayExpression]; + } + + export interface SetUnion { + /** + * Takes two or more arrays and returns an array containing the elements that appear in any input array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/#mongodb-expression-exp.-setUnion + */ + $setUnion: ArrayExpression[]; + } + + export interface Accumulator { + /** + * Defines a custom accumulator operator. Accumulators are operators that maintain their state (e.g. totals, + * maximums, minimums, and related data) as documents progress through the pipeline. Use the $accumulator operator + * to execute your own JavaScript functions to implement behavior not supported by the MongoDB Query Language. See + * also $function. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/#mongodb-expression-exp.-accumulator + */ + $accumulator: { + /** + * Function used to initialize the state. The init function receives its arguments from the initArgs array + * expression. You can specify the function definition as either BSON type Code or String. + */ + init: CodeExpression; + /** + * Arguments passed to the init function. + */ + initArgs?: ArrayExpression; + /** + * Function used to accumulate documents. The accumulate function receives its arguments from the current state + * and accumulateArgs array expression. The result of the accumulate function becomes the new state. You can + * specify the function definition as either BSON type Code or String. + */ + accumulate: CodeExpression; + /** + * Arguments passed to the accumulate function. You can use accumulateArgs to specify what field value(s) to + * pass to the accumulate function. + */ + accumulateArgs: ArrayExpression; + /** + * Function used to merge two internal states. merge must be either a String or Code BSON type. merge returns + * the combined result of the two merged states. For information on when the merge function is called, see Merge + * Two States with $merge. + */ + merge: CodeExpression; + /** + * Function used to update the result of the accumulation. + */ + finalize?: CodeExpression; + /** + * The language used in the $accumulator code. + */ + lang: 'js'; + } + } + + export interface AddToSet { + /** + * Returns an array of all unique values that results from applying an expression to each document in a group. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/#mongodb-expression-exp.-addToSet + */ + $addToSet: Expression | Record; + } + + export interface Avg { + /** + * Returns the average value of the numeric values. $avg ignores non-numeric values. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#mongodb-expression-exp.-avg + */ + $avg: Expression; + } + + export interface Bottom { + /** + * Returns the bottom element within a group according to the specified sort order. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottom/#mongodb-group-grp.-bottom + */ + $bottom: { + sortBy: AnyObject, + output: Expression + }; + } + + export interface BottomN { + /** + * Returns an aggregation of the bottom n elements within a group, according to the specified sort order. + * If the group contains fewer than n elements, $bottomN returns all elements in the group. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottomN/#mongodb-group-grp.-bottomN + */ + $bottomN: { + n: Expression, + sortBy: AnyObject, + output: Expression + }; + } + + export interface Count { + /** + * Returns the number of documents in a group. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/count/#mongodb-expression-exp.-count + */ + $count: Record | Path; + } + + export interface CovariancePop { + /** + * Returns the population covariance of two numeric expressions that are evaluated using documents in the + * $setWindowFields stage window. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/covariancePop/#mongodb-expression-exp.-covariancePop + */ + $covariancePop: [NumberExpression, NumberExpression]; + } + + export interface CovarianceSamp { + /** + * Returns the sample covariance of two numeric expressions that are evaluated using documents in the + * $setWindowFields stage window. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/covarianceSamp/#mongodb-expression-exp.-covarianceSamp + */ + $covarianceSamp: [NumberExpression, NumberExpression]; + } + + export interface DenseRank { + /** + * Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage + * partition. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#mongodb-expression-exp.-denseRank + */ + $denseRank: Record; + } + + export interface Derivative { + /** + * Returns the average rate of change within the specified window, which is calculated using the: + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/derivative/#mongodb-expression-exp.-derivative + */ + $derivative: { + /** + * Specifies the expression to evaluate. The expression must evaluate to a number. + */ + input: NumberExpression; + /** + * A string that specifies the time unit. + */ + unit?: DateUnit; + } + } + + export interface DocumentNumber { + /** + * Returns the position of a document (known as the document number) in the $setWindowFields stage partition. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/documentNumber/#mongodb-expression-exp.-documentNumber + */ + $documentNumber: Record; + } + + export interface ExpMovingAvg { + /** + * Returns the exponential moving average of numeric expressions applied to documents in a partition defined in + * the $setWindowFields stage. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#mongodb-expression-exp.-expMovingAvg + */ + $expMovingAvg: { + /** + * Specifies the expression to evaluate. Non-numeric expressions are ignored. + */ + input: Expression; + + /** + * An integer that specifies the number of historical documents that have a significant mathematical weight in + * the exponential moving average calculation, with the most recent documents contributing the most weight. + * + * You must specify either N or alpha. You cannot specify both. + */ + N: NumberExpression; + + /** + * A double that specifies the exponential decay value to use in the exponential moving average calculation. A + * higher alpha value assigns a lower mathematical significance to previous results from the calculation. + * + * You must specify either N or alpha. You cannot specify both. + */ + alpha?: never; + } | + { + /** + * Specifies the expression to evaluate. Non-numeric expressions are ignored. + */ + input: Expression; + + /** + * An integer that specifies the number of historical documents that have a significant mathematical weight in + * the exponential moving average calculation, with the most recent documents contributing the most weight. + * + * You must specify either N or alpha. You cannot specify both. + */ + N?: never; + + /** + * A double that specifies the exponential decay value to use in the exponential moving average calculation. A + * higher alpha value assigns a lower mathematical significance to previous results from the calculation. + * + * You must specify either N or alpha. You cannot specify both. + */ + alpha: NumberExpression; + } + } + + export interface Integral { + /** + * Returns the approximation of the area under a curve, which is calculated using the trapezoidal rule where each + * set of adjacent documents form a trapezoid using the: + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/#mongodb-expression-exp.-integral + */ + $integral: { + /** + * Specifies the expression to evaluate. You must provide an expression that returns a number. + */ + input: NumberExpression; + + /** + * A string that specifies the time unit. + */ + unit?: DateUnit; + } + } + + export interface Max { + /** + * Returns the maximum value. $max compares both value and type, using the specified BSON comparison order for + * values of different types. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/#mongodb-expression-exp.-max + */ + $max: Expression | Expression[]; + } + + export interface MaxN { + /** + * Returns an aggregation of the maxmimum value n elements within a group. + * If the group contains fewer than n elements, $maxN returns all elements in the group. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN/#mongodb-group-grp.-maxN + */ + $maxN: { + input: Expression + n: Expression, + }; + } + + export interface Min { + /** + * Returns the minimum value. $min compares both value and type, using the specified BSON comparison order for + * values of different types. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/#mongodb-expression-exp.-min + */ + $min: Expression | Expression[]; + } + + export interface MinN { + /** + * Returns an aggregation of the minimum value n elements within a group. + * If the group contains fewer than n elements, $minN returns all elements in the group. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#mongodb-group-grp.-minN + */ + $minN: { + input: Expression + n: Expression, + }; + } + + export interface Push { + /** + * Returns an array of all values that result from applying an expression to documents. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/push/#mongodb-expression-exp.-push + */ + $push: Expression | Record; + } + + export interface Rank { + /** + * Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage + * partition. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#mongodb-expression-exp.-rank + */ + $rank: Record; + } + + export interface Shift { + /** + * Returns the value from an expression applied to a document in a specified position relative to the current + * document in the $setWindowFields stage partition. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#mongodb-expression-exp.-shift + */ + $shift: { + /** + * Specifies an expression to evaluate and return in the output. + */ + output: Expression; + /** + * Specifies an integer with a numeric document position relative to the current document in the output. + */ + by: number; + /** + * Specifies an optional default expression to evaluate if the document position is outside of the implicit + * $setWindowFields stage window. The implicit window contains all the documents in the partition. + */ + default?: Expression; + } + } + + export interface Median { + /** + * Returns an approximation of the median, the 50th percentile, as a scalar value. + * + * @see https://www.mongodb.com/docs/v7.0/reference/operator/aggregation/median/ + */ + $median: { + input: number | Expression, + method: 'approximate' + } + } + + export interface StdDevPop { + /** + * Calculates the population standard deviation of the input values. Use if the values encompass the entire + * population of data you want to represent and do not wish to generalize about a larger population. $stdDevPop + * ignores non-numeric values. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/#mongodb-expression-exp.-stdDevPop + */ + $stdDevPop: Expression; + } + + export interface StdDevSamp { + /** + * Calculates the sample standard deviation of the input values. Use if the values encompass a sample of a + * population of data from which to generalize about the population. $stdDevSamp ignores non-numeric values. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/#mongodb-expression-exp.-stdDevSamp + */ + $stdDevSamp: Expression; + } + + export interface Sum { + /** + * Calculates and returns the collective sum of numeric values. $sum ignores non-numeric values. + * + * @version 5.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/#mongodb-expression-exp.-sum + */ + $sum: number | Expression | Expression[]; + } + + export interface Convert { + /** + * Checks if the specified expression resolves to one of the following numeric + * - Integer + * - Decimal + * - Double + * - Long + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/convert/#mongodb-expression-exp.-convert + */ + $convert: { + input: Expression; + to: K; + onError?: Expression; + onNull?: Expression; + }; + } + + export interface IsNumber { + /** + * Checks if the specified expression resolves to one of the following numeric + * - Integer + * - Decimal + * - Double + * - Long + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isNumber/#mongodb-expression-exp.-isNumber + */ + $isNumber: Expression; + } + + export interface ToBool { + /** + * Converts a value to a boolean. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toBool/#mongodb-expression-exp.-toBool + */ + $toBool: Expression; + } + + export interface ToDecimal { + /** + * Converts a value to a decimal. If the value cannot be converted to a decimal, $toDecimal errors. If the value + * is null or missing, $toDecimal returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDecimal/#mongodb-expression-exp.-toDecimal + */ + $toDecimal: Expression; + } + + export interface ToDouble { + /** + * Converts a value to a double. If the value cannot be converted to an double, $toDouble errors. If the value is + * null or missing, $toDouble returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDouble/#mongodb-expression-exp.-toDouble + */ + $toDouble: Expression; + } + + export interface ToInt { + /** + * Converts a value to a long. If the value cannot be converted to a long, $toLong errors. If the value is null or + * missing, $toLong returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toInt/#mongodb-expression-exp.-toInt + */ + $toInt: Expression; + } + + export interface ToLong { + /** + * Converts a value to a long. If the value cannot be converted to a long, $toLong errors. If the value is null or + * missing, $toLong returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLong/#mongodb-expression-exp.-toLong + */ + $toLong: Expression; + } + + export interface ToObjectId { + /** + * Converts a value to an ObjectId(). If the value cannot be converted to an ObjectId, $toObjectId errors. If the + * value is null or missing, $toObjectId returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toObjectId/#mongodb-expression-exp.-toObjectId + */ + $toObjectId: Expression; + } + + export interface Top { + $top: { + sortBy: AnyObject, + output: Expression + }; + } + + export interface TopN { + $topN: { + n: Expression, + sortBy: AnyObject, + output: Expression + }; + } + + export interface ToString { + /** + * Converts a value to a string. If the value cannot be converted to a string, $toString errors. If the value is + * null or missing, $toString returns null. + * + * @version 4.0 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toString/#mongodb-expression-exp.-toString + */ + $toString: Expression; + } + + export interface Type { + /** + * Returns a string that specifies the BSON type of the argument. + * + * @version 3.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/type/#mongodb-expression-exp.-type + */ + $type: Expression; + } + + export interface BinarySize { + /** + * Returns the size of a given string or binary data value's content in bytes. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/binarySize/#mongodb-expression-exp.-binarySize + */ + $binarySize: NullExpression | StringExpression | BinaryExpression; + } + + export interface BsonSize { + /** + * Returns the size in bytes of a given document (i.e. bsontype Object) when encoded as BSON. You can use + * $bsonSize as an alternative to the Object.bsonSize() method. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/bsonSize/#mongodb-expression-exp.-bsonSize + */ + $bsonSize: NullExpression | ObjectExpression; + } + + export interface Function { + /** + * Defines a custom aggregation function or expression in JavaScript. + * + * @version 4.4 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/function/#mongodb-expression-exp.-function + */ + $function: { + /** + * The function definition. You can specify the function definition as either BSON type Code or String. + */ + body: CodeExpression; + /** + * Arguments passed to the function body. If the body function does not take an argument, you can specify an + * empty array [ ] + */ + args: ArrayExpression; + /** + * The language used in the body. You must specify lang: "js". + */ + lang: 'js' + }; + } + } + + type Path = string; + + + export type Expression = + Path | + ArithmeticExpressionOperator | + ArrayExpressionOperator | + BooleanExpressionOperator | + ComparisonExpressionOperator | + ConditionalExpressionOperator | + CustomAggregationExpressionOperator | + DataSizeOperator | + DateExpressionOperator | + LiteralExpressionOperator | + MiscellaneousExpressionOperator | + ObjectExpressionOperator | + SetExpressionOperator | + StringExpressionOperator | + TextExpressionOperator | + TrigonometryExpressionOperator | + TypeExpressionOperator | + AccumulatorOperator | + VariableExpressionOperator | + WindowOperator | + Expression.Top | + Expression.TopN | + any; + + export type NullExpression = null; + + export type CodeExpression = + string | + Function; + + export type BinaryExpression = + Path; + + export type FunctionExpression = + Expression.Function; + + export type AnyExpression = + ArrayExpression | + BooleanExpression | + NumberExpression | + ObjectExpression | + StringExpression | + DateExpression | + BinaryExpression | + FunctionExpression | + ObjectIdExpression | + ConditionalExpressionOperator | + any; + + export type ObjectIdExpression = + TypeExpressionOperatorReturningObjectId; + + export type ArrayExpression = + T[] | + Path | + ArrayExpressionOperatorReturningAny | + ArrayExpressionOperatorReturningArray | + StringExpressionOperatorReturningArray | + ObjectExpressionOperatorReturningArray | + SetExpressionOperatorReturningArray | + LiteralExpressionOperatorReturningAny | + WindowOperatorReturningArray | + CustomAggregationExpressionOperatorReturningAny | + WindowOperatorReturningAny; + + export type BooleanExpression = + boolean | + Path | + BooleanExpressionOperator | + ArrayExpressionOperatorReturningAny | + ComparisonExpressionOperatorReturningBoolean | + StringExpressionOperatorReturningBoolean | + SetExpressionOperatorReturningBoolean | + LiteralExpressionOperatorReturningAny | + CustomAggregationExpressionOperatorReturningAny | + TypeExpressionOperatorReturningBoolean; + + export type NumberExpression = + number | + Path | + ArrayExpressionOperatorReturningAny | + ArrayExpressionOperatorReturningNumber | + ArithmeticExpressionOperator | + ComparisonExpressionOperatorReturningNumber | + TrigonometryExpressionOperator | + MiscellaneousExpressionOperatorReturningNumber | + StringExpressionOperatorReturningNumber | + LiteralExpressionOperatorReturningAny | + ObjectExpressionOperator | + SetExpressionOperator | + WindowOperatorReturningNumber | + WindowOperatorReturningAny | + DataSizeOperatorReturningNumber | + CustomAggregationExpressionOperatorReturningAny | + TypeExpressionOperatorReturningNumber | + DateExpression | + DateExpressionOperatorReturningNumber; + + export type ObjectExpression = + Path | + ArrayExpressionOperatorReturningAny | + DateExpressionOperatorReturningObject | + StringExpressionOperatorReturningObject | + ObjectExpressionOperatorReturningObject | + CustomAggregationExpressionOperatorReturningAny | + LiteralExpressionOperatorReturningAny; + + export type StringExpression = + Path | + ArrayExpressionOperatorReturningAny | + DateExpressionOperatorReturningString | + StringExpressionOperatorReturningString | + LiteralExpressionReturningAny | + CustomAggregationExpressionOperatorReturningAny | + TypeExpressionOperatorReturningString | + T; + + export type DateExpression = + Path | + NativeDate | + DateExpressionOperatorReturningDate | + TypeExpressionOperatorReturningDate | + LiteralExpressionReturningAny; + + export type ArithmeticExpressionOperator = + Expression.Abs | + Expression.Add | + Expression.Ceil | + Expression.Divide | + Expression.Exp | + Expression.Floor | + Expression.Ln | + Expression.Log | + Expression.Log10 | + Expression.Mod | + Expression.Multiply | + Expression.Pow | + Expression.Round | + Expression.Sqrt | + Expression.Subtract | + Expression.Trunc; + + export type ArrayExpressionOperator = + ArrayExpressionOperatorReturningAny | + ArrayExpressionOperatorReturningBoolean | + ArrayExpressionOperatorReturningNumber | + ArrayExpressionOperatorReturningObject; + + export type LiteralExpressionOperator = + Expression.Literal; + + export type LiteralExpressionReturningAny = + LiteralExpressionOperatorReturningAny; + + export type LiteralExpressionOperatorReturningAny = + Expression.Literal; + + export type MiscellaneousExpressionOperator = + Expression.Rand | + Expression.SampleRate; + + export type MiscellaneousExpressionOperatorReturningNumber = + Expression.Rand; + + export type ArrayExpressionOperatorReturningAny = + Expression.ArrayElemAt | + Expression.First | + Expression.Last | + Expression.Reduce; + + export type ArrayExpressionOperatorReturningArray = + Expression.ConcatArrays | + Expression.Filter | + Expression.FirstN | + Expression.LastN | + Expression.Map | + Expression.ObjectToArray | + Expression.Range | + Expression.ReverseArray | + Expression.Slice | + Expression.Zip; + + export type ArrayExpressionOperatorReturningNumber = + Expression.IndexOfArray | + Expression.Size; + + export type ArrayExpressionOperatorReturningObject = + Expression.ArrayToObject; + + export type ArrayExpressionOperatorReturningBoolean = + Expression.In | + Expression.IsArray; + + export type BooleanExpressionOperator = + Expression.And | + Expression.Or | + Expression.Not; + + export type ComparisonExpressionOperator = + ComparisonExpressionOperatorReturningBoolean | + ComparisonExpressionOperatorReturningNumber; + + export type ComparisonExpressionOperatorReturningBoolean = + Expression.Eq | + Expression.Gt | + Expression.Gte | + Expression.Lt | + Expression.Lte | + Expression.Ne; + + export type ComparisonExpressionOperatorReturningNumber = + Expression.Cmp; + + export type ConditionalExpressionOperator = + Expression.Cond | + Expression.IfNull | + Expression.Switch; + + export type StringExpressionOperator = + StringExpressionOperatorReturningArray | + StringExpressionOperatorReturningBoolean | + StringExpressionOperatorReturningNumber | + StringExpressionOperatorReturningObject | + StringExpressionOperatorReturningString; + + export type StringExpressionOperatorReturningArray = + Expression.RegexFindAll | + Expression.Split; + + export type StringExpressionOperatorReturningBoolean = + Expression.RegexMatch; + + export type StringExpressionOperatorReturningNumber = + Expression.IndexOfBytes | + Expression.IndexOfCP | + Expression.Strcasecmp | + Expression.StrLenBytes | + Expression.StrLenCP; + + export type StringExpressionOperatorReturningObject = + Expression.RegexFind; + + export type StringExpressionOperatorReturningString = + Expression.Concat | + Expression.Ltrim | + Expression.Ltrim | + Expression.ReplaceOne | + Expression.ReplaceAll | + Expression.Substr | + Expression.SubstrBytes | + Expression.SubstrCP | + Expression.ToLower | + Expression.ToString | + Expression.ToUpper | + Expression.Trim; + + export type ObjectExpressionOperator = + Expression.MergeObjects | + Expression.ObjectToArray | + Expression.SetField | + Expression.UnsetField; + + export type ObjectExpressionOperatorReturningArray = + Expression.ObjectToArray; + + export type ObjectExpressionOperatorReturningObject = + Expression.MergeObjects | + Expression.SetField | + Expression.UnsetField; + + export type VariableExpressionOperator = + Expression.Let; + + export type VariableExpressionOperatorReturningAny = + Expression.Let; + + export type SetExpressionOperator = + Expression.AllElementsTrue | + Expression.AnyElementsTrue | + Expression.SetDifference | + Expression.SetEquals | + Expression.SetIntersection | + Expression.SetIsSubset | + Expression.SetUnion; + + export type SetExpressionOperatorReturningBoolean = + Expression.AllElementsTrue | + Expression.AnyElementsTrue | + Expression.SetEquals | + Expression.SetIsSubset; + + export type SetExpressionOperatorReturningArray = + Expression.SetDifference | + Expression.SetIntersection | + Expression.SetUnion; + + /** + * Trigonometry expressions perform trigonometric operations on numbers. + * Values that represent angles are always input or output in radians. + * Use $degreesToRadians and $radiansToDegrees to convert between degree + * and radian measurements. + */ + export type TrigonometryExpressionOperator = + Expression.Sin | + Expression.Cos | + Expression.Tan | + Expression.Asin | + Expression.Acos | + Expression.Atan | + Expression.Atan2 | + Expression.Asinh | + Expression.Acosh | + Expression.Atanh | + Expression.Sinh | + Expression.Cosh | + Expression.Tanh | + Expression.DegreesToRadians | + Expression.RadiansToDegrees; + + export type TextExpressionOperator = + Expression.Meta; + + export type WindowOperator = + Expression.AddToSet | + Expression.Avg | + Expression.Count | + Expression.CovariancePop | + Expression.CovarianceSamp | + Expression.DenseRank | + Expression.Derivative | + Expression.DocumentNumber | + Expression.ExpMovingAvg | + Expression.First | + Expression.FirstN | + Expression.Integral | + Expression.Last | + Expression.LastN | + Expression.LinearFill | + Expression.Locf | + Expression.Max | + Expression.MaxN | + Expression.Median | + Expression.Min | + Expression.MinN | + Expression.Push | + Expression.Rank | + Expression.Shift | + Expression.StdDevPop | + Expression.StdDevSamp | + Expression.Sum; + + export type WindowOperatorReturningAny = + Expression.First | + Expression.Last | + Expression.Shift; + + export type WindowOperatorReturningArray = + Expression.AddToSet | + Expression.FirstN | + Expression.LastN | + Expression.MaxN | + Expression.MinN | + Expression.Push; + + export type WindowOperatorReturningNumber = + Expression.Avg | + Expression.Count | + Expression.CovariancePop | + Expression.CovarianceSamp | + Expression.DenseRank | + Expression.DocumentNumber | + Expression.ExpMovingAvg | + Expression.Integral | + Expression.Max | + Expression.Median | + Expression.Min | + Expression.StdDevPop | + Expression.StdDevSamp | + Expression.Sum; + + export type TypeExpressionOperator = + Expression.Convert | + Expression.IsNumber | + Expression.ToBool | + Expression.ToDate | + Expression.ToDecimal | + Expression.ToDouble | + Expression.ToInt | + Expression.ToLong | + Expression.ToObjectId | + Expression.ToString | + Expression.Type; + + export type TypeExpressionOperatorReturningNumber = + Expression.Convert<'double' | 1 | 'int' | 16 | 'long' | 18 | 'decimal' | 19> | + Expression.ToDecimal | + Expression.ToDouble | + Expression.ToInt | + Expression.ToLong; + + export type TypeExpressionOperatorReturningBoolean = + Expression.Convert<'bool' | 8> | + Expression.IsNumber | + Expression.ToBool; + + + export type TypeExpressionOperatorReturningString = + Expression.Convert<'string' | 2> | + Expression.ToString | + Expression.Type; + + export type TypeExpressionOperatorReturningObjectId = + Expression.Convert<'objectId' | 7> | + Expression.ToObjectId; + + export type TypeExpressionOperatorReturningDate = + Expression.Convert<'date' | 9> | + Expression.ToDate; + + export type DataSizeOperator = + Expression.BinarySize | + Expression.BsonSize; + + export type DataSizeOperatorReturningNumber = + Expression.BinarySize | + Expression.BsonSize; + + export type CustomAggregationExpressionOperator = + Expression.Accumulator | + Expression.Function; + + export type CustomAggregationExpressionOperatorReturningAny = + Expression.Function; + + export type AccumulatorOperator = + Expression.Accumulator | + Expression.AddToSet | + Expression.Avg | + Expression.Bottom | + Expression.BottomN | + Expression.Count | + Expression.First | + Expression.FirstN | + Expression.Last | + Expression.LastN | + Expression.Max | + Expression.MaxN | + Expression.Median | + Expression.MergeObjects | + Expression.Min | + Expression.MinN | + Expression.Push | + Expression.StdDevPop | + Expression.StdDevSamp | + Expression.Sum | + Expression.Top | + Expression.TopN; + + export type tzExpression = UTCOffset | StringExpressionOperatorReturningBoolean | string; + + type hh = '-00' | '-01' | '-02' | '-03' | '-04' | '-05' | '-06' | '-07' | '-08' | '-09' | '-10' | '-11' | '-12' | + '+00' | '+01' | '+02' | '+03' | '+04' | '+05' | '+06' | '+07' | '+08' | '+09' | '+10' | '+11' | '+12' | '+13' | '+14'; + type mm = '00' | '30' | '45'; + + type UTCOffset = `${hh}` | `${hh}${mm}` | `${hh}:${mm}`; + + type RegexOptions = + 'i' | 'm' | 's' | 'x' | + 'is' | 'im' | 'ix' | 'si' | 'sm' | 'sx' | 'mi' | 'ms' | 'mx' | 'xi' | 'xs' | 'xm' | + 'ism' | 'isx' | 'ims' | 'imx' | 'ixs' | 'ixm' | 'sim' | 'six' | 'smi' | 'smx' | 'sxi' | 'sxm' | 'mis' | 'mix' | 'msi' | 'msx' | 'mxi' | 'mxs' | 'xis' | 'xim' | 'xsi' | 'xsm' | 'xmi' | 'xms' | + 'ismx' | 'isxm' | 'imsx' | 'imxs' | 'ixsm' | 'ixms' | 'simx' | 'sixm' | 'smix' | 'smxi' | 'sxim' | 'sxmi' | 'misx' | 'mixs' | 'msix' | 'msxi' | 'mxis' | 'mxsi' | 'xism' | 'xims' | 'xsim' | 'xsmi' | 'xmis' | 'xmsi'; + + type StartOfWeek = + 'monday' | 'mon' | + 'tuesday' | 'tue' | + 'wednesday' | 'wed' | + 'thursday' | 'thu' | + 'friday' | 'fri' | + 'saturday' | 'sat' | + 'sunday' | 'sun'; + + type DateUnit = 'year' | 'quarter' | 'week' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'; + + type FormatString = string; + + export type DateExpressionOperator = + DateExpressionOperatorReturningDate | + DateExpressionOperatorReturningNumber | + DateExpressionOperatorReturningString | + DateExpressionOperatorReturningObject; + + export type DateExpressionOperatorReturningObject = + Expression.DateToParts; + + export type DateExpressionOperatorReturningNumber = + Expression.DateDiff | + Expression.DayOfMonth | + Expression.DayOfWeek | + Expression.DayOfYear | + Expression.IsoDayOfWeek | + Expression.IsoWeek | + Expression.IsoWeekYear | + Expression.Millisecond | + Expression.Second | + Expression.Minute | + Expression.Hour | + Expression.Month | + Expression.Year; + + export type DateExpressionOperatorReturningDate = + Expression.DateAdd | + Expression.DateFromParts | + Expression.DateFromString | + Expression.DateSubtract | + Expression.DateTrunc | + Expression.ToDate; + + export type DateExpressionOperatorReturningString = + Expression.DateToString; + +} diff --git a/backend/node_modules/mongoose/types/helpers.d.ts b/backend/node_modules/mongoose/types/helpers.d.ts new file mode 100644 index 0000000..91e2ea2 --- /dev/null +++ b/backend/node_modules/mongoose/types/helpers.d.ts @@ -0,0 +1,32 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + /** + * Mongoose uses this function to get the current time when setting + * [timestamps](/docs/guide.html#timestamps). You may stub out this function + * using a tool like [Sinon](https://www.npmjs.com/package/sinon) for testing. + */ + function now(): NativeDate; + + /** + * Tells `sanitizeFilter()` to skip the given object when filtering out potential query selector injection attacks. + * Use this method when you have a known query selector that you want to use. + */ + function trusted(obj: T): T; + + /** + * Returns true if the given value is a Mongoose ObjectId (using `instanceof`) or if the + * given value is a 24 character hex string, which is the most commonly used string representation + * of an ObjectId. + */ + function isObjectIdOrHexString(v: mongodb.ObjectId): true; + function isObjectIdOrHexString(v: mongodb.ObjectId | string): boolean; + function isObjectIdOrHexString(v: any): false; + + /** + * Returns true if Mongoose can cast the given value to an ObjectId, or + * false otherwise. + */ + function isValidObjectId(v: mongodb.ObjectId | Types.ObjectId): true; + function isValidObjectId(v: any): boolean; +} diff --git a/backend/node_modules/mongoose/types/index.d.ts b/backend/node_modules/mongoose/types/index.d.ts new file mode 100644 index 0000000..6dd1b5c --- /dev/null +++ b/backend/node_modules/mongoose/types/index.d.ts @@ -0,0 +1,1146 @@ +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// +/// + +declare class NativeDate extends globalThis.Date { } + +declare module 'mongoose' { + import Kareem = require('kareem'); + import events = require('events'); + import mongodb = require('mongodb'); + import mongoose = require('mongoose'); + + export type Mongoose = typeof mongoose; + + /** + * Mongoose constructor. The exports object of the `mongoose` module is an instance of this + * class. Most apps will only use this one instance. + */ + export const Mongoose: new (options?: MongooseOptions | null) => Mongoose; + + export let Promise: any; + + /** + * Can be extended to explicitly type specific models. + */ + export interface Models { + [modelName: string]: Model + } + + /** An array containing all models associated with this Mongoose instance. */ + export const models: Models; + + /** + * Removes the model named `name` from the default connection, if it exists. + * You can use this function to clean up any models you created in your tests to + * prevent OverwriteModelErrors. + */ + export function deleteModel(name: string | RegExp): Mongoose; + + /** + * Sanitizes query filters against query selector injection attacks by wrapping + * any nested objects that have a property whose name starts with `$` in a `$eq`. + */ + export function sanitizeFilter(filter: QueryFilter): QueryFilter; + + /** Gets mongoose options */ + export function get(key: K): MongooseOptions[K]; + + export function omitUndefined>(val: T): T; + + export type HydratedDocFromModel> = ReturnType; + + /* ! ignore */ + export type CompileModelOptions = { + overwriteModels?: boolean, + connection?: Connection + }; + + export function model( + name: string, + schema?: TSchema, + collection?: string, + options?: CompileModelOptions + ): Model< + InferSchemaType, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + // If first schema generic param is set, that means we have an explicit raw doc type, + // so user should also specify a hydrated doc type if the auto inferred one isn't correct. + IsItRecordAndNotAny> extends true + ? ObtainSchemaGeneric + : HydratedDocument< + InferSchemaType, + ObtainSchemaGeneric & ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + InferSchemaType, + ObtainSchemaGeneric + >, + TSchema, + ObtainSchemaGeneric + > & ObtainSchemaGeneric; + + export function model(name: string, schema?: Schema | Schema, collection?: string, options?: CompileModelOptions): Model; + + export function model( + name: string, + schema?: Schema, + collection?: string, + options?: CompileModelOptions + ): U; + + /** Returns an array of model names created on this instance of Mongoose. */ + export function modelNames(): Array; + + /** + * Overwrites the current driver used by this Mongoose instance. A driver is a + * Mongoose-specific interface that defines functions like `find()`. + */ + export function setDriver(driver: any): Mongoose; + + /** The node-mongodb-native driver Mongoose uses. */ + export { mongodb as mongo }; + + /** Declares a global plugin executed on all Schemas. */ + export function plugin(fn: (schema: Schema, opts?: any) => void, opts?: any): Mongoose; + + /** Getter/setter around function for pluralizing collection names. */ + export function pluralize(fn?: ((str: string) => string) | null): ((str: string) => string) | null; + + /** Sets mongoose options */ + export function set(key: K, value: MongooseOptions[K]): Mongoose; + export function set(options: { [K in keyof MongooseOptions]: MongooseOptions[K] }): Mongoose; + + /** The Mongoose version */ + export const version: string; + + export type AnyKeys = { [P in keyof T]?: T[P] | any }; + export interface AnyObject { + [k: string]: any + } + + export type Require_id = T extends { _id?: infer U } + ? IfAny> + : T & { _id: Types.ObjectId }; + + export type Default__v = TSchemaOptions extends { versionKey: false } + ? T + : TSchemaOptions extends { versionKey: infer VK } + ? ( + // If VK is a *literal* string, add that property + T & { + [K in VK as K extends string + ? (string extends K ? never : K) // drop if wide string + : never + ]: number + } + ) + : T extends { __v?: infer U } + ? T + : T & { __v: number }; + + /** Helper type for getting the hydrated document type from the raw document type. The hydrated document type is what `new MyModel()` returns. */ + export type HydratedDocument< + HydratedDocPathsType, + TOverrides = {}, + TQueryHelpers = {}, + TVirtuals = {}, + RawDocType = HydratedDocPathsType, + TSchemaOptions = DefaultSchemaOptions + > = IfAny< + HydratedDocPathsType, + any, + TOverrides extends Record ? + Document & Default__v, TSchemaOptions> & AddDefaultId : + IfAny< + TOverrides, + Document & Default__v, TSchemaOptions>, + Document & MergeType< + Default__v, TSchemaOptions>, + TOverrides + > + > + >; + + export type HydratedSingleSubdocument< + DocType, + TOverrides = {} + > = IfAny< + DocType, + any, + TOverrides extends Record ? + Types.Subdocument, DocType> & Require_id : + IfAny< + TOverrides, + Types.Subdocument, DocType> & Require_id, + Types.Subdocument, DocType> & MergeType< + Require_id, + TOverrides + > + > + >; + export type HydratedArraySubdocument = IfAny< + DocType, + any, + TOverrides extends Record ? + Types.ArraySubdocument, DocType> & Require_id : + IfAny< + TOverrides, + Types.ArraySubdocument, DocType> & Require_id, + Types.ArraySubdocument, DocType> & MergeType< + Require_id, + TOverrides + > + > + >; + + export type HydratedDocumentFromSchema = HydratedDocument< + InferSchemaType, + ObtainSchemaGeneric & ObtainSchemaGeneric, + ObtainSchemaGeneric, + ObtainSchemaGeneric, + InferSchemaType, + ObtainSchemaGeneric + >; + + export interface TagSet { + [k: string]: string; + } + + export interface ToObjectOptions> { + /** if `options.virtuals = true`, you can set `options.aliases = false` to skip applying aliases. This option is a no-op if `options.virtuals = false`. */ + aliases?: boolean; + /** if true, replace any conventionally populated paths with the original id in the output. Has no affect on virtual populated paths. */ + depopulate?: boolean; + /** if true, convert Maps to POJOs. Useful if you want to `JSON.stringify()` the result of `toObject()`. */ + flattenMaps?: boolean; + /** if true, convert any ObjectIds in the result to 24 character hex strings. */ + flattenObjectIds?: boolean; + /** if true, convert any UUIDs in the result to 36 character hex strings. */ + flattenUUIDs?: boolean; + /** apply all getters (path and virtual getters) */ + getters?: boolean; + /** remove empty objects (defaults to true) */ + minimize?: boolean; + /** If true, the resulting object will only have fields that are defined in the document's schema. By default, `toJSON()` & `toObject()` returns all fields in the underlying document from MongoDB, including ones that are not listed in the schema. */ + schemaFieldsOnly?: boolean; + /** if set, mongoose will call this function to allow you to transform the returned object */ + transform?: boolean | (( + doc: THydratedDocumentType, + ret: Default__v>, + options: ToObjectOptions + ) => any); + /** If true, omits fields that are excluded in this document's projection. Unless you specified a projection, this will omit any field that has `select: false` in the schema. */ + useProjection?: boolean; + /** if false, exclude the version key (`__v` by default) from the output */ + versionKey?: boolean; + /** apply virtual getters (can override getters option) */ + virtuals?: boolean | string[]; + } + + export type DiscriminatorModel = T extends Model + ? + M extends Model + ? Model & T, MQueryHelpers | TQueryHelpers, MInstanceMethods | TInstanceMethods, MVirtuals | TVirtuals> + : M + : M; + + export type DiscriminatorSchema = + DisSchema extends Schema + ? Schema, DiscriminatorModel, DisSchemaInstanceMethods | TInstanceMethods, DisSchemaQueryhelpers | TQueryHelpers, DisSchemaVirtuals | TVirtuals, DisSchemaStatics & TStaticMethods> + : Schema; + + type QueryResultType = T extends Query ? ResultType : never; + + type PluginFunction< + DocType, + M, + TInstanceMethods, + TQueryHelpers, + TVirtuals, + TStaticMethods> = (schema: Schema, opts?: any) => void; + + export class Schema< + RawDocType = any, + TModelType = Model, + TInstanceMethods = {}, + TQueryHelpers = {}, + TVirtuals = {}, + TStaticMethods = {}, + TSchemaOptions = DefaultSchemaOptions, + DocType extends ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + > = ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + >, + THydratedDocumentType = HydratedDocument< + DocType, + AddDefaultId & TInstanceMethods, + TQueryHelpers, + AddDefaultId, + IsItRecordAndNotAny extends true ? RawDocType : DocType, + ResolveSchemaOptions + >, + TSchemaDefinition = IfAny, RawDocType, THydratedDocumentType>>, + LeanResultType = IsItRecordAndNotAny extends true ? RawDocType : Default__v>>> + > + extends events.EventEmitter { + /** + * Create a new schema + */ + constructor( + definition?: SchemaDefinition, RawDocType, THydratedDocumentType> | DocType, + options?: SchemaOptions< + FlatRecord, + TInstanceMethods, + TQueryHelpers, + TStaticMethods, + TVirtuals, + THydratedDocumentType, + IfEquals< + TModelType, + Model, + Model, + TModelType + > + > | ResolveSchemaOptions + ); + + /* Creates a new schema with the given definition and options. Equivalent to `new Schema(definition, options)`, but with better automatic type inference. */ + static create< + TSchemaDefinition extends SchemaDefinition, + TSchemaOptions extends DefaultSchemaOptions, + RawDocType extends ApplySchemaOptions< + InferRawDocType>, + ResolveSchemaOptions + >, + THydratedDocumentType extends AnyObject = HydratedDocument< + InferHydratedDocType>, + TSchemaOptions extends { methods: infer M } ? M : {}, + TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {}, + ResolveVirtuals, + RawDocType, + ResolveSchemaOptions + > + >(def: TSchemaDefinition): Schema< + RawDocType, + Model, + TSchemaOptions extends { methods: infer M } ? M : {}, + TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {}, + ResolveVirtuals, + TSchemaOptions extends { statics: any } ? TSchemaOptions['statics'] : {}, + TSchemaOptions, + ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + >, + THydratedDocumentType, + TSchemaDefinition, + ApplySchemaOptions< + InferRawDocType, { bufferToBinary: true }>, + ResolveSchemaOptions + > + >; + + static create< + TSchemaDefinition extends SchemaDefinition, + TSchemaOptions extends SchemaOptions>, + RawDocType extends ApplySchemaOptions< + InferRawDocType>, + ResolveSchemaOptions + >, + TMethods = TSchemaOptions extends { methods: infer M } ? { [K in keyof M]: OmitThisParameter } : {}, + TStatics = TSchemaOptions extends { statics: infer S } ? { [K in keyof S]: OmitThisParameter } : {} + >(def: TSchemaDefinition, options: TSchemaOptions & { + statics?: SchemaOptionsStaticsPropertyType< + TStatics, + Model + > + }): Schema< + RawDocType, + Model, + TMethods, + TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {}, + ResolveVirtuals, + TStatics, + TSchemaOptions, + ApplySchemaOptions< + ObtainDocumentType>, + ResolveSchemaOptions + >, + HydratedDocument< + InferHydratedDocType>, + TMethods, + TSchemaOptions extends { query: any } ? TSchemaOptions['query'] : {}, + ResolveVirtuals, + RawDocType, + ResolveSchemaOptions + >, + TSchemaDefinition, + ApplySchemaOptions< + InferRawDocType, { bufferToBinary: true }>, + ResolveSchemaOptions + > + >; + + /** Adds key path / schema type pairs to this schema. */ + add(obj: SchemaDefinition, RawDocType> | Schema, prefix?: string): this; + + /** + * Add an alias for `path`. This means getting or setting the `alias` + * is equivalent to getting or setting the `path`. + */ + alias(path: string, alias: string | string[]): this; + + /** + * Array of child schemas (from document arrays and single nested subdocs) + * and their corresponding compiled models. Each element of the array is + * an object with 2 properties: `schema` and `model`. + */ + childSchemas: { schema: Schema, model: any }[]; + + /** Removes all indexes on this schema */ + clearIndexes(): this; + + /** Returns a copy of this schema */ + clone(): this; + + discriminator(name: string | number, schema: DisSchema, options?: DiscriminatorOptions): this; + + /** Returns a new schema that has the picked `paths` from this schema. */ + pick(paths: string[], options?: SchemaOptions): T; + + /** Object containing discriminators defined on this schema */ + discriminators?: { [name: string]: Schema }; + + /** Iterates the schemas paths similar to Array#forEach. */ + eachPath(fn: (path: string, type: SchemaType) => void): this; + + /** Defines an index (most likely compound) for this schema. */ + index(fields: IndexDefinition, options?: Omit & { unique?: boolean | [true, string] }): this; + + /** + * Define a search index for this schema. + * + * @remarks Search indexes are only supported when used against a 7.0+ Mongo Atlas cluster. + */ + searchIndex(description: SearchIndexDescription): this; + + /** + * Returns a list of indexes that this schema declares, via `schema.index()` + * or by `index: true` in a path's options. + */ + indexes(): Array<[IndexDefinition, IndexOptions]>; + + /** Gets a schema option. */ + get(key: K): SchemaOptions[K]; + + /** + * Loads an ES6 class into a schema. Maps [setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/set) + [getters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get), [static methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static), + * and [instance methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Class_body_and_method_definitions) + * to schema [virtuals](http://mongoosejs.com/docs/guide.html#virtuals), + * [statics](http://mongoosejs.com/docs/guide.html#statics), and + * [methods](http://mongoosejs.com/docs/guide.html#methods). + */ + loadClass(model: Function, onlyVirtuals?: boolean): this; + + /** Adds an instance method to documents constructed from Models compiled from this schema. */ + method(name: string, fn: (this: Context, ...args: any[]) => any, opts?: any): this; + method(obj: Partial): this; + + /** Object of currently defined methods on this schema. */ + methods: AddThisParameter & AnyObject; + + /** The original object passed to the schema constructor */ + obj: SchemaDefinition, RawDocType>; + + /** Returns a new schema that has the `paths` from the original schema, minus the omitted ones. */ + omit(paths: string[], options?: SchemaOptions): T; + + options: SchemaOptions; + + /** Gets/sets schema paths. */ + path>(path: string): ResultType; + path(path: pathGeneric): SchemaType; + path(path: string, constructor: any): this; + + /** Lists all paths and their type in the schema. */ + paths: { + [key: string]: SchemaType; + }; + + /** Returns the pathType of `path` for this schema. */ + pathType(path: string): string; + + /** Registers a plugin for this schema. */ + plugin, POptions extends Parameters[1] = Parameters[1]>(fn: PFunc, opts?: POptions): this; + plugin, any, any, any, any>, POptions extends Parameters[1] = Parameters[1]>(fn: PFunc, opts?: POptions): this; + + /** Defines a post hook for the model. */ + + // PostMiddlewareFunction + // with errorHandler set to true + post>(method: MongooseQueryMiddleware | MongooseQueryMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption): this; + post(method: MongooseDocumentMiddleware | MongooseDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption): this; + post>(method: 'aggregate' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption>): this; + post(method: 'insertMany' | RegExp, options: SchemaPostOptions & { errorHandler: true }, fn: ErrorHandlingMiddlewareWithOption): this; + + // this = never since it never happens + post(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: false }, fn: PostMiddlewareFunction): this; + post(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPostOptions & { document: boolean, query: false }, fn: PostMiddlewareFunction>): this; + post(method: MongooseDistinctDocumentMiddleware | MongooseDistinctDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: true }, fn: PostMiddlewareFunction): this; + // this = Document + post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], fn: PostMiddlewareFunction): this; + post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], options: SchemaPostOptions & SchemaPostOptions, fn: PostMiddlewareFunction): this; + post(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: true, query: false }, fn: PostMiddlewareFunction): this; + post(method: 'init', fn: PostMiddlewareFunction): this; + + // this = Query + post>(method: MongooseRawResultQueryMiddleware|MongooseRawResultQueryMiddleware[], fn: PostMiddlewareFunction | ModifyResult>>): this; + post>(method: MongooseDefaultQueryMiddleware|MongooseDefaultQueryMiddleware[], fn: PostMiddlewareFunction>): this; + post>(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPostOptions, fn: PostMiddlewareFunction>): this; + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: true }, fn: PostMiddlewareFunction>): this; + // this = Union of Document and Query, could be called with any of them + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: true, query: true }, fn: PostMiddlewareFunction>): this; + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, fn: PostMiddlewareFunction>): this; + + // ErrorHandlingMiddlewareFunction + // this = never since it never happens + post(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPostOptions & { document: boolean, query: false }, fn: ErrorHandlingMiddlewareFunction): this; + post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], options: SchemaPostOptions & { document: false, query: boolean }, fn: ErrorHandlingMiddlewareFunction): this; + post(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: false }, fn: ErrorHandlingMiddlewareFunction): this; + // this = Document + post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], fn: ErrorHandlingMiddlewareFunction): this; + post(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction): this; + post(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: true, query: false }, fn: ErrorHandlingMiddlewareFunction): this; + // this = Query + post>(method: MongooseDefaultQueryMiddleware|MongooseDefaultQueryMiddleware[], fn: ErrorHandlingMiddlewareFunction): this; + post>(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction): this; + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: false, query: true }, fn: ErrorHandlingMiddlewareFunction): this; + // this = Union of Document and Query, could be called with any of them + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPostOptions & { document: true, query: true }, fn: ErrorHandlingMiddlewareFunction): this; + post>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, fn: ErrorHandlingMiddlewareFunction): this; + + // method aggregate and insertMany with PostMiddlewareFunction + post>(method: 'aggregate' | RegExp, fn: PostMiddlewareFunction>>): this; + post>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction>>): this; + post(method: 'insertMany' | RegExp, fn: PostMiddlewareFunction): this; + post(method: 'insertMany' | RegExp, options: SchemaPostOptions, fn: PostMiddlewareFunction): this; + + // method aggregate and insertMany with ErrorHandlingMiddlewareFunction + post>(method: 'aggregate' | RegExp, fn: ErrorHandlingMiddlewareFunction>): this; + post>(method: 'aggregate' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction>): this; + post(method: 'bulkWrite' | 'createCollection' | 'insertMany' | RegExp, fn: ErrorHandlingMiddlewareFunction): this; + post(method: 'bulkWrite' | 'createCollection' | 'insertMany' | RegExp, options: SchemaPostOptions, fn: ErrorHandlingMiddlewareFunction): this; + + /** Defines a pre hook for the model. */ + // this = never since it never happens + pre(method: 'save', options: SchemaPreOptions & { document: false, query: boolean }, fn: PreSaveMiddlewareFunction): this; + pre(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPreOptions & { document: false, query: false }, fn: PreMiddlewareFunction): this; + pre(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPreOptions & { document: boolean, query: false }, fn: PreMiddlewareFunction): this; + pre(method: MongooseDistinctDocumentMiddleware | MongooseDistinctDocumentMiddleware[] | RegExp, options: SchemaPreOptions & { document: false, query: boolean }, fn: PreMiddlewareFunction): this; + // this = Union of Document and Query, could be called with any of them + pre>( + method: 'updateOne' | RegExp, + options: SchemaPreOptions & { document: true, query: true }, + fn: PreUpdateOneMiddlewareFunction + ): this; + pre>( + method: 'deleteOne' | RegExp, + options: SchemaPreOptions & { document: true, query: true }, + fn: PreDeleteOneMiddlewareFunction + ): this; + // this = Document + pre(method: 'save', fn: PreSaveMiddlewareFunction): this; + pre(method: 'init', fn: (this: T, doc: U) => void): this; + pre(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], fn: PreMiddlewareFunction): this; + pre(method: MongooseDistinctDocumentMiddleware|MongooseDistinctDocumentMiddleware[], options: SchemaPreOptions, fn: PreMiddlewareFunction): this; + pre( + method: 'updateOne' | RegExp, + options: SchemaPreOptions & { document: true }, + fn: PreUpdateOneMiddlewareFunction + ): this; + pre( + method: 'deleteOne' | RegExp, + options: SchemaPreOptions & { document: true }, + fn: PreDeleteOneMiddlewareFunction + ): this; + pre(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPreOptions & { document: true, query: false }, fn: PreMiddlewareFunction): this; + // this = Query + pre>(method: MongooseDefaultQueryMiddleware|MongooseDefaultQueryMiddleware[], fn: PreMiddlewareFunction): this; + pre>(method: MongooseDistinctQueryMiddleware|MongooseDistinctQueryMiddleware[], options: SchemaPreOptions, fn: PreMiddlewareFunction): this; + pre>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPreOptions & { document: false, query: true }, fn: PreMiddlewareFunction): this; + // this = Union of Document and Query, could be called with any of them + pre>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, options: SchemaPreOptions & { document: true, query: true }, fn: PreMiddlewareFunction): this; + pre>(method: MongooseQueryOrDocumentMiddleware | MongooseQueryOrDocumentMiddleware[] | RegExp, fn: PreMiddlewareFunction): this; + // method aggregate + pre>(method: 'aggregate' | RegExp, fn: PreMiddlewareFunction): this; + /* method insertMany */ + pre( + method: 'insertMany' | RegExp, + fn: ( + this: TModelType, + docs: any | Array, + options?: InsertManyOptions & { lean?: boolean } + ) => void | Promise + ): this; + /* method bulkWrite */ + pre( + method: 'bulkWrite' | RegExp, + fn: ( + this: TModelType, + ops: Array>, + options?: mongodb.BulkWriteOptions & MongooseBulkWriteOptions + ) => void | Promise + ): this; + /* method createCollection */ + pre( + method: 'createCollection' | RegExp, + fn: ( + this: TModelType, + options?: mongodb.CreateCollectionOptions & Pick + ) => void | Promise + ): this; + + /** Object of currently defined query helpers on this schema. */ + query: TQueryHelpers; + + /** Adds a method call to the queue. */ + queue(name: string, args: any[]): this; + + /** Removes the given `path` (or [`paths`]). */ + remove(paths: string | Array): this; + + /** Removes index by name or index spec */ + removeIndex(index: string | AnyObject): this; + + /** Returns an Array of path strings that are required by this schema. */ + requiredPaths(invalidate?: boolean): string[]; + + /** Sets a schema option. */ + set(key: K, value: SchemaOptions[K], _tags?: any): this; + + /** Adds static "class" methods to Models compiled from this schema. */ + static(name: K, fn: TStaticMethods[K]): this; + static(obj: Partial & { [name: string]: (this: TModelType, ...args: any[]) => any }): this; + static(name: string, fn: (this: TModelType, ...args: any[]) => any): this; + + /** Object of currently defined statics on this schema. */ + statics: { [F in keyof TStaticMethods]: TStaticMethods[F] } & + { [name: string]: (this: TModelType, ...args: any[]) => unknown }; + + toJSONSchema(options?: { useBsonType?: boolean }): Record; + + /** Creates a virtual type with the given name. */ + virtual( + name: keyof TVirtuals | string, + options?: VirtualTypeOptions + ): VirtualType; + + /** Object of currently defined virtuals on this schema */ + virtuals: TVirtuals; + + /** Returns the virtual type with the given `name`. */ + virtualpath(name: string): VirtualType | null; + + static ObjectId: typeof Schema.Types.ObjectId; + } + + export type NumberSchemaDefinition = typeof Number | 'number' | 'Number' | typeof Schema.Types.Number | Schema.Types.Number; + export type StringSchemaDefinition = typeof String | 'string' | 'String' | typeof Schema.Types.String | Schema.Types.String; + export type BooleanSchemaDefinition = typeof Boolean | 'boolean' | 'Boolean' | typeof Schema.Types.Boolean | Schema.Types.Boolean; + export type DateSchemaDefinition = DateConstructor | 'date' | 'Date' | typeof Schema.Types.Date | Schema.Types.Date; + export type ObjectIdSchemaDefinition = 'ObjectId' | 'ObjectID' | typeof Schema.Types.ObjectId | Schema.Types.ObjectId | Types.ObjectId | typeof Types.ObjectId; + export type BufferSchemaDefinition = typeof Buffer | 'buffer' | 'Buffer' | typeof Schema.Types.Buffer; + export type Decimal128SchemaDefinition = 'decimal128' | 'Decimal128' | typeof Schema.Types.Decimal128 | Schema.Types.Decimal128 | Types.Decimal128; + export type BigintSchemaDefinition = 'bigint' | 'BigInt' | typeof Schema.Types.BigInt | Schema.Types.BigInt | typeof BigInt | BigInt; + export type UuidSchemaDefinition = 'uuid' | 'UUID' | typeof Schema.Types.UUID | Schema.Types.UUID; + export type MapSchemaDefinition = MapConstructor | 'Map' | typeof Schema.Types.Map; + export type UnionSchemaDefinition = 'Union' | 'union' | typeof Schema.Types.Union | Schema.Types.Union; + export type DoubleSchemaDefinition = 'double' | 'Double' | typeof Schema.Types.Double | Schema.Types.Double; + + export type SchemaDefinitionWithBuiltInClass = T extends number + ? NumberSchemaDefinition + : T extends string + ? StringSchemaDefinition + : T extends boolean + ? BooleanSchemaDefinition + : T extends NativeDate + ? DateSchemaDefinition + : (Function | string); + + export type SchemaDefinitionProperty> = + // ThisType intersection here avoids corrupting ThisType for SchemaTypeOptions (see test gh11828) + | SchemaDefinitionWithBuiltInClass & ThisType + | SchemaTypeOptions + | typeof SchemaType + | Schema + | Schema[] + | SchemaTypeOptions, EnforcedDocType, THydratedDocumentType>[] + | Function[] + | SchemaDefinition + | SchemaDefinition, EnforcedDocType, THydratedDocumentType>[] + | typeof Schema.Types.Mixed + | MixedSchemaTypeOptions; + + export type SchemaDefinition> = T extends undefined + ? { [path: string]: SchemaDefinitionProperty; } + : { [path in keyof T]?: SchemaDefinitionProperty; }; + + export type AnyArray = T[] | ReadonlyArray; + export type ExtractMongooseArray = T extends Types.Array ? AnyArray> : T; + + export interface MixedSchemaTypeOptions extends SchemaTypeOptions { + type: typeof Schema.Types.Mixed; + } + + export type RefType = + | number + | string + | Buffer + | undefined + | Types.ObjectId + | Types.Buffer + | typeof Schema.Types.Number + | typeof Schema.Types.String + | typeof Schema.Types.Buffer + | typeof Schema.Types.ObjectId + | typeof Schema.Types.UUID; + + + export type InferId = mongodb.InferIdType; + + export interface VirtualTypeOptions { + /** If `ref` is not nullish, this becomes a populated virtual. */ + ref?: string | Function; + + /** The local field to populate on if this is a populated virtual. */ + localField?: string | ((this: HydratedDocType, doc: HydratedDocType) => string); + + /** The foreign field to populate on if this is a populated virtual. */ + foreignField?: string | ((this: HydratedDocType, doc: HydratedDocType) => string); + + /** + * By default, a populated virtual is an array. If you set `justOne`, + * the populated virtual will be a single doc or `null`. + */ + justOne?: boolean; + + /** If you set this to `true`, Mongoose will call any custom getters you defined on this virtual. */ + getters?: boolean; + + /** + * If you set this to `true`, `populate()` will set this virtual to the number of populated + * documents, as opposed to the documents themselves, using `Query#countDocuments()`. + */ + count?: boolean; + + /** Add an extra match condition to `populate()`. */ + match?: QueryFilter | ((doc: Record, virtual?: this) => Record | null); + + /** Add a default `limit` to the `populate()` query. */ + limit?: number; + + /** Add a default `skip` to the `populate()` query. */ + skip?: number; + + /** + * For legacy reasons, `limit` with `populate()` may give incorrect results because it only + * executes a single query for every document being populated. If you set `perDocumentLimit`, + * Mongoose will ensure correct `limit` per document by executing a separate query for each + * document to `populate()`. For example, `.find().populate({ path: 'test', perDocumentLimit: 2 })` + * will execute 2 additional queries if `.find()` returns 2 documents. + */ + perDocumentLimit?: number; + + /** Additional options like `limit` and `lean`. */ + options?: QueryOptions & { match?: AnyObject }; + + /** If true and the given `name` is a direct child of an array, apply the virtual to the array rather than the elements. */ + applyToArray?: boolean; + + /** Additional options for plugins */ + [extra: string]: any; + } + + export class VirtualType { + /** Applies getters to `value`. */ + applyGetters(value: any, doc: Document): any; + + /** Applies setters to `value`. */ + applySetters(value: any, doc: Document): any; + + /** Adds a custom getter to this virtual. */ + get(fn: (this: T, value: any, virtualType: VirtualType, doc: T) => any): this; + + /** Adds a custom setter to this virtual. */ + set(fn: (this: T, value: any, virtualType: VirtualType, doc: T) => void): this; + } + + export type ReturnsNewDoc = { new: true } | { returnOriginal: false } | { returnDocument: 'after' }; + + type ArrayOperators = { $slice: number | [number, number]; $elemMatch?: never } | { $elemMatch: Record; $slice?: never }; + /** + * This Type Assigns `Element | undefined` recursively to the `T` type. + * if it is an array it will do this to the element of the array, if it is an object it will do this for the properties of the object. + * `Element` is the truthy or falsy values that are going to be used as the value of the projection.(1 | true or 0 | false) + * For the elements of the array we will use: `Element | `undefined` | `ArrayOperators` + * @example + * type CalculatedType = Projector<{ a: string, b: number, c: { d: string }, d: string[] }, true> + * type CalculatedType = { + a?: true | undefined; + b?: true | undefined; + c?: true | { + d?: true | undefined; + } | undefined; + d?: true | ArrayOperators | undefined; + } + */ + type Projector = T extends Array + ? Projector | ArrayOperators + : T extends TreatAsPrimitives + ? Element + : T extends Record + ? { + [K in keyof T]?: T[K] extends Record ? Projector | Element : Element; + } + : Element; + type _IDType = { _id?: boolean | number }; + export type InclusionProjection = IsItRecordAndNotAny extends true + ? Omit, boolean | number>, '_id'> & _IDType + : AnyObject; + export type ExclusionProjection = IsItRecordAndNotAny extends true + ? Omit, false | 0>, '_id'> & _IDType + : AnyObject; + + export type ProjectionType = (InclusionProjection & AnyObject) + | (ExclusionProjection & AnyObject) + | string; + export type SortValues = SortOrder; + + export type SortOrder = -1 | 1 | 'asc' | 'ascending' | 'desc' | 'descending'; + + type _UpdateQuery = { + /** @see https://www.mongodb.com/docs/manual/reference/operator/update-field/ */ + $currentDate?: AnyKeys & AdditionalProperties; + $inc?: AnyKeys & AdditionalProperties; + $min?: AnyKeys & AdditionalProperties; + $max?: AnyKeys & AdditionalProperties; + $mul?: AnyKeys & AdditionalProperties; + $rename?: Record; + $set?: AnyKeys & AdditionalProperties; + $setOnInsert?: AnyKeys & AdditionalProperties; + $unset?: AnyKeys & AdditionalProperties; + + /** @see https://www.mongodb.com/docs/manual/reference/operator/update-array/ */ + $addToSet?: AnyKeys & AdditionalProperties; + $pop?: AnyKeys & AdditionalProperties; + $pull?: AnyKeys & AdditionalProperties; + $push?: AnyKeys & AdditionalProperties; + $pullAll?: AnyKeys & AdditionalProperties; + + /** @see https://www.mongodb.com/docs/manual/reference/operator/update-bitwise/ */ + $bit?: AnyKeys; + }; + + export type UpdateWithAggregationPipeline = UpdateAggregationStage[]; + export type UpdateAggregationStage = { $addFields: any } | + { $set: any } | + { $project: any } | + { $unset: any } | + { $replaceRoot: any } | + { $replaceWith: any }; + + /** + * Update query command to perform on the document + * @example + * ```js + * { age: 30 } + * ``` + */ + export type UpdateQuery = AnyKeys & _UpdateQuery & AnyObject; + + /** + * A more strict form of UpdateQuery that enforces updating only + * known top-level properties. + * @example + * ```ts + * function updateUser(_id: mongoose.Types.ObjectId, update: UpdateQueryKnownOnly) { + * return User.updateOne({ _id }, update); + * } + * ``` + */ + export type UpdateQueryKnownOnly = _UpdateQuery; + + export type FlattenMaps = { + [K in keyof T]: FlattenProperty; + }; + + export type BufferToBinaryProperty = unknown extends Buffer + ? T + : T extends Buffer + ? mongodb.Binary + : T extends Types.DocumentArray + ? Types.DocumentArray> + : T extends Types.Subdocument + ? HydratedSingleSubdocument> + : BufferToBinary; + + /** + * Converts any Buffer properties into mongodb.Binary instances, which is what `lean()` returns + */ + export type BufferToBinary = unknown extends Buffer + ? T + : T extends Buffer + ? mongodb.Binary + : T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record + ? { + [K in keyof T]: BufferToBinaryProperty + } + : T; + + /** + * Converts any Buffer properties into { type: 'buffer', data: [1, 2, 3] } format for JSON serialization + */ + export type BufferToJSON = unknown extends Buffer + ? T + : T extends Buffer + ? { type: 'buffer', data: number[] } + : T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record ? { + [K in keyof T]: T[K] extends Buffer + ? { type: 'buffer', data: number[] } + : T[K] extends Types.DocumentArray + ? Types.DocumentArray> + : T[K] extends Types.Subdocument + ? HydratedSingleSubdocument + : BufferToBinary; + } : T; + + /** + * Converts any UUID properties into strings for JSON serialization + */ + export type UUIDToString = T extends Types.UUID + ? string + : T extends mongodb.UUID + ? string + : T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record ? { + [K in keyof T]: T[K] extends Types.UUID + ? string + : T[K] extends mongodb.UUID + ? string + : T[K] extends Types.DocumentArray + ? Types.DocumentArray> + : T[K] extends Types.Subdocument + ? HydratedSingleSubdocument> + : UUIDToString; + } : T; + + /** + * Alias for UUIDToString for backwards compatibility. + * @deprecated Use UUIDToString instead. + */ + export type UUIDToJSON = UUIDToString; + + /** + * Converts any ObjectId properties into strings for JSON serialization + */ + export type ObjectIdToString = T extends mongodb.ObjectId + ? string + : T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record ? { + [K in keyof T]: T[K] extends mongodb.ObjectId + ? string + : T[K] extends Types.DocumentArray + ? Types.DocumentArray> + : T[K] extends Types.Subdocument + ? HydratedSingleSubdocument> + : ObjectIdToString; + } : T; + + /** + * Converts any Date properties into strings for JSON serialization + */ + export type DateToString = T extends NativeDate + ? string + : T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record ? { + [K in keyof T]: T[K] extends NativeDate + ? string + : T[K] extends (NativeDate | null | undefined) + ? string | null | undefined + : T[K] extends Types.DocumentArray + ? Types.DocumentArray> + : T[K] extends Types.Subdocument + ? HydratedSingleSubdocument> + : DateToString; + } : T; + + /** + * Converts any Mongoose subdocuments (single nested or doc arrays) into POJO equivalents + */ + export type SubdocsToPOJOs = T extends Document + ? T + : T extends TreatAsPrimitives + ? T + : T extends Record ? { + [K in keyof T]: T[K] extends Types.DocumentArray + ? ItemType[] + : T[K] extends Types.Subdocument + ? SubdocType + : SubdocsToPOJOs; + } : T; + + export type JSONSerialized = SubdocsToPOJOs< + FlattenMaps< + BufferToJSON< + ObjectIdToString< + UUIDToJSON< + DateToString + > + > + > + > + >; + + /** + * Helper types for computing toObject/toJSON return types based on options. + * These compose transforms conditionally to avoid combinatorial explosion of overloads. + */ + type ApplyVirtuals = + O extends { virtuals: true } ? T & TVirtuals : T; + + type GetVersionKeyName = + TSchemaOptions extends { versionKey: infer VK } + ? VK extends string ? VK : '__v' + : '__v'; + + type ApplyVersionKey = + O extends { versionKey: false } ? Omit> : T; + + /** + * Single-pass type that applies all flatten transforms (flattenMaps, flattenObjectIds, flattenUUIDs) + * in one recursive traversal. This avoids TypeScript's "union type too complex" error that occurs + * when nesting separate recursive transform types. + * + * By handling all transforms in one pass, we correctly handle ObjectIds/UUIDs nested inside Maps + * since we recurse into Map values during the same traversal that converts ObjectIds/UUIDs. + */ + type ApplyFlattenTransforms = + // Handle ObjectId first (before TreatAsPrimitives since ObjectId is in TreatAsPrimitives) + T extends mongodb.ObjectId + ? O extends { flattenObjectIds: true } ? string : T + // Handle UUID (both Types.UUID and mongodb.UUID) + : T extends Types.UUID + ? O extends { flattenUUIDs: true } ? string : T + : T extends mongodb.UUID + ? O extends { flattenUUIDs: true } ? string : T + // Handle Map - flatten to Record and recurse into values + : T extends Map + ? O extends { flattenMaps: true } + ? Record> + : T + // Don't recurse into Documents + : T extends Document + ? T + // Don't modify primitives + : T extends TreatAsPrimitives + ? T + // Handle DocumentArray - recurse into items + : T extends Types.DocumentArray + ? Types.DocumentArray> + // Handle Subdocument - recurse into subdoc type + : T extends Types.Subdocument + ? HydratedSingleSubdocument> + // Handle regular objects - recurse into properties + : T extends Record + ? { [K in keyof T]: ApplyFlattenTransforms } + : T; + + /** + * Computes the return type of toObject/toJSON based on the provided options. + * Uses a single-pass transform for flatten operations to correctly handle all combinations. + */ + export type ToObjectReturnType = + ApplyVersionKey< + Default__v< + ApplyFlattenTransforms< + ApplyVirtuals, TVirtuals, O>, + O + >, + TSchemaOptions + >, + O, + TSchemaOptions + >; + + /** + * Separate type is needed for properties of union type (for example, Types.DocumentArray | undefined) to apply conditional check to each member of it + * https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types + */ + type FlattenProperty = T extends Map + ? Record : T extends TreatAsPrimitives + ? T : T extends Document ? T : T extends Types.DocumentArray + ? Types.DocumentArray> : FlattenMaps; + + export type actualPrimitives = string | boolean | number | bigint | symbol | null | undefined; + export type TreatAsPrimitives = actualPrimitives | NativeDate | RegExp | symbol | Error | BigInt | Types.ObjectId | Buffer | Function | mongodb.Binary | mongodb.ClientSession; + + export type SchemaDefinitionType = T extends Document ? Omit> : T; + + /* for ts-mongoose */ + export class mquery { } + + export function overwriteMiddlewareResult(val: any): Kareem.OverwriteMiddlewareResult; + + export function skipMiddlewareFunction(val: any): Kareem.SkipWrappedFunction; + + export function overwriteMiddlewareArguments(val: any): Kareem.OverwriteArguments; + + export default mongoose; +} diff --git a/backend/node_modules/mongoose/types/indexes.d.ts b/backend/node_modules/mongoose/types/indexes.d.ts new file mode 100644 index 0000000..10cbdd5 --- /dev/null +++ b/backend/node_modules/mongoose/types/indexes.d.ts @@ -0,0 +1,97 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + /** + * Makes the indexes in MongoDB match the indexes defined in every model's + * schema. This function will drop any indexes that are not defined in + * the model's schema except the `_id` index, and build any indexes that + * are in your schema but not in MongoDB. + */ + function syncIndexes(options?: SyncIndexesOptions): Promise; + + interface IndexManager { + /* Deletes all indexes that aren't defined in this model's schema. Used by `syncIndexes()`. Returns list of dropped index names. */ + cleanIndexes(options?: { toDrop?: string[], hideIndexes?: boolean }): Promise; + + /** + * Similar to `ensureIndexes()`, except for it uses the [`createIndex`](https://mongodb.github.io/node-mongodb-native/7.0/classes/Collection.html#createIndex) + * function. + */ + createIndexes(options?: mongodb.CreateIndexesOptions): Promise; + + /** + * Does a dry-run of Model.syncIndexes(), meaning that + * the result of this function would be the result of + * Model.syncIndexes(). + */ + diffIndexes(options?: Record): Promise + + /** + * Sends `createIndex` commands to mongo for each index declared in the schema. + * The `createIndex` commands are sent in series. + */ + ensureIndexes(options?: mongodb.CreateIndexesOptions): Promise; + + /** + * Lists the indexes currently defined in MongoDB. This may or may not be + * the same as the indexes defined in your schema depending on whether you + * use the [`autoIndex` option](/docs/guide.html#autoIndex) and if you + * build indexes manually. + */ + listIndexes(): Promise>; + + /** + * Makes the indexes in MongoDB match the indexes defined in this model's + * schema. This function will drop any indexes that are not defined in + * the model's schema except the `_id` index, and build any indexes that + * are in your schema but not in MongoDB. + */ + syncIndexes(options?: SyncIndexesOptions): Promise>; + } + + interface IndexesDiff { + /** Indexes that would be created in mongodb. */ + toCreate: Array + /** Indexes that would be dropped in mongodb. */ + toDrop: Array + } + + type IndexDirection = 1 | -1 | '2d' | '2dsphere' | 'geoHaystack' | 'hashed' | 'text' | 'ascending' | 'asc' | 'descending' | 'desc'; + type IndexDefinition = Record; + + interface SyncIndexesOptions extends mongodb.CreateIndexesOptions { + continueOnError?: boolean; + hideIndexes?: boolean; + } + type ConnectionSyncIndexesResult = Record; + type OneCollectionSyncIndexesResult = Array & mongodb.MongoServerError; + + type IndexOptions = Omit & { + /** + * `expires` utilizes the `ms` module from [vercel](https://github.com/vercel/ms) allowing us to use a friendlier syntax: + * + * @example + * ```js + * const schema = new Schema({ prop1: Date }); + * + * // expire in 24 hours + * schema.index({ prop1: 1 }, { expires: 60*60*24 }) + * + * // expire in 24 hours + * schema.index({ prop1: 1 }, { expires: '24h' }) + * + * // expire in 1.5 hours + * schema.index({ prop1: 1 }, { expires: '1.5h' }) + * + * // expire in 7 days + * schema.index({ prop1: 1 }, { expires: '7d' }) + * ``` + */ + expires?: number | string; + weights?: Record; + + unique?: boolean | [true, string] + }; + + type SearchIndexDescription = mongodb.SearchIndexDescription; +} diff --git a/backend/node_modules/mongoose/types/inferhydrateddoctype.d.ts b/backend/node_modules/mongoose/types/inferhydrateddoctype.d.ts new file mode 100644 index 0000000..512c9d9 --- /dev/null +++ b/backend/node_modules/mongoose/types/inferhydrateddoctype.d.ts @@ -0,0 +1,135 @@ +import { + IsPathRequired, + IsSchemaTypeFromBuiltinClass, + RequiredPaths, + OptionalPaths, + PathEnumOrString +} from './inferschematype'; +import { UUID } from 'mongodb'; + +declare module 'mongoose' { + export type InferHydratedDocTypeFromSchema> = ObtainSchemaGeneric; + + /** + * Given a schema definition, returns the hydrated document type from the schema definition. + */ + export type InferHydratedDocType< + DocDefinition, + TSchemaOptions extends Record = DefaultSchemaOptions + > = Require_id & + OptionalPaths) + ]: IsPathRequired extends true + ? ObtainHydratedDocumentPathType + : ObtainHydratedDocumentPathType | null; + }, TSchemaOptions>>; + + /** + * @summary Obtains schema Path type. + * @description Obtains Path type by separating path type from other options and calling {@link ResolveHydratedPathType} + * @param {PathValueType} PathValueType Document definition path type. + * @param {TypeKey} TypeKey A generic refers to document definition. + */ + type ObtainHydratedDocumentPathType< + PathValueType, + TypeKey extends string = DefaultTypeKey + > = ResolveHydratedPathType< + TypeKey extends keyof PathValueType + ? TypeKey extends keyof PathValueType[TypeKey] + ? PathValueType + : PathValueType[TypeKey] + : PathValueType, + TypeKey extends keyof PathValueType + ? TypeKey extends keyof PathValueType[TypeKey] + ? {} + : Omit + : {}, + TypeKey, + HydratedDocTypeHint, + HydratedDiscriminatorEnumType + >; + + /** + * @summary Allows users to optionally choose their own type for a schema field for stronger typing. + */ + type HydratedDocTypeHint = T extends { __hydratedDocTypeHint: infer U } ? U + : never; + + type ResolveDiscriminatorHydratedPathType = + IsAny extends true ? never + : TDiscriminators extends Record ? + TDiscriminators[keyof TDiscriminators] extends Schema ? + MergeType, InferHydratedDocTypeFromSchema> + : never + : never; + + type HydratedDiscriminatorEnumType = string extends keyof T ? never + : number extends keyof T ? never + : T extends { type: infer BaseType; discriminators: infer TDiscriminators } ? + IsAny extends true ? never + : BaseType extends Schema ? + ResolveDiscriminatorHydratedPathType + : never + : never; + + /** + * Same as inferSchemaType, except: + * + * 1. Replace `Types.DocumentArray` and `Types.Array` with vanilla `Array` + * 2. Replace `ObtainDocumentPathType` with `ObtainHydratedDocumentPathType` + * 3. Replace `ResolvePathType` with `ResolveHydratedPathType` + * + * @summary Resolve path type by returning the corresponding type. + * @param {PathValueType} PathValueType Document definition path type. + * @param {Options} Options Document definition path options except path type. + * @param {TypeKey} TypeKey A generic of literal string type. Refers to the property used for path type definition. + * @returns Type + */ + type ResolveHydratedPathType = {}, TypeKey extends string = DefaultSchemaOptions['typeKey'], TypeHint = never, TDiscriminatorEnumType = never> = + IsNotNever extends true ? TypeHint + : IsNotNever extends true ? TDiscriminatorEnumType + : PathValueType extends Schema ? + THydratedDocumentType : + PathValueType extends AnyArray ? + IfEquals ? + IsItRecordAndNotAny extends true ? + Types.DocumentArray & EmbeddedHydratedDocType> : + Types.DocumentArray, Types.Subdocument['_id'], unknown, InferHydratedDocType> & InferHydratedDocType> : + Item extends Record ? + Item[TypeKey] extends Function | String ? + Types.Array> : + Types.DocumentArray< + InferRawDocType, + Types.Subdocument['_id'], unknown, InferHydratedDocType> & InferHydratedDocType + >: + IsSchemaTypeFromBuiltinClass extends true ? + Types.Array> : + IsItRecordAndNotAny extends true ? + Item extends Record ? + Types.Array> : + Types.DocumentArray< + InferRawDocType, + Types.Subdocument['_id'], unknown, InferHydratedDocType> & InferHydratedDocType + > : + Types.Array> + > + : PathValueType extends StringSchemaDefinition ? PathEnumOrString + : IfEquals extends true ? PathEnumOrString + : PathValueType extends NumberSchemaDefinition ? Options['enum'] extends ReadonlyArray ? Options['enum'][number] : number + : PathValueType extends DateSchemaDefinition ? NativeDate + : PathValueType extends BufferSchemaDefinition ? Buffer + : PathValueType extends BooleanSchemaDefinition ? boolean + : PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId + : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 + : PathValueType extends BigintSchemaDefinition ? bigint + : PathValueType extends UuidSchemaDefinition ? UUID + : PathValueType extends DoubleSchemaDefinition ? Types.Double + : PathValueType extends typeof Schema.Types.Mixed ? any + : PathValueType extends MapSchemaDefinition ? Map> + : IfEquals extends true ? any + : PathValueType extends typeof SchemaType ? PathValueType['prototype'] + : PathValueType extends ArrayConstructor ? Types.Array + : PathValueType extends Record ? InferHydratedDocType + : unknown; +} diff --git a/backend/node_modules/mongoose/types/inferrawdoctype.d.ts b/backend/node_modules/mongoose/types/inferrawdoctype.d.ts new file mode 100644 index 0000000..8f0a7c5 --- /dev/null +++ b/backend/node_modules/mongoose/types/inferrawdoctype.d.ts @@ -0,0 +1,182 @@ +import { + IsSchemaTypeFromBuiltinClass, + PathEnumOrString, + OptionalPaths, + RequiredPaths, + IsPathRequired +} from './inferschematype'; +import { Binary, UUID } from 'mongodb'; + +declare module 'mongoose' { + export type InferRawDocTypeFromSchema> = IsItRecordAndNotAny> extends true + ? ObtainSchemaGeneric + : unknown extends ObtainSchemaGeneric + ? Require_id>>> + : InferRawDocType>; + + export type InferRawDocTypeWithout_id< + SchemaDefinition, + TSchemaOptions extends Record = DefaultSchemaOptions, + TTransformOptions = { bufferToBinary: false } + > = ApplySchemaOptions<{ + [ + K in keyof (RequiredPaths & + OptionalPaths) + ]: IsPathRequired extends true + ? ObtainRawDocumentPathType + : ObtainRawDocumentPathType | null; + }, TSchemaOptions>; + + export type InferRawDocType< + SchemaDefinition, + TSchemaOptions extends Record = DefaultSchemaOptions, + TTransformOptions = { bufferToBinary: false } + > = TSchemaOptions extends { _id: false } + ? InferRawDocTypeWithout_id + : Require_id>; + + /** + * @summary Allows users to optionally choose their own type for a schema field for stronger typing. + * Make sure to check for `any` because `T extends { __rawDocTypeHint: infer U }` will infer `unknown` if T is `any`. + */ + type RawDocTypeHint = IsAny extends true ? never + : T extends { __rawDocTypeHint: infer U } ? U: never; + + type ResolveDiscriminatorRawPathType = + IsAny extends true ? never + : TDiscriminators extends Record ? + TDiscriminators[keyof TDiscriminators] extends Schema ? + MergeType, InferRawDocTypeFromSchema> + : never + : never; + + type RawDiscriminatorEnumType = string extends keyof T ? never + : number extends keyof T ? never + : T extends { type: infer BaseType; discriminators: infer TDiscriminators } ? + IsAny extends true ? never + : BaseType extends Schema ? + ResolveDiscriminatorRawPathType + : never + : never; + + /** + * @summary Obtains schema Path type. + * @description Obtains Path type by separating path type from other options and calling {@link ResolveRawPathType} + * @param {PathValueType} PathValueType Document definition path type. + * @param {TypeKey} TypeKey A generic refers to document definition. + */ + type ObtainRawDocumentPathType< + PathValueType, + TypeKey extends string = DefaultTypeKey, + TTransformOptions = { bufferToBinary: false } + > = ResolveRawPathType< + TypeKey extends keyof PathValueType ? + TypeKey extends keyof PathValueType[TypeKey] ? + PathValueType + : PathValueType[TypeKey] + : PathValueType, + TypeKey extends keyof PathValueType ? + TypeKey extends keyof PathValueType[TypeKey] ? + {} + : Omit + : {}, + TypeKey, + TTransformOptions, + RawDocTypeHint, + RawDiscriminatorEnumType, + TypeKey extends keyof PathValueType ? false : true + >; + + type neverOrAny = ' ~neverOrAny~'; + + /** + * Same as inferSchemaType, except: + * + * 1. Replace `Types.DocumentArray` and `Types.Array` with vanilla `Array` + * 2. Replace `ObtainDocumentPathType` with `ObtainRawDocumentPathType` + * 3. Replace `ResolvePathType` with `ResolveRawPathType` + * + * @summary Resolve path type by returning the corresponding type. + * @param {PathValueType} PathValueType Document definition path type. + * @param {Options} Options Document definition path options except path type. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns Number, "Number" or "number" will be resolved to number type. + */ + type ResolveRawPathType< + PathValueType, + Options extends SchemaTypeOptions = {}, + TypeKey extends string = DefaultSchemaOptions['typeKey'], + TTransformOptions = { bufferToBinary: false }, + TypeHint = never, + TDiscriminatorEnumType = never, + IsNestedPath extends boolean = false + > = + IsNotNever extends true ? TypeHint + : IsNotNever extends true ? TDiscriminatorEnumType + : [PathValueType] extends [neverOrAny] ? PathValueType + : PathValueType extends Schema ? + IsItRecordAndNotAny extends true ? + RawDocType : + unknown extends TSchemaDefinition ? + TSchemaOptions extends { _id: false } ? + FlattenMaps> : + Require_id>> : + InferRawDocType, TTransformOptions> + : PathValueType extends ReadonlyArray ? + IfEquals extends true ? any[] + : Item extends Schema ? + // If Item is a schema, infer its type. + Array extends true ? + RawDocType : + unknown extends TSchemaDefinition ? + TSchemaOptions extends { _id: false } ? + FlattenMaps> : + Require_id>> : + InferRawDocType, TTransformOptions>> + : TypeKey extends keyof Item ? + Item[TypeKey] extends Function | String ? + // If Item has a type key that's a string or a callable, it must be a scalar, + // so we can directly obtain its path type. + ObtainRawDocumentPathType[] + : // If the type key isn't callable, then this is an array of objects, in which case + // we need to call InferRawDocType to correctly infer its type. + Array> + : IsSchemaTypeFromBuiltinClass extends true ? ResolveRawPathType[] + : IsItRecordAndNotAny extends true ? + Item extends Record ? + ObtainRawDocumentPathType[] + : Array> + : ObtainRawDocumentPathType[] + : PathValueType extends StringSchemaDefinition ? PathEnumOrString + : IfEquals extends true ? PathEnumOrString + : PathValueType extends NumberSchemaDefinition ? + Options['enum'] extends ReadonlyArray ? + Options['enum'][number] + : number + : PathValueType extends DateSchemaDefinition ? NativeDate + : PathValueType extends BufferSchemaDefinition ? (TTransformOptions extends { bufferToBinary: true } ? Binary : Buffer) + : PathValueType extends BooleanSchemaDefinition ? boolean + : PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId + : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 + : PathValueType extends BigintSchemaDefinition ? bigint + : PathValueType extends UuidSchemaDefinition ? Types.UUID + : PathValueType extends MapSchemaDefinition ? Record> + : PathValueType extends DoubleSchemaDefinition ? Types.Double + : PathValueType extends UnionSchemaDefinition ? + ResolveRawPathType ? Item : never> + : PathValueType extends ArrayConstructor ? any[] + : PathValueType extends typeof Schema.Types.Mixed ? any + : IfEquals extends true ? any + : IfEquals extends true ? any + : PathValueType extends typeof SchemaType ? PathValueType['prototype'] + : PathValueType extends Record ? + IsNestedPath extends true ? + // Nested path (no type key) - no _id + InferRawDocTypeWithout_id + : Options extends { _id: false } ? + // Subdocument with _id: false + InferRawDocTypeWithout_id + : // Subdocument with _id (default) + InferRawDocType + : unknown; +} diff --git a/backend/node_modules/mongoose/types/inferschematype.d.ts b/backend/node_modules/mongoose/types/inferschematype.d.ts new file mode 100644 index 0000000..ed8e0ba --- /dev/null +++ b/backend/node_modules/mongoose/types/inferschematype.d.ts @@ -0,0 +1,379 @@ +import { + AnyArray, + BooleanSchemaDefinition, + BigintSchemaDefinition, + BufferSchemaDefinition, + DateSchemaDefinition, + Decimal128SchemaDefinition, + DefaultSchemaOptions, + DefaultTypeKey, + DoubleSchemaDefinition, + IfAny, + IfEquals, + InferSchemaType, + IsItRecordAndNotAny, + MapSchemaDefinition, + MergeType, + NumberSchemaDefinition, + ObjectIdSchemaDefinition, + ObtainDocumentType, + Schema, + SchemaType, + SchemaTypeOptions, + StringSchemaDefinition, + Types, + UnionSchemaDefinition, + UuidSchemaDefinition +} from 'mongoose'; + +declare module 'mongoose' { + /** + * @summary Obtains document schema type. + * @description Obtains document schema type from document Definition OR returns enforced schema type if it's provided. + * @param {DocDefinition} DocDefinition A generic equals to the type of document definition "provided in as first parameter in Schema constructor". + * @param {EnforcedDocType} EnforcedDocType A generic type enforced by user "provided before schema constructor". + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + */ + type ObtainDocumentType< + DocDefinition, + EnforcedDocType = any, + TSchemaOptions extends Record = DefaultSchemaOptions + > = + IsItRecordAndNotAny extends true ? EnforcedDocType + : { + [K in keyof (RequiredPaths & + OptionalPaths)]: IsPathRequired< + DocDefinition[K], + TSchemaOptions['typeKey'] + > extends true ? + ObtainDocumentPathType + : ObtainDocumentPathType | null; + }; + + /** + * @summary Obtains document schema type from Schema instance. + * @param {Schema} TSchema `typeof` a schema instance. + * @example + * const userSchema = new Schema({userName:String}); + * type UserType = InferSchemaType; + * // result + * type UserType = {userName?: string} + */ + export type InferSchemaType = IfAny>; + + export type DefaultIdVirtual = { id: string }; + export type AddDefaultId = (DocType extends { id: any } ? TVirtuals : TSchemaOptions extends { id: false } ? TVirtuals : TVirtuals & { id: string }); + + /** + * @summary Obtains schema Generic type by using generic alias. + * @param {TSchema} TSchema A generic of schema type instance. + * @param {alias} alias Targeted generic alias. + */ + type ObtainSchemaGeneric< + TSchema, + alias extends + | 'EnforcedDocType' + | 'M' + | 'TInstanceMethods' + | 'TQueryHelpers' + | 'TVirtuals' + | 'TStaticMethods' + | 'TSchemaOptions' + | 'DocType' + | 'THydratedDocumentType' + | 'TSchemaDefinition' + | 'TLeanResultType' + > = + TSchema extends ( + Schema< + infer EnforcedDocType, + infer M, + infer TInstanceMethods, + infer TQueryHelpers, + infer TVirtuals, + infer TStaticMethods, + infer TSchemaOptions, + infer DocType, + infer THydratedDocumentType, + infer TSchemaDefinition, + infer TLeanResultType + > + ) ? + { + EnforcedDocType: EnforcedDocType; + M: M; + TInstanceMethods: IfEquals; + TQueryHelpers: IfEquals; + TVirtuals: AddDefaultId, TSchemaOptions>; + TStaticMethods: IfEquals; + TSchemaOptions: TSchemaOptions; + DocType: DocType; + THydratedDocumentType: THydratedDocumentType; + TSchemaDefinition: TSchemaDefinition; + TLeanResultType: TLeanResultType; + }[alias] + : unknown; + + type TypesAreEqual = [A] extends [B] ? [B] extends [A] ? true : false : false; + + type ResolveSchemaOptions = + keyof T extends never ? DefaultSchemaOptions : + TypesAreEqual extends true ? DefaultSchemaOptions : + MergeType; + + /*! + * ResolveVirtuals resolves the virtuals option from the schema options. + * Adds the default `id` virtual if it is not disabled in schema options or + * overwritten in the RawDocType. + */ + type ResolveVirtuals = + ResolveSchemaOptions extends { virtuals: infer V } + ? AddDefaultId> + : {}; + + type ApplySchemaOptions = ResolveTimestamps; + + type DefaultTimestampProps = { + createdAt: NativeDate; + updatedAt: NativeDate; + }; + + type ResolveTimestamps = + O extends { timestamps?: false } ? T + : O extends { timestamps: infer TimestampOptions } ? + TimestampOptions extends true ? T & DefaultTimestampProps + : TimestampOptions extends SchemaTimestampsConfig ? + Show< + T & { + [K in keyof TimestampOptions & keyof DefaultTimestampProps as TimestampOptions[K] extends true ? K + : TimestampOptions[K] & string]: NativeDate; + } + > + : T + : T; +} + +type RequiredPropertyDefinition = + | { + required: true | string | [true, string | undefined] | { isRequired: true }; + } + | ArrayConstructor + | any[]; + +/** + * @summary Checks if a document path is required or optional. + * @param {P} P Document path. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + */ +type IsPathRequired = + P extends RequiredPropertyDefinition ? true + : P extends { required: boolean } ? + P extends { required: false } ? + false + : true + : P extends { default: undefined | null | ((...args: any[]) => undefined) | ((...args: any[]) => null) } ? false + : P extends { default: any } ? true + : P extends Record ? true + : false; + +// Internal type used to efficiently check for never or any types +// can be efficiently checked like: +// `[T] extends [neverOrAny] ? T : ...` +// to avoid edge cases +type neverOrAny = ' ~neverOrAny~'; + +/** + * @summary A Utility to obtain schema's required path keys. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns required paths keys of document definition. + */ +type RequiredPathKeys = Exclude>; + +/** + * @summary A Utility to obtain schema's required paths. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns a record contains required paths with the corresponding type. + */ +type RequiredPaths = Pick< + { -readonly [K in keyof T]: T[K] }, + RequiredPathKeys +>; + +/** + * @summary A Utility to obtain schema's optional path keys. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns optional paths keys of document definition. + */ +type OptionalPathKeys = { + [K in keyof T]: IsPathRequired extends true ? never : K; +}[keyof T]; + +/** + * @summary A Utility to obtain schema's optional paths. + * @param {T} T A generic refers to document definition. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns a record contains optional paths with the corresponding type. + */ +type OptionalPaths = Pick< + { -readonly [K in keyof T]?: T[K] }, + OptionalPathKeys +>; + +/** + * @summary Allows users to optionally choose their own type for a schema field for stronger typing. + */ +type TypeHint = T extends { __typehint: infer U } ? U : never; + +/** + * @summary Obtains schema Path type. + * @description Obtains Path type by separating path type from other options and calling {@link ResolvePathType} + * @param {PathValueType} PathValueType Document definition path type. + * @param {TypeKey} TypeKey A generic refers to document definition. + */ +type ObtainDocumentPathType = ResolvePathType< + TypeKey extends keyof PathValueType ? + TypeKey extends keyof PathValueType[TypeKey] ? + PathValueType + : PathValueType[TypeKey] + : PathValueType, + TypeKey extends keyof PathValueType ? + TypeKey extends keyof PathValueType[TypeKey] ? + {} + : Omit + : {}, + TypeKey, + TypeHint, + DiscriminatorEnumType +>; + +/** + * @param {T} T A generic refers to string path enums. + * @returns Path enum values type as literal strings or string. + */ +type PathEnumOrString['enum']> = + T extends ReadonlyArray ? E + : T extends { values: any } ? PathEnumOrString + : T extends Record ? V + : string; + +type UnionToType = T[number] extends infer U + ? ResolvePathType + : never; + +type ResolveDiscriminatorPathType = + IfAny ? + TDiscriminators[keyof TDiscriminators] extends Schema ? + MergeType, InferSchemaType> + : never + : never>; + +type DiscriminatorEnumType = string extends keyof T ? never + : number extends keyof T ? never + : T extends { type: infer BaseType; discriminators: infer TDiscriminators } ? + IfAny + : never> + : never; + +type IsSchemaTypeFromBuiltinClass = + T extends typeof String ? true + : unknown extends Buffer ? false + : T extends typeof Number ? true + : T extends typeof Boolean ? true + : T extends typeof Buffer ? true + : T extends typeof Schema.Types.ObjectId ? true + : T extends typeof Schema.Types.UUID ? true + : T extends typeof Schema.Types.Decimal128 ? true + : T extends typeof Schema.Types.Int32 ? true + : T extends typeof Schema.Types.String ? true + : T extends typeof Schema.Types.Number ? true + : T extends typeof Schema.Types.Date ? true + : T extends typeof Schema.Types.Double ? true + : T extends typeof Schema.Types.Boolean ? true + : T extends Types.ObjectId ? true + : T extends Types.Decimal128 ? true + : T extends NativeDate ? true + : T extends typeof Schema.Types.Mixed ? true + : T extends Types.UUID ? true + : T extends Buffer ? true + : false; + +/** + * @summary Resolve path type by returning the corresponding type. + * @param {PathValueType} PathValueType Document definition path type. + * @param {Options} Options Document definition path options except path type. + * @param {TypeKey} TypeKey A generic of literal string type."Refers to the property used for path type definition". + * @returns Number, "Number" or "number" will be resolved to number type. + */ +type ResolvePathType< + PathValueType, + Options extends SchemaTypeOptions = {}, + TypeKey extends string = DefaultSchemaOptions['typeKey'], + TypeHint = never, + TDiscriminatorEnumType = never +> = [TypeHint] extends [never] + ? [TDiscriminatorEnumType] extends [never] + ? PathValueType extends Schema ? + InferSchemaType + : PathValueType extends AnyArray ? + [Item] extends [never] + ? any[] + : Item extends Schema ? + // If Item is a schema, infer its type. + Types.DocumentArray> + : Item extends Record ? + Item[TypeKey] extends Schema ? + Types.DocumentArray< + [DiscriminatorEnumType] extends [never] ? InferSchemaType : DiscriminatorEnumType + > + : Item[TypeKey] extends Function | String ? + // If Item has a type key that's a string or a callable, it must be a scalar, + // so we can directly obtain its path type. + ObtainDocumentPathType[] + : // If the type key isn't callable, then this is an array of objects, in which case + // we need to call ObtainDocumentType to correctly infer its type. + Types.DocumentArray> + : IsSchemaTypeFromBuiltinClass extends true ? ResolvePathType[] + : IsItRecordAndNotAny extends true ? + Item extends Record ? + ObtainDocumentPathType[] + : Types.DocumentArray> + : ObtainDocumentPathType[] + : PathValueType extends StringSchemaDefinition ? PathEnumOrString + : IfEquals extends true ? PathEnumOrString + : PathValueType extends NumberSchemaDefinition ? + Options['enum'] extends ReadonlyArray ? + Options['enum'][number] + : number + : PathValueType extends DateSchemaDefinition ? NativeDate + : PathValueType extends UuidSchemaDefinition ? Types.UUID + : PathValueType extends BufferSchemaDefinition ? Buffer + : PathValueType extends BooleanSchemaDefinition ? boolean + : PathValueType extends ObjectIdSchemaDefinition ? Types.ObjectId + : PathValueType extends Decimal128SchemaDefinition ? Types.Decimal128 + : PathValueType extends BigintSchemaDefinition ? bigint + : PathValueType extends DoubleSchemaDefinition ? Types.Double + : PathValueType extends MapSchemaDefinition ? Map> + : PathValueType extends UnionSchemaDefinition ? + Options['of'] extends AnyArray ? ResolvePathType + : never + : PathValueType extends ArrayConstructor ? any[] + : PathValueType extends typeof Schema.Types.Mixed ? any + : PathValueType extends ObjectConstructor ? any + : IfEquals extends true ? any + : PathValueType extends typeof SchemaType ? PathValueType['prototype'] + : PathValueType extends Record ? + ObtainDocumentType< + PathValueType, + any, + { + typeKey: TypeKey; + } + > + : unknown + : TDiscriminatorEnumType + : TypeHint; diff --git a/backend/node_modules/mongoose/types/middlewares.d.ts b/backend/node_modules/mongoose/types/middlewares.d.ts new file mode 100644 index 0000000..1e707c2 --- /dev/null +++ b/backend/node_modules/mongoose/types/middlewares.d.ts @@ -0,0 +1,66 @@ +declare module 'mongoose' { + import Kareem = require('kareem'); + + type MongooseQueryAndDocumentMiddleware = 'updateOne' | 'deleteOne'; + + type MongooseDistinctDocumentMiddleware = 'save' | 'validate'; + type MongooseDocumentMiddleware = MongooseDistinctDocumentMiddleware | MongooseQueryAndDocumentMiddleware; + + type MongooseRawResultQueryMiddleware = 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; + type MongooseDistinctQueryMiddleware = 'estimatedDocumentCount' | 'countDocuments' | 'deleteMany' | 'distinct' | 'find' | 'findOne' | 'findOneAndDelete' | 'findOneAndReplace' | 'findOneAndUpdate' | 'replaceOne' | 'updateMany'; + + type MongooseDefaultQueryMiddleware = MongooseDistinctQueryMiddleware | 'updateOne' | 'deleteOne'; + type MongooseQueryMiddleware = MongooseDistinctQueryMiddleware | MongooseQueryAndDocumentMiddleware; + + type MongooseQueryOrDocumentMiddleware = MongooseDistinctQueryMiddleware|MongooseDistinctDocumentMiddleware|MongooseQueryAndDocumentMiddleware; + + type MiddlewareOptions = { + /** + * Enable this Hook for the Document Methods + * @default true + */ + document?: boolean, + /** + * Enable this Hook for the Query Methods + * @default true + */ + query?: boolean, + /** + * Explicitly set this function to be a Error handler instead of based on how many arguments are used + * @default false + */ + errorHandler?: boolean + }; + type SchemaPreOptions = MiddlewareOptions; + type SchemaPostOptions = MiddlewareOptions; + + interface SkipMiddlewareOptions { + /** If `false`, skip pre middleware. */ + pre?: boolean; + /** If `false`, skip post middleware. */ + post?: boolean; + } + + type PreMiddlewareFunction = ( + this: ThisType, + opts?: Record + ) => void | Promise | Kareem.SkipWrappedFunction; + type PreDeleteOneMiddlewareFunction = ( + this: ThisType, + doc: ThisType, + opts?: Record + ) => void | Promise | Kareem.SkipWrappedFunction; + type PreUpdateOneMiddlewareFunction = ( + this: ThisType, + doc: ThisType, + update?: Record, + opts?: Record + ) => void | Promise | Kareem.SkipWrappedFunction; + type PreSaveMiddlewareFunction = ( + this: ThisType, + opts: SaveOptions + ) => void | Promise | Kareem.SkipWrappedFunction; + type PostMiddlewareFunction = (this: ThisType, res: ResType, next: CallbackWithoutResultAndOptionalError) => void | Promise | Kareem.OverwriteMiddlewareResult; + type ErrorHandlingMiddlewareFunction = (this: ThisType, err: NativeError, res: ResType, next: CallbackWithoutResultAndOptionalError) => void; + type ErrorHandlingMiddlewareWithOption = (this: ThisType, err: NativeError, res: ResType | null, next: CallbackWithoutResultAndOptionalError) => void | Promise | Kareem.OverwriteMiddlewareResult; +} diff --git a/backend/node_modules/mongoose/types/models.d.ts b/backend/node_modules/mongoose/types/models.d.ts new file mode 100644 index 0000000..9de93b8 --- /dev/null +++ b/backend/node_modules/mongoose/types/models.d.ts @@ -0,0 +1,1265 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + export interface DiscriminatorOptions { + value?: string | number | ObjectId; + clone?: boolean; + overwriteModels?: boolean; + mergeHooks?: boolean; + mergePlugins?: boolean; + } + + export interface AcceptsDiscriminator { + /** Adds a discriminator type. */ + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model; + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model; + } + + export type MongooseBulkWriteResult = mongodb.BulkWriteResult & { + mongoose?: { + validationErrors: Error[], + results: Array + } + }; + + export interface MongooseBulkWriteOptions extends mongodb.BulkWriteOptions { + session?: ClientSession; + skipValidation?: boolean; + throwOnValidationError?: boolean; + strict?: boolean | 'throw'; + /** When false, do not add timestamps to documents. Can be overridden at the operation level. */ + timestamps?: boolean; + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + } + + interface MongooseBulkSaveOptions extends mongodb.BulkWriteOptions { + timestamps?: boolean; + session?: ClientSession; + validateBeforeSave?: boolean; + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + } + + /** + * @deprecated use AnyBulkWriteOperation instead + */ + interface MongooseBulkWritePerWriteOptions { + timestamps?: boolean; + strict?: boolean | 'throw'; + session?: ClientSession; + skipValidation?: boolean; + } + + interface HydrateOptions { + setters?: boolean; + hydratedPopulatedDocs?: boolean; + virtuals?: boolean; + strict?: boolean | 'throw'; + } + + interface InsertManyOptions extends + PopulateOption, + SessionOption { + limit?: number; + // @deprecated, use includeResultMetadata instead + rawResult?: boolean; + includeResultMetadata?: boolean; + ordered?: boolean; + lean?: boolean; + throwOnValidationError?: boolean; + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + timestamps?: boolean | QueryTimestampsConfig; + } + + interface InsertManyResult extends mongodb.InsertManyResult { + mongoose?: { validationErrors?: Array }; + } + + type UpdateWriteOpResult = mongodb.UpdateResult; + type UpdateResult = mongodb.UpdateResult; + type DeleteResult = mongodb.DeleteResult; + + interface ModifyResult { + value: Default__v> | null; + /** see https://www.mongodb.com/docs/manual/reference/command/findAndModify/#lasterrorobject */ + lastErrorObject?: { + updatedExisting?: boolean; + upserted?: mongodb.ObjectId; + }; + ok: 0 | 1; + } + + type WriteConcern = mongodb.WriteConcern; + + /** A list of paths to validate. If set, Mongoose will validate only the modified paths that are in the given list. */ + type PathsToValidate = string[] | string; + /** + * @deprecated + */ + type pathsToValidate = PathsToValidate; + + interface SaveOptions extends + SessionOption { + checkKeys?: boolean; + j?: boolean; + safe?: boolean | WriteConcern; + timestamps?: boolean | QueryTimestampsConfig; + validateBeforeSave?: boolean; + validateModifiedOnly?: boolean; + w?: number | string; + wtimeout?: number; + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + } + + interface CreateOptions extends SaveOptions { + ordered?: boolean; + aggregateErrors?: boolean; + } + + interface RemoveOptions extends SessionOption, Omit {} + + interface MongooseBulkWritePerOperationOptions { + /** Skip validation for this operation. */ + skipValidation?: boolean; + /** When false, do not add timestamps. When true, overrides the `timestamps` option set in the `bulkWrite` options. */ + timestamps?: boolean; + } + + interface MongooseBulkUpdatePerOperationOptions extends MongooseBulkWritePerOperationOptions { + /** When true, allows updating fields that are marked as `immutable` in the schema. */ + overwriteImmutable?: boolean; + /** When false, do not set default values on insert. */ + setDefaultsOnInsert?: boolean; + } + + export type InsertOneModel = + mongodb.InsertOneModel & MongooseBulkWritePerOperationOptions; + + export type ReplaceOneModel = + Omit, 'filter'> & + { filter: QueryFilter } & + MongooseBulkUpdatePerOperationOptions; + + export type UpdateOneModel = + Omit, 'filter'> & + { filter: QueryFilter } & + MongooseBulkUpdatePerOperationOptions; + + export type UpdateManyModel = + Omit, 'filter'> & + { filter: QueryFilter } & + MongooseBulkUpdatePerOperationOptions; + + export type DeleteOneModel = + Omit, 'filter'> & + { filter: QueryFilter }; + + export type DeleteManyModel = + Omit, 'filter'> & + { filter: QueryFilter }; + + export type AnyBulkWriteOperation = + | { insertOne: InsertOneModel } + | { replaceOne: ReplaceOneModel } + | { updateOne: UpdateOneModel } + | { updateMany: UpdateManyModel } + | { deleteOne: DeleteOneModel } + | { deleteMany: DeleteManyModel }; + + const Model: Model; + + /* + * Apply common casting logic to the given type, allowing: + * - strings for ObjectIds + * - strings and numbers for Dates + * - strings for Buffers + * - strings for UUIDs + * - POJOs for subdocuments + * - vanilla arrays of POJOs for document arrays + * - POJOs and array of arrays for maps + */ + type CreateObjectWithExtraKeys = T & Record; + type ApplyBasicCreateCasting = { + [K in keyof T]: NonNullable extends Map + ? (Record | Array<[KeyType, ValueType]> | T[K]) + : NonNullable extends Types.DocumentArray + ? RawSubdocType[] | T[K] + : NonNullable extends Document + ? CreateObjectWithExtraKeys> | T[K] + : NonNullable extends TreatAsPrimitives + ? QueryTypeCasting + : NonNullable extends object + ? CreateObjectWithExtraKeys> | T[K] + : QueryTypeCasting; + }; + + type HasLeanOption = 'lean' extends keyof ObtainSchemaGeneric ? + ObtainSchemaGeneric['lean'] extends Record ? + true : + ObtainSchemaGeneric['lean'] : + false; + + /** + * Models are fancy constructors compiled from `Schema` definitions. + * An instance of a model is called a document. + * Models are responsible for creating and reading documents from the underlying MongoDB database + */ + export interface Model< + TRawDocType, + TQueryHelpers = {}, + TInstanceMethods = {}, + TVirtuals = {}, + THydratedDocumentType = HydratedDocument, + TSchema = any, + TLeanResultType = TRawDocType> extends + NodeJS.EventEmitter, + IndexManager, + SessionStarter { + new >(doc?: DocType, fields?: any | null, options?: AnyObject): THydratedDocumentType; + + aggregate(pipeline?: PipelineStage[], options?: AggregateOptions): Aggregate>; + aggregate(pipeline: PipelineStage[]): Aggregate>; + + /** Base Mongoose instance the model uses. */ + base: Mongoose; + + /** + * If this is a discriminator model, `baseModelName` is the name of + * the base model. + */ + baseModelName: string | undefined; + + /* Cast the given POJO to the model's schema */ + castObject(obj: AnyObject, options?: { ignoreCastErrors?: boolean }): TRawDocType; + + /* Apply defaults to the given document or POJO. */ + applyDefaults(obj: AnyObject): AnyObject; + applyDefaults(obj: TRawDocType): TRawDocType; + + /* Apply virtuals to the given POJO. */ + applyVirtuals(obj: AnyObject, virtalsToApply?: string[]): AnyObject; + + /** + * Apply this model's timestamps to a given POJO, including subdocument timestamps + */ + applyTimestamps(obj: AnyObject, options?: { isUpdate?: boolean, currentTime?: () => Date }): AnyObject; + + /** + * Sends multiple `insertOne`, `updateOne`, `updateMany`, `replaceOne`, + * `deleteOne`, and/or `deleteMany` operations to the MongoDB server in one + * command. This is faster than sending multiple independent operations (e.g. + * if you use `create()`) because with `bulkWrite()` there is only one network + * round trip to the MongoDB server. + */ + bulkWrite( + writes: Array>, + options: mongodb.BulkWriteOptions & MongooseBulkWriteOptions & { ordered: false } + ): Promise; + bulkWrite( + writes: Array>, + options?: mongodb.BulkWriteOptions & MongooseBulkWriteOptions + ): Promise; + + /** + * Sends multiple `save()` calls in a single `bulkWrite()`. This is faster than + * sending multiple `save()` calls because with `bulkSave()` there is only one + * network round trip to the MongoDB server. + */ + bulkSave(documents: Array, options?: MongooseBulkSaveOptions): Promise; + + /** Collection the model uses. */ + collection: Collection; + + /** Creates a `countDocuments` query: counts the number of documents that match `filter`. */ + countDocuments( + filter?: QueryFilter, + options?: (mongodb.CountOptions & MongooseBaseQueryOptions & mongodb.Abortable) | null + ): QueryWithHelpers< + number, + THydratedDocumentType, + TQueryHelpers, + TRawDocType, + 'countDocuments', + TInstanceMethods & TVirtuals + >; + countDocuments( + filter?: Query, + options?: (mongodb.CountOptions & MongooseBaseQueryOptions & mongodb.Abortable) | null + ): QueryWithHelpers< + number, + THydratedDocumentType, + TQueryHelpers, + TRawDocType, + 'countDocuments', + TInstanceMethods & TVirtuals + >; + + /** Creates a new document or documents */ + create(): Promise; + create(doc: Partial): Promise; + create(docs: Array>): Promise; + create(docs: Array>>>, options: CreateOptions & { aggregateErrors: true }): Promise<(THydratedDocumentType | Error)[]>; + create(docs: Array>>>, options?: CreateOptions): Promise; + create(doc: DeepPartial>>): Promise; + create(...docs: Array>>>): Promise; + + /** + * Create the collection for this model. By default, if no indexes are specified, + * mongoose will not create the collection for the model until any documents are + * created. Use this method to create the collection explicitly. + */ + createCollection(options?: mongodb.CreateCollectionOptions & Pick & { middleware?: boolean | SkipMiddlewareOptions }): Promise>; + + /** + * Create an [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/). + * This function only works when connected to MongoDB Atlas. + */ + createSearchIndex(description: SearchIndexDescription): Promise; + + /** + * Creates all [Atlas search indexes](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) defined in this model's schema. + * This function only works when connected to MongoDB Atlas. + */ + createSearchIndexes(): Promise; + + /** Connection the model uses. */ + db: Connection; + + /** + * Deletes all of the documents that match `conditions` from the collection. + * Behaves like `remove()`, but deletes all documents that match `conditions` + * regardless of the `single` option. + */ + deleteMany( + filter?: QueryFilter, + options?: (mongodb.DeleteOptions & MongooseBaseQueryOptions) | null + ): QueryWithHelpers< + mongodb.DeleteResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'deleteMany', + TInstanceMethods & TVirtuals + >; + deleteMany( + filter?: Query, + options?: (mongodb.DeleteOptions & MongooseBaseQueryOptions) | null + ): QueryWithHelpers< + mongodb.DeleteResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'deleteMany', + TInstanceMethods & TVirtuals + >; + + /** + * Deletes the first document that matches `conditions` from the collection. + * Behaves like `remove()`, but deletes at most one document regardless of the + * `single` option. + */ + deleteOne( + filter?: QueryFilter, + options?: (mongodb.DeleteOptions & MongooseBaseQueryOptions) | null + ): QueryWithHelpers< + mongodb.DeleteResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'deleteOne', + TInstanceMethods & TVirtuals + >; + deleteOne( + filter?: Query, + options?: (mongodb.DeleteOptions & MongooseBaseQueryOptions) | null + ): QueryWithHelpers< + mongodb.DeleteResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'deleteOne', + TInstanceMethods & TVirtuals + >; + + /** Adds a discriminator type. */ + discriminator>( + name: string | number, + schema: TDiscriminatorSchema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model< + TRawDocType & InferSchemaType, + TQueryHelpers & ObtainSchemaGeneric, + TInstanceMethods & ObtainSchemaGeneric, + TVirtuals & ObtainSchemaGeneric + > & ObtainSchemaGeneric & ObtainSchemaGeneric; + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): Model; + discriminator( + name: string | number, + schema: Schema, + value?: string | number | ObjectId | DiscriminatorOptions + ): U; + + /** + * Delete an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) by name. + * This function only works when connected to MongoDB Atlas. + */ + dropSearchIndex(name: string): Promise; + + /** + * Event emitter that reports any errors that occurred. Useful for global error + * handling. + */ + events: NodeJS.EventEmitter; + + /** + * Finds a single document by its _id field. `findById(id)` is almost* + * equivalent to `findOne({ _id: id })`. If you want to query by a document's + * `_id`, use `findById()` instead of `findOne()`. + */ + findById( + id: any, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + findById( + id?: any, + projection?: ProjectionType | null | undefined, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + + /** Finds one document. */ + findOne( + filter: QueryFilter, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + findOne( + filter: Query, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + TLeanResultType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + findOne( + filter?: QueryFilter, + projection?: ProjectionType | null | undefined, + options?: QueryOptions & mongodb.Abortable | null | undefined + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + findOne( + filter?: Query, + projection?: ProjectionType | null | undefined, + options?: QueryOptions & mongodb.Abortable | null | undefined + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : THydratedDocumentType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + + /** + * Shortcut for creating a new Document from existing raw data, pre-saved in the DB. + * The document returned has no paths marked as modified initially. + */ + hydrate(obj: any, projection?: ProjectionType, options?: HydrateOptions): THydratedDocumentType; + + /** + * This function is responsible for building [indexes](https://www.mongodb.com/docs/manual/indexes/), + * unless [`autoIndex`](http://mongoosejs.com/docs/guide.html#autoIndex) is turned off. + * Mongoose calls this function automatically when a model is created using + * [`mongoose.model()`](/docs/api/mongoose.html#mongoose_Mongoose-model) or + * [`connection.model()`](/docs/api/connection.html#connection_Connection-model), so you + * don't need to call it. + */ + init(): Promise; + + /** Inserts one or more new documents as a single `insertMany` call to the MongoDB server. */ + insertMany( + docs: Array + ): Promise>; + insertMany( + doc: Array, + options: InsertManyOptions & { ordered: false; rawResult: true; } + ): Promise> & { + mongoose: { + validationErrors: (CastError | Error.ValidatorError)[]; + results: Array< + Error | + Object | + THydratedDocumentType + > + } + }>; + insertMany( + docs: Array, + options: InsertManyOptions & { lean: true, rawResult: true; } + ): Promise>>; + insertMany( + doc: DocContents | TRawDocType, + options: InsertManyOptions & { ordered: false; rawResult: true; } + ): Promise> & { + mongoose: { + validationErrors: (CastError | Error.ValidatorError)[]; + results: Array< + Error | + Object | + MergeType + > + } + }>; + insertMany( + docs: Array, + options: InsertManyOptions & { lean: true; } + ): Promise>>; + insertMany( + docs: Array, + options: InsertManyOptions & { rawResult: true; } + ): Promise>>; + insertMany( + docs: Array, + options: InsertManyOptions & { lean: true; } + ): Promise>>; + insertMany( + docs: Array, + options: InsertManyOptions & { rawResult: true; } + ): Promise>>; + insertMany( + doc: DocContents, + options: InsertManyOptions & { lean: true; } + ): Promise>>; + insertMany( + doc: DocContents, + options: InsertManyOptions & { rawResult: true; } + ): Promise>>; + insertMany( + doc: Array, + options: InsertManyOptions + ): Promise>; + insertMany( + docs: Array + ): Promise>>>; + insertMany( + doc: DocContents, + options: InsertManyOptions + ): Promise>>>; + insertMany( + docs: Array, + options: InsertManyOptions + ): Promise>>>; + insertMany( + doc: DocContents + ): Promise< + Array>> + >; + + /** + * Shortcut for saving one document to the database. + * `MyModel.insertOne(obj, options)` is almost equivalent to `new MyModel(obj).save(options)`. + * The difference is that `insertOne()` checks if `obj` is already a document, and checks for discriminators. + */ + insertOne(doc: Partial>, options?: SaveOptions): Promise; + + /** + * List all [Atlas search indexes](https://www.mongodb.com/docs/atlas/atlas-search/create-index/) on this model's collection. + * This function only works when connected to MongoDB Atlas. + */ + listSearchIndexes(options?: mongodb.ListSearchIndexesOptions): Promise>; + + /** The name of the model */ + modelName: string; + + /** Populates document references. */ + populate( + docs: Array, + options: PopulateOptions | Array | string + ): Promise>; + populate( + doc: any, options: PopulateOptions | Array | string + ): Promise; + populate( + docs: Array, + options: PopulateOptions | Array | string + ): Promise>>; + populate( + doc: any, options: PopulateOptions | Array | string + ): Promise>; + + /** + * Update an existing [Atlas search index](https://www.mongodb.com/docs/atlas/atlas-search/create-index/). + * This function only works when connected to MongoDB Atlas. + */ + updateSearchIndex(name: string, definition: AnyObject): Promise; + + /** + * Changes the Connection instance this model uses to make requests to MongoDB. + * This function is most useful for changing the Connection that a Model defined using `mongoose.model()` uses + * after initialization. + */ + useConnection(connection: Connection): this; + + /** Casts and validates the given object against this model's schema, passing the given `context` to custom validators. */ + validate(): Promise; + validate(obj: any): Promise; + validate(obj: any, pathsOrOptions: PathsToValidate): Promise; + validate(obj: any, pathsOrOptions: { pathsToSkip?: pathsToSkip }): Promise; + + /** Watches the underlying collection for changes using [MongoDB change streams](https://www.mongodb.com/docs/manual/changeStreams/). */ + watch(pipeline?: Array>, options?: mongodb.ChangeStreamOptions & { hydrate?: boolean }): mongodb.ChangeStream; + + /** Adds a `$where` clause to this query */ + $where(argument: string | Function): QueryWithHelpers, THydratedDocumentType, TQueryHelpers, TRawDocType, 'find', TInstanceMethods & TVirtuals>; + + /** Registered discriminators for this model. */ + discriminators: { [name: string]: Model } | undefined; + + /** Translate any aliases fields/conditions so the final query or document object is pure */ + translateAliases(raw: any): any; + + /** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */ + distinct( + field: DocKey, + filter?: QueryFilter, + options?: QueryOptions + ): QueryWithHelpers< + Array< + DocKey extends keyof WithLevel1NestedPaths + ? WithoutUndefined[DocKey]>> + : unknown + >, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'distinct', + TInstanceMethods & TVirtuals + >; + distinct( + field: DocKey, + filter?: Query, + options?: QueryOptions + ): QueryWithHelpers< + Array< + DocKey extends keyof WithLevel1NestedPaths + ? WithoutUndefined[DocKey]>> + : unknown + >, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'distinct', + TInstanceMethods & TVirtuals + >; + + /** Creates a `estimatedDocumentCount` query: counts the number of documents in the collection. */ + estimatedDocumentCount(options?: QueryOptions): QueryWithHelpers< + number, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'estimatedDocumentCount', + TInstanceMethods & TVirtuals + >; + + /** + * Returns a document with its `_id` if at least one document exists in the database that matches + * the given `filter`, and `null` otherwise. + */ + exists( + filter: QueryFilter + ): QueryWithHelpers< + { _id: InferId } | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + exists( + filter: Query + ): QueryWithHelpers< + { _id: InferId } | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOne', + TInstanceMethods & TVirtuals + >; + + /** Creates a `find` query: gets a list of documents that match `filter`. */ + find( + filter: QueryFilter, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + GetLeanResultType, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + find( + filter: Query, + projection: ProjectionType | null | undefined, + options: QueryOptions & { lean: true } & mongodb.Abortable + ): QueryWithHelpers< + GetLeanResultType, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + find( + filter?: QueryFilter, + projection?: ProjectionType | null | undefined, + options?: QueryOptions & mongodb.Abortable + ): QueryWithHelpers< + ResultDoc[], + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + find( + filter?: Query, + projection?: ProjectionType | null | undefined, + options?: QueryOptions & mongodb.Abortable + ): QueryWithHelpers< + THydratedDocumentType[], + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + + /** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */ + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findByIdAndDelete( + id?: mongodb.ObjectId | any, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + + + /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ + findByIdAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findByIdAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType : ResultDoc, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findByIdAndUpdate( + id?: mongodb.ObjectId | any, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + + /** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */ + findOneAndDelete( + filter: QueryFilter, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findOneAndDelete( + filter: Query, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findOneAndDelete( + filter: QueryFilter, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findOneAndDelete( + filter: Query, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findOneAndDelete( + filter?: QueryFilter | null, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TRawDocType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + findOneAndDelete( + filter?: Query | null, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TRawDocType | null : THydratedDocumentType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndDelete', + TInstanceMethods & TVirtuals + >; + + /** Creates a `findOneAndReplace` query: atomically finds the given document and replaces it with `replacement`. */ + findOneAndReplace( + filter: QueryFilter, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter: Query, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + TLeanResultType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter: QueryFilter, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter: Query, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter: QueryFilter, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType : ResultDoc, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter: Query, + replacement: TRawDocType | AnyObject, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType : THydratedDocumentType, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter?: QueryFilter, + replacement?: TRawDocType | AnyObject, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + findOneAndReplace( + filter?: Query, + replacement?: TRawDocType | AnyObject, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : THydratedDocumentType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndReplace', + TInstanceMethods & TVirtuals + >; + + /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true, lean: true } + ): QueryWithHelpers< + ModifyResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { lean: true } + ): QueryWithHelpers< + GetLeanResultType | null, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers< + HasLeanOption extends true ? ModifyResult : ModifyResult, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType : ResultDoc, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType : THydratedDocumentType, + THydratedDocumentType, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter?: QueryFilter, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + findOneAndUpdate( + filter?: Query, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers< + HasLeanOption extends true ? TLeanResultType | null : ResultDoc | null, + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'findOneAndUpdate', + TInstanceMethods & TVirtuals + >; + + /** Creates a `replaceOne` query: finds the first document that matches `filter` and replaces it with `replacement`. */ + replaceOne( + filter?: QueryFilter, + replacement?: TRawDocType | AnyObject, + options?: (mongodb.ReplaceOptions & QueryOptions) | null + ): QueryWithHelpers; + replaceOne( + filter?: Query, + replacement?: TRawDocType | AnyObject, + options?: (mongodb.ReplaceOptions & QueryOptions) | null + ): QueryWithHelpers; + + /** Apply changes made to this model's schema after this model was compiled. */ + recompileSchema(): void; + + /** Schema the model uses. */ + schema: TSchema; + + /** Creates a `updateMany` query: updates all documents that match `filter` with `update`. */ + updateMany( + filter: QueryFilter, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null + ): QueryWithHelpers; + updateMany( + filter: Query, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null + ): QueryWithHelpers; + + /** Creates a `updateOne` query: updates the first document that matches `filter` with `update`. */ + updateOne( + filter: QueryFilter, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null + ): QueryWithHelpers; + updateOne( + filter: Query, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: (mongodb.UpdateOptions & MongooseUpdateQueryOptions) | null + ): QueryWithHelpers; + + /** Creates a Query, applies the passed conditions, and returns the Query. */ + where( + path: string, + val?: any + ): QueryWithHelpers extends true ? TRawDocType[] : ResultDoc[], ResultDoc, TQueryHelpers, TRawDocType, 'find', TInstanceMethods>; + where(obj: object): QueryWithHelpers< + HasLeanOption extends true ? TRawDocType[] : ResultDoc[], + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + where(): QueryWithHelpers< + HasLeanOption extends true ? TRawDocType[] : ResultDoc[], + ResultDoc, + TQueryHelpers, + TLeanResultType, + 'find', + TInstanceMethods & TVirtuals + >; + + /** + * If auto encryption is enabled, returns a ClientEncryption instance that is configured with the same settings that + * Mongoose's underlying MongoClient is using. If the client has not yet been configured, returns null. + */ + clientEncryption(): mongodb.ClientEncryption | null; + } +} diff --git a/backend/node_modules/mongoose/types/mongooseoptions.d.ts b/backend/node_modules/mongoose/types/mongooseoptions.d.ts new file mode 100644 index 0000000..073fdf6 --- /dev/null +++ b/backend/node_modules/mongoose/types/mongooseoptions.d.ts @@ -0,0 +1,232 @@ +declare module 'mongoose' { + import stream = require('stream'); + + interface MongooseOptions { + /** + * Set to `true` to set `allowDiskUse` to true to all aggregation operations by default. + * + * @default false + */ + allowDiskUse?: boolean; + /** + * Set to `false` to skip applying global plugins to child schemas. + * + * @default true + */ + applyPluginsToChildSchemas?: boolean; + + /** + * Set to `true` to apply global plugins to discriminator schemas. + * This typically isn't necessary because plugins are applied to the base schema and + * discriminators copy all middleware, methods, statics, and properties from the base schema. + * + * @default false + */ + applyPluginsToDiscriminators?: boolean; + + /** + * autoCreate is `true` by default unless readPreference is secondary or secondaryPreferred, + * which means Mongoose will attempt to create every model's underlying collection before + * creating indexes. If readPreference is secondary or secondaryPreferred, Mongoose will + * default to false for both autoCreate and autoIndex because both createCollection() and + * createIndex() will fail when connected to a secondary. + */ + autoCreate?: boolean; + + /** + * Set to `false` to disable automatic index creation for all models associated with this Mongoose instance. + * + * @default true + */ + autoIndex?: boolean; + + /** + * enable/disable mongoose's buffering mechanism for all connections and models. + * + * @default true + */ + bufferCommands?: boolean; + + /** + * If bufferCommands is on, this option sets the maximum amount of time Mongoose + * buffering will wait before throwing an error. + * If not specified, Mongoose will use 10000 (10 seconds). + * + * @default 10000 + */ + bufferTimeoutMS?: number; + + /** + * Set to `true` to `clone()` all schemas before compiling into a model. + * + * @default false + */ + cloneSchemas?: boolean; + + /** + * Set to `false` to disable the creation of the initial default connection. + * + * @default true + */ + createInitialConnection?: boolean; + + /** + * If `true`, prints the operations mongoose sends to MongoDB to the console. + * If a writable stream is passed, it will log to that stream, without colorization. + * If a callback function is passed, it will receive the collection name, the method + * name, then all arguments passed to the method. For example, if you wanted to + * replicate the default logging, you could output from the callback + * `Mongoose: ${collectionName}.${methodName}(${methodArgs.join(', ')})`. + * + * @default false + */ + debug?: + | boolean + | { color?: boolean; shell?: boolean; } + | stream.Writable + | ((collectionName: string, methodName: string, ...methodArgs: any[]) => void); + + /** + * If `true`, adds a `id` virtual to all schemas unless overwritten on a per-schema basis. + * @defaultValue true + */ + id?: boolean; + + /** + * If `false`, it will change the `createdAt` field to be [`immutable: false`](https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-immutable) + * which means you can update the `createdAt`. + * + * @default true + */ + 'timestamps.createdAt.immutable'?: boolean + + /** If set, attaches [maxTimeMS](https://www.mongodb.com/docs/manual/reference/operator/meta/maxTimeMS/) to every query */ + maxTimeMS?: number; + + /** + * Mongoose adds a getter to MongoDB ObjectId's called `_id` that + * returns `this` for convenience with populate. Set this to false to remove the getter. + * + * @default true + */ + objectIdGetter?: boolean; + + /** + * Set to `true` to default to overwriting models with the same name when calling + * `mongoose.model()`, as opposed to throwing an `OverwriteModelError`. + * + * @default false + */ + overwriteModels?: boolean; + + /** + * Set the default value for the `returnDocument` option to `findOneAndUpdate()`, + * `findByIdAndUpdate()`, and `findOneAndReplace()`. + * - `'before'`: Return the document before the update was applied. + * - `'after'`: Return the document after the update was applied. + * + * @default 'before' + */ + returnDocument?: 'before' | 'after'; + + /** + * @deprecated Use `returnDocument` instead. + */ + returnOriginal?: boolean; + + /** + * Set to true to enable [update validators]( + * https://mongoosejs.com/docs/validation.html#update-validators + * ) for all validators by default. + * + * @default false + */ + runValidators?: boolean; + + /** + * Sanitizes query filters against [query selector injection attacks]( + * https://thecodebarbarian.com/2014/09/04/defending-against-query-selector-injection-attacks.html + * ) by wrapping any nested objects that have a property whose name starts with $ in a $eq. + */ + sanitizeFilter?: boolean; + + sanitizeProjection?: boolean; + + /** + * Set to false to opt out of Mongoose adding all fields that you `populate()` + * to your `select()`. The schema-level option `selectPopulatedPaths` overwrites this one. + * + * @default true + */ + selectPopulatedPaths?: boolean; + + /** + * Mongoose also sets defaults on update() and findOneAndUpdate() when the upsert option is + * set by adding your schema's defaults to a MongoDB $setOnInsert operator. You can disable + * this behavior by setting the setDefaultsOnInsert option to false. + * + * @default true + */ + setDefaultsOnInsert?: boolean; + + /** + * Sets the default strict mode for schemas. + * May be `false`, `true`, or `'throw'`. + * + * @default true + */ + strict?: boolean | 'throw'; + + /** + * Set to `false` to allow populating paths that aren't in the schema. + * + * @default true + */ + strictPopulate?: boolean; + + /** + * Sets the default [strictQuery](https://mongoosejs.com/docs/guide.html#strictQuery) mode for schemas. + * May be `false`, `true`, or `'throw'`. + * + * @default false + */ + strictQuery?: boolean | 'throw'; + + /** + * Overwrites default objects to `toJSON()`, for determining how Mongoose + * documents get serialized by `JSON.stringify()` + * + * @default { transform: true, flattenDecimals: true } + */ + toJSON?: ToObjectOptions; + + /** + * Overwrites default objects to `toObject()` + * + * @default { transform: true, flattenDecimals: true } + */ + toObject?: ToObjectOptions; + + /** + * Set to true to make Mongoose use Node.js' built-in AsyncLocalStorage (Node >= 16.0.0) + * to set `session` option on all operations within a `connection.transaction(fn)` call + * by default. Defaults to false. + */ + transactionAsyncLocalStorage?: boolean; + + /** + * If `true`, convert any aliases in filter, projection, update, and distinct + * to their database property names. Defaults to false. + */ + translateAliases?: boolean; + + /** + * If `true`, allows passing update pipelines (arrays) to update operations by default + * without explicitly setting `updatePipeline: true` in each query. This is the global + * default for the `updatePipeline` query option. + * + * @default false + */ + updatePipeline?: boolean; + } +} diff --git a/backend/node_modules/mongoose/types/pipelinestage.d.ts b/backend/node_modules/mongoose/types/pipelinestage.d.ts new file mode 100644 index 0000000..a998ee0 --- /dev/null +++ b/backend/node_modules/mongoose/types/pipelinestage.d.ts @@ -0,0 +1,334 @@ +declare module 'mongoose' { + /** + * [Stages reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation-pipeline/#aggregation-pipeline-stages) + */ + export type PipelineStage = + | PipelineStage.AddFields + | PipelineStage.Bucket + | PipelineStage.BucketAuto + | PipelineStage.CollStats + | PipelineStage.Count + | PipelineStage.Densify + | PipelineStage.Documents + | PipelineStage.Facet + | PipelineStage.Fill + | PipelineStage.GeoNear + | PipelineStage.GraphLookup + | PipelineStage.Group + | PipelineStage.IndexStats + | PipelineStage.Limit + | PipelineStage.ListSessions + | PipelineStage.Lookup + | PipelineStage.Match + | PipelineStage.Merge + | PipelineStage.Out + | PipelineStage.PlanCacheStats + | PipelineStage.Project + | PipelineStage.Redact + | PipelineStage.ReplaceRoot + | PipelineStage.ReplaceWith + | PipelineStage.Sample + | PipelineStage.Search + | PipelineStage.SearchMeta + | PipelineStage.Set + | PipelineStage.SetWindowFields + | PipelineStage.Skip + | PipelineStage.Sort + | PipelineStage.SortByCount + | PipelineStage.UnionWith + | PipelineStage.Unset + | PipelineStage.Unwind + | PipelineStage.VectorSearch; + + export namespace PipelineStage { + export interface AddFields { + /** [`$addFields` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/) */ + $addFields: Record + } + + export interface Bucket { + /** [`$bucket` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucket/) */ + $bucket: { + groupBy: Expression; + boundaries: any[]; + default?: any + output?: Record + } + } + + export interface BucketAuto { + /** [`$bucketAuto` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucketAuto/) */ + $bucketAuto: { + groupBy: Expression | Record; + buckets: number; + output?: Record; + granularity?: 'R5' | 'R10' | 'R20' | 'R40' | 'R80' | '1-2-5' | 'E6' | 'E12' | 'E24' | 'E48' | 'E96' | 'E192' | 'POWERSOF2'; + } + } + + export interface CollStats { + /** [`$collStats` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/) */ + $collStats: { + latencyStats?: { histograms?: boolean }; + storageStats?: { scale?: number }; + count?: Record; + queryExecStats?: Record; + } + } + + export interface Count { + /** [`$count` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/count/) */ + $count: string; + } + + export interface Densify{ + /** [`$densify` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/) */ + $densify: { + field: string, + partitionByFields?: string[], + range: { + step: number, + unit?: 'millisecond' | 'second' | 'minute' | 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year', + bounds: number[] | globalThis.Date[] | 'full' | 'partition' + } + } + } + + export interface Documents { + /** [`$documents` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/) */ + $documents: Record[] + } + + export interface Fill { + /** [`$fill` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/) */ + $fill: { + partitionBy?: Expression, + partitionByFields?: string[], + sortBy?: Record, + output: Record + } + } + + export interface Facet { + /** [`$facet` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/) */ + $facet: Record; + } + + export type FacetPipelineStage = Exclude; + export type UnionWithPipelineStage = Exclude; + + export interface GeoNear { + /** [`$geoNear` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/) */ + $geoNear: { + near: { type: 'Point'; coordinates: [number, number] } | [number, number]; + distanceField: string; + distanceMultiplier?: number; + includeLocs?: string; + key?: string; + maxDistance?: number; + minDistance?: number; + query?: AnyObject; + spherical?: boolean; + /** + * Deprecated. Use only with MondoDB below 4.2 (removed in 4.2) + * @deprecated + */ + num?: number; + } + } + + export interface GraphLookup { + /** [`$graphLookup` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/) */ + $graphLookup: { + from: string; + startWith: any + connectFromField: string; + connectToField: string; + as: string; + maxDepth?: number; + depthField?: string; + restrictSearchWithMatch?: AnyObject; + } + } + + export interface Group { + /** [`$group` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/group) */ + $group: { _id: any } | { [key: string]: AccumulatorOperator } + } + + export interface IndexStats { + /** [`$indexStats` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexStats/) */ + $indexStats: Record; + } + + export interface Limit { + /** [`$limit` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/) */ + $limit: number + } + + export interface ListSessions { + /** [`$listSessions` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/) */ + $listSessions: { users?: { user: string; db: string }[] } | { allUsers?: true } + } + + export interface Lookup { + /** [`$lookup` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/) */ + $lookup: { + from: string + as: string + localField?: string + foreignField?: string + let?: Record + pipeline?: Exclude[] + } + } + + export interface Match { + /** [`$match` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/) */ + $match: QueryFilter; + } + + export interface Merge { + /** [`$merge` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/) */ + $merge: { + into: string | { db: string; coll: string } + on?: string | string[] + let?: Record + whenMatched?: 'replace' | 'keepExisting' | 'merge' | 'fail' | Extract[] + whenNotMatched?: 'insert' | 'discard' | 'fail' + } + } + + export interface Out { + /** [`$out` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/) */ + $out: string | { db: string; coll: string } + } + + export interface PlanCacheStats { + /** [`$planCacheStats` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/) */ + $planCacheStats: Record + } + + export interface Project { + /** [`$project` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/) */ + $project: { [field: string]: AnyExpression | Expression | Project['$project'] } + } + + export interface Redact { + /** [`$redact` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/) */ + $redact: Expression; + } + + export interface ReplaceRoot { + /** [`$replaceRoot` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/) */ + $replaceRoot: { newRoot: AnyExpression } + } + + export interface ReplaceWith { + /** [`$replaceWith` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/) */ + $replaceWith: ObjectExpressionOperator | { [field: string]: Expression } | `$${string}`; + } + + export interface Sample { + /** [`$sample` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/) */ + $sample: { size: number } + } + + export interface Search { + /** [`$search` reference](https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/) */ + $search: { + index?: string; + highlight?: { + /** [`highlightPath` reference](https://docs.atlas.mongodb.com/atlas-search/path-construction/#multiple-field-search) */ + path: string | string[] | { value: string, multi: string }; + maxCharsToExamine?: number; + maxNumPassages?: number; + }; + [operator: string]: any; + } + } + + export interface SearchMeta { + /** [`$searchMeta` reference](https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#mongodb-pipeline-pipe.-searchMeta) */ + $searchMeta: { + index?: string; + highlight?: { + /** [`highlightPath` reference](https://docs.atlas.mongodb.com/atlas-search/path-construction/#multiple-field-search) */ + path: string | string[] | { value: string, multi: string }; + maxCharsToExamine?: number; + maxNumPassages?: number; + }; + [operator: string]: any; + } + } + + export interface Set { + /** [`$set` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/) */ + $set: Record + } + + export interface SetWindowFields { + /** [`$setWindowFields` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/) */ + $setWindowFields: { + partitionBy?: any + sortBy?: Record + output: Record< + string, + WindowOperator & { + window?: { + documents?: [string | number, string | number] + range?: [string | number, string | number] + unit?: 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' + } + } + > + } + } + + export interface Skip { + /** [`$skip` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/) */ + $skip: number + } + + export interface Sort { + /** [`$sort` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/) */ + $sort: Record + } + + export interface SortByCount { + /** [`$sortByCount` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/) */ + $sortByCount: Expression; + } + + export interface UnionWith { + /** [`$unionWith` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/) */ + $unionWith: + | string + | { coll: string; pipeline?: UnionWithPipelineStage[] } + | { coll?: string; pipeline: UnionWithPipelineStage[] } + } + + export interface Unset { + /** [`$unset` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/) */ + $unset: string | string[] + } + + export interface Unwind { + /** [`$unwind` reference](https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/) */ + $unwind: string | { path: string; includeArrayIndex?: string; preserveNullAndEmptyArrays?: boolean } + } + export interface VectorSearch { + /** [`$vectorSearch` reference](https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/) */ + $vectorSearch: { + exact?: boolean; + index: string, + path: string, + queryVector: number[], + numCandidates?: number, + limit: number, + filter?: Expression, + } + } + + } +} diff --git a/backend/node_modules/mongoose/types/populate.d.ts b/backend/node_modules/mongoose/types/populate.d.ts new file mode 100644 index 0000000..dac2a24 --- /dev/null +++ b/backend/node_modules/mongoose/types/populate.d.ts @@ -0,0 +1,53 @@ +declare module 'mongoose' { + + /** + * Reference another Model + */ + type PopulatedDoc< + PopulatedType, + RawId extends RefType = (PopulatedType extends { _id?: RefType; } ? NonNullable : Types.ObjectId) | undefined + > = PopulatedType | RawId; + + interface PopulateOptions { + /** space delimited path(s) to populate */ + path: string; + /** fields to select */ + select?: any; + /** query conditions to match */ + match?: any; + /** optional model to use for population */ + model?: string | Model; + /** optional query options like sort, limit, etc */ + options?: QueryOptions; + /** correct limit on populated array */ + perDocumentLimit?: number; + /** optional boolean, set to `false` to allow populating paths that aren't in the schema */ + strictPopulate?: boolean; + /** deep populate */ + populate?: string | PopulateOptions | (string | PopulateOptions)[]; + /** + * If true Mongoose will always set `path` to a document, or `null` if no document was found. + * If false Mongoose will always set `path` to an array, which will be empty if no documents are found. + * Inferred from schema by default. + */ + justOne?: boolean; + /** transform function to call on every populated doc */ + transform?: (doc: any, id: any) => any; + /** Overwrite the schema-level local field to populate on if this is a populated virtual. */ + localField?: string; + /** Overwrite the schema-level foreign field to populate on if this is a populated virtual. */ + foreignField?: string; + /** Set to `false` to prevent Mongoose from repopulating paths that are already populated */ + forceRepopulate?: boolean; + /** + * Set to `true` to execute any populate queries one at a time, as opposed to in parallel. + * We recommend setting this option to `true` if using transactions, especially if also populating multiple paths or paths with multiple models. + * MongoDB server does **not** support multiple operations in parallel on a single transaction. + */ + ordered?: boolean; + } + + interface PopulateOption { + populate?: string | string[] | PopulateOptions | PopulateOptions[]; + } +} diff --git a/backend/node_modules/mongoose/types/query.d.ts b/backend/node_modules/mongoose/types/query.d.ts new file mode 100644 index 0000000..d487435 --- /dev/null +++ b/backend/node_modules/mongoose/types/query.d.ts @@ -0,0 +1,955 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + type StringQueryTypeCasting = string | RegExp; + type ObjectIdQueryTypeCasting = Types.ObjectId | string; + type DateQueryTypeCasting = string | number | NativeDate; + type UUIDQueryTypeCasting = Types.UUID | string; + type BufferQueryCasting = Buffer | mongodb.Binary | number[] | string | { $binary: string | mongodb.Binary }; + type QueryTypeCasting = T extends string + ? StringQueryTypeCasting + : T extends Types.ObjectId + ? ObjectIdQueryTypeCasting + : T extends Types.UUID + ? UUIDQueryTypeCasting + : T extends Buffer + ? BufferQueryCasting + : T extends NativeDate + ? DateQueryTypeCasting + : T; + + export type ApplyBasicQueryCasting = QueryTypeCasting | QueryTypeCasting | (T extends (infer U)[] ? QueryTypeCasting : T) | null; + + type _QueryFilter = ( + { [P in keyof T]?: mongodb.Condition>; } & + mongodb.RootFilterOperators<{ [P in keyof mongodb.WithId]?: ApplyBasicQueryCasting[P]>; }> + ); + type _QueryFilterLooseId = ( + { [P in keyof T]?: mongodb.Condition>; } & + mongodb.RootFilterOperators< + { [P in keyof T]?: ApplyBasicQueryCasting; } & + { _id?: any; } + > + ); + type QueryFilter = IsItRecordAndNotAny extends true ? _QueryFilter> : _QueryFilterLooseId>; + + type MongooseBaseQueryOptionKeys = + | 'context' + | 'middleware' + | 'multipleCastError' + | 'overwriteDiscriminatorKey' + | 'overwriteImmutable' + | 'populate' + | 'runValidators' + | 'sanitizeProjection' + | 'sanitizeFilter' + | 'schemaLevelProjections' + | 'setDefaultsOnInsert' + | 'strict' + | 'strictQuery' + | 'translateAliases' + | 'updatePipeline'; + + type MongooseBaseQueryOptions = Pick, MongooseBaseQueryOptionKeys | 'timestamps' | 'lean'> & { + [other: string]: any; + }; + + type MongooseUpdateQueryOptions = Pick, MongooseBaseQueryOptionKeys | 'timestamps'>; + + type ProjectionFields = { [Key in keyof DocType]?: any } & Record; + + type QueryWithHelpers< + ResultType, + DocType, + THelpers = {}, + RawDocType = DocType, + QueryOp = 'find', + TDocOverrides = Record + > = Query & THelpers; + + interface QueryTimestampsConfig { + createdAt?: boolean; + updatedAt?: boolean; + } + + // Options that can be passed to Query.prototype.lean() + interface LeanOptions { + // Set to false to strip out the version key + versionKey?: boolean; + // Transform the result document in place. `doc` is the raw document being transformed. + // Typed as `Record` because TypeScript gets confused when handling Document.prototype.deleteOne() + // and other document methods that try to infer the raw doc type from the Document class. + transform?: (doc: Record) => void; + [key: string]: any; + } + + interface QueryOptions extends + PopulateOption, + SessionOption { + arrayFilters?: AnyObject[]; + batchSize?: number; + collation?: mongodb.CollationOptions; + comment?: any; + context?: string; + explain?: mongodb.ExplainVerbosityLike; + fields?: any | string; + hint?: mongodb.Hint; + /** + * If truthy, mongoose will return the document as a plain JavaScript object rather than a mongoose document. + */ + lean?: boolean | LeanOptions; + limit?: number; + maxTimeMS?: number; + multi?: boolean; + multipleCastError?: boolean; + /** + * By default, `findOneAndUpdate()` returns the document as it was **before** + * `update` was applied. If you set `new: true`, `findOneAndUpdate()` will + * instead give you the object after `update` was applied. + * Use `returnDocument: 'after'` instead of `new: true`, or `returnDocument: 'before'` instead of `new: false`. + * + * @deprecated + */ + new?: boolean; + + overwriteDiscriminatorKey?: boolean; + /** + * Mongoose removes updated immutable properties from `update` by default (excluding $setOnInsert). + * Set `overwriteImmutable` to `true` to allow updating immutable properties using other update operators. + */ + overwriteImmutable?: boolean; + projection?: AnyObject | string; + /** + * if true, returns the full ModifyResult rather than just the document + */ + includeResultMetadata?: boolean; + readPreference?: string | mongodb.ReadPreferenceMode; + /** + * An alias for the `new` option. `returnOriginal: false` is equivalent to `new: true`. + * Use `returnDocument: 'after'` instead of `returnOriginal: false`, or `returnDocument: 'before'` instead of `returnOriginal: true`. + * + * @deprecated + */ + returnOriginal?: boolean; + /** + * Has two possible values, `'before'` and `'after'`. By default, it will return the document before the update was applied. + * @default 'before' + */ + returnDocument?: 'before' | 'after'; + /** + * Set to true to enable `update validators` + * (https://mongoosejs.com/docs/validation.html#update-validators). Defaults to false. + */ + runValidators?: boolean; + /* Set to `true` to automatically sanitize potentially unsafe user-generated query projections */ + sanitizeProjection?: boolean; + /** + * Set to `true` to automatically sanitize potentially unsafe query filters by stripping out query selectors that + * aren't explicitly allowed using `mongoose.trusted()`. + */ + sanitizeFilter?: boolean; + /** + * Enable or disable schema level projections for this query. Enabled by default. + * Set to `false` to include fields with `select: false` in the query result by default. + */ + schemaLevelProjections?: boolean; + setDefaultsOnInsert?: boolean; + skip?: number; + sort?: any; + /** overwrites the schema's strict mode option */ + strict?: boolean | string; + /** + * equal to `strict` by default, may be `false`, `true`, or `'throw'`. Sets the default + * [strictQuery](https://mongoosejs.com/docs/guide.html#strictQuery) mode for schemas. + */ + strictQuery?: boolean | 'throw'; + tailable?: number; + /** + * If set to `false` and schema-level timestamps are enabled, + * skip timestamps for this update. Note that this allows you to overwrite + * timestamps. Does nothing if schema-level timestamps are not set. + */ + timestamps?: boolean | QueryTimestampsConfig; + /** + * If `true`, convert any aliases in filter, projection, update, and distinct + * to their database property names. Defaults to false. + */ + translateAliases?: boolean; + upsert?: boolean; + useBigInt64?: boolean; + /** + * Set to true to allow passing in an update pipeline instead of an update document. + * Mongoose disallows update pipelines by default because Mongoose does not cast update pipelines. + */ + updatePipeline?: boolean; + writeConcern?: mongodb.WriteConcern; + + /** set to `false` to skip all user-defined middleware, or `{ pre: false }` / `{ post: false }` to skip only pre or post hooks */ + middleware?: boolean | SkipMiddlewareOptions; + + [other: string]: any; + } + + type QueryOpThatReturnsDocument = 'find' | 'findOne' | 'findOneAndUpdate' | 'findOneAndReplace' | 'findOneAndDelete'; + + type GetLeanResultType = QueryOp extends QueryOpThatReturnsDocument + ? (ResultType extends any[] ? Default__v>[] : Default__v>) + : ResultType; + + type MergePopulatePaths> = QueryOp extends QueryOpThatReturnsDocument + ? ResultType extends null + ? ResultType + : ResultType extends (infer U)[] + ? U extends Document + ? HydratedDocument, TDocOverrides, TQueryHelpers>[] + : (MergeType)[] + : ResultType extends Document + ? HydratedDocument, TDocOverrides, TQueryHelpers> + : MergeType + : MergeType; + + class Query> implements SessionOperation { + _mongooseOptions: QueryOptions; + + /** + * Returns a wrapper around a [mongodb driver cursor](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html). + * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function. + * This is equivalent to calling `.cursor()` with no arguments. + */ + [Symbol.asyncIterator](): AsyncIterableIterator>; + + /** Executes the query */ + exec(): Promise; + + $where(argument: string | Function): QueryWithHelpers< + DocType[], + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + + /** Specifies an `$all` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + all(path: string, val: Array): this; + all(val: Array): this; + + /** Sets the allowDiskUse option for the query (ignored for < 4.4.0) */ + allowDiskUse(value: boolean): this; + + /** Specifies arguments for an `$and` condition. */ + and(array: QueryFilter[]): this; + + /** Specifies the batchSize option. */ + batchSize(val: number): this; + + /** Specifies a `$box` condition */ + box(lower: number[], upper: number[]): this; + box(val: any): this; + + /** + * Casts this query to the schema of `model`. + * + * @param {Model} [model] the model to cast to. If not set, defaults to `this.model` + * @param {Object} [obj] If not set, defaults to this query's conditions + * @return {Object} the casted `obj` + */ + cast(model?: Model | null, obj?: any): any; + + /** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * Like `.then()`, but only takes a rejection handler. + */ + catch: Promise['catch']; + + /** + * Executes the query returning a `Promise` which will be + * resolved with `.finally()` chained. + */ + finally: Promise['finally']; + + // Returns a string representation of this query. + [Symbol.toStringTag]: string; + + /** Specifies a `$center` or `$centerSphere` condition. */ + circle(path: string, area: any): this; + circle(area: any): this; + + /** Make a copy of this query so you can re-execute it. */ + clone(): this; + + /** Adds a collation to this op (MongoDB 3.4 and up) */ + collation(value: mongodb.CollationOptions): this; + + /** Specifies the `comment` option. */ + comment(val: string): this; + + /** Specifies this query as a `countDocuments` query. */ + countDocuments( + criteria?: QueryFilter, + options?: QueryOptions + ): QueryWithHelpers; + countDocuments( + criteria?: Query, + options?: QueryOptions + ): QueryWithHelpers; + + /** + * Returns a wrapper around a [mongodb driver cursor](https://mongodb.github.io/node-mongodb-native/7.0/classes/FindCursor.html). + * A QueryCursor exposes a Streams3 interface, as well as a `.next()` function. + */ + cursor(options?: QueryOptions): Cursor, QueryOptions>; + + /** + * Declare and/or execute this query as a `deleteMany()` operation. Works like + * remove, except it deletes _every_ document that matches `filter` in the + * collection, regardless of the value of `single`. + */ + deleteMany( + filter?: QueryFilter, + options?: QueryOptions + ): QueryWithHelpers; + deleteMany( + filter?: Query, + options?: QueryOptions + ): QueryWithHelpers; + deleteMany(filter: QueryFilter): QueryWithHelpers< + any, + DocType, + THelpers, + RawDocType, + 'deleteMany', + TDocOverrides + >; + deleteMany(filter: Query): QueryWithHelpers< + any, + DocType, + THelpers, + RawDocType, + 'deleteMany', + TDocOverrides + >; + deleteMany(): QueryWithHelpers; + + /** + * Declare and/or execute this query as a `deleteOne()` operation. Works like + * remove, except it deletes at most one document regardless of the `single` + * option. + */ + deleteOne( + filter?: QueryFilter, + options?: QueryOptions + ): QueryWithHelpers; + deleteOne( + filter?: Query, + options?: QueryOptions + ): QueryWithHelpers; + deleteOne(filter: QueryFilter): QueryWithHelpers< + any, + DocType, + THelpers, + RawDocType, + 'deleteOne', + TDocOverrides + >; + deleteOne(filter: Query): QueryWithHelpers< + any, + DocType, + THelpers, + RawDocType, + 'deleteOne', + TDocOverrides + >; + deleteOne(): QueryWithHelpers; + + /** Creates a `distinct` query: returns the distinct values of the given `field` that match `filter`. */ + distinct( + field: DocKey, + filter?: QueryFilter, + options?: QueryOptions + ): QueryWithHelpers< + Array< + DocKey extends keyof WithLevel1NestedPaths + ? WithoutUndefined[DocKey]>> + : ResultType + >, + DocType, + THelpers, + RawDocType, + 'distinct', + TDocOverrides + >; + distinct( + field: DocKey, + filter?: Query, + options?: QueryOptions + ): QueryWithHelpers< + Array< + DocKey extends keyof WithLevel1NestedPaths + ? WithoutUndefined[DocKey]>> + : ResultType + >, + DocType, + THelpers, + RawDocType, + 'distinct', + TDocOverrides + >; + + /** Specifies a `$elemMatch` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + elemMatch(path: string, val: any): this; + elemMatch(val: Function | any): this; + + /** + * Gets/sets the error flag on this query. If this flag is not null or + * undefined, the `exec()` promise will reject without executing. + */ + error(): NativeError | null; + error(val: NativeError | null): this; + + /** Specifies the complementary comparison value for paths specified with `where()` */ + equals(val: any): this; + + /** Creates a `estimatedDocumentCount` query: counts the number of documents in the collection. */ + estimatedDocumentCount(options?: QueryOptions): QueryWithHelpers< + number, + DocType, + THelpers, + RawDocType, + 'estimatedDocumentCount', + TDocOverrides + >; + + /** Specifies a `$exists` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + exists(path: string, val: boolean): this; + exists(val: boolean): this; + + /** + * Sets the [`explain` option](https://www.mongodb.com/docs/manual/reference/method/cursor.explain/), + * which makes this query return detailed execution stats instead of the actual + * query result. This method is useful for determining what index your queries + * use. + */ + explain(verbose?: mongodb.ExplainVerbosityLike): this; + + /** Creates a `find` query: gets a list of documents that match `filter`. */ + find( + filter?: QueryFilter, + projection?: ProjectionType | null, + options?: QueryOptions | null + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find', TDocOverrides>; + find( + filter?: Query, + projection?: ProjectionType | null, + options?: QueryOptions | null + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'find', TDocOverrides>; + + /** Declares the query a findOne operation. When executed, returns the first found document. */ + findOne( + filter?: QueryFilter, + projection?: ProjectionType | null, + options?: QueryOptions | null + ): QueryWithHelpers; + findOne( + filter?: Query, + projection?: ProjectionType | null, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Creates a `findOneAndDelete` query: atomically finds the given document, deletes it, and returns the document as it was before deletion. */ + findOneAndDelete( + filter?: QueryFilter, + options?: QueryOptions | null + ): QueryWithHelpers; + findOneAndDelete( + filter?: Query, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Creates a `findOneAndUpdate` query: atomically find the first document that matches `filter` and apply `update`. */ + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'findOneAndUpdate', TDocOverrides>; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'findOneAndUpdate', TDocOverrides>; + findOneAndUpdate( + filter: QueryFilter, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers; + findOneAndUpdate( + filter: Query, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers; + findOneAndUpdate( + filter?: QueryFilter, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers; + findOneAndUpdate( + filter?: Query, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Declares the query a findById operation. When executed, returns the document with the given `_id`. */ + findById( + id: mongodb.ObjectId | any, + projection?: ProjectionType | null, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Creates a `findByIdAndDelete` query, filtering by the given `_id`. */ + findByIdAndDelete( + id: mongodb.ObjectId | any, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers, DocType, THelpers, RawDocType, 'findOneAndDelete', TDocOverrides>; + findByIdAndDelete( + id?: mongodb.ObjectId | any, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Creates a `findOneAndUpdate` query, filtering by the given `_id`. */ + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { includeResultMetadata: true } + ): QueryWithHelpers; + findByIdAndUpdate( + id: mongodb.ObjectId | any, + update: UpdateQuery, + options: QueryOptions & { upsert: true } & ReturnsNewDoc + ): QueryWithHelpers; + findByIdAndUpdate( + id?: mongodb.ObjectId | any, + update?: UpdateQuery, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** Specifies a `$geometry` condition */ + geometry(object: { type: string, coordinates: any[] }): this; + + /** + * For update operations, returns the value of a path in the update's `$set`. + * Useful for writing getters/setters that can work with both update operations + * and `save()`. + */ + get(path: string): any; + + /** Returns the current query filter (also known as conditions) as a POJO. */ + getFilter(): QueryFilter; + + /** Gets query options. */ + getOptions(): QueryOptions; + + /** Gets a list of paths to be populated by this query */ + getPopulatedPaths(): Array; + + /** Returns the current query filter. Equivalent to `getFilter()`. */ + getQuery(): QueryFilter; + + /** Returns the current update operations as a JSON object. */ + getUpdate(): UpdateQuery | UpdateWithAggregationPipeline | null; + + /** Specifies a `$gt` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + gt(path: string, val: any): this; + gt(val: number): this; + + /** Specifies a `$gte` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + gte(path: string, val: any): this; + gte(val: number): this; + + /** Sets query hints. */ + hint(val: any): this; + + /** Specifies an `$in` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + in(path: string, val: any[]): this; + in(val: Array): this; + + /** Declares an intersects query for `geometry()`. */ + intersects(arg?: any): this; + + /** Requests acknowledgement that this operation has been persisted to MongoDB's on-disk journal. */ + j(val: boolean | null): this; + + /** Sets the lean option. */ + lean(): QueryWithHelpers< + ResultType extends null + ? GetLeanResultType | null + : GetLeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + lean( + val: true | LeanOptions + ): QueryWithHelpers< + ResultType extends null + ? GetLeanResultType | null + : GetLeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + lean( + val: false + ): QueryWithHelpers< + ResultType extends AnyArray + ? DocType[] + : ResultType extends null + ? DocType | null + : DocType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + lean(): QueryWithHelpers< + ResultType extends null + ? LeanResultType | null + : LeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + lean( + val: boolean | LeanOptions + ): QueryWithHelpers< + ResultType extends null + ? LeanResultType | null + : LeanResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + + /** Specifies the maximum number of documents the query will return. */ + limit(val: number): this; + + /** Specifies a `$lt` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + lt(path: string, val: any): this; + lt(val: number): this; + + /** Specifies a `$lte` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + lte(path: string, val: any): this; + lte(val: number): this; + + /** + * Runs a function `fn` and treats the return value of `fn` as the new value + * for the query to resolve to. + */ + transform(fn: (doc: ResultType) => MappedType): QueryWithHelpers; + + /** Specifies an `$maxDistance` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + maxDistance(path: string, val: number): this; + maxDistance(val: number): this; + + /** + * Sets the [maxTimeMS](https://www.mongodb.com/docs/manual/reference/method/cursor.maxTimeMS/) + * option. This will tell the MongoDB server to abort if the query or write op + * has been running for more than `ms` milliseconds. + */ + maxTimeMS(ms: number): this; + + /** Merges another Query or conditions object into this one. */ + merge(source: QueryFilter): this; + merge(source: Query): this; + + /** Specifies a `$mod` condition, filters documents for documents whose `path` property is a number that is equal to `remainder` modulo `divisor`. */ + mod(path: string, val: number): this; + mod(val: Array): this; + + /** The model this query was created from */ + model: Model; // Can't use DocType, causes "Type instantiation is excessively deep" + + /** + * Getter/setter around the current mongoose-specific options for this query + * Below are the current Mongoose-specific options. + */ + mongooseOptions(val?: QueryOptions): QueryOptions; + + /** Specifies a `$ne` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + ne(path: string, val: any): this; + ne(val: any): this; + + /** Specifies a `$near` or `$nearSphere` condition */ + near(path: string, val: any): this; + near(val: any): this; + + /** Specifies an `$nin` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + nin(path: string, val: any[]): this; + nin(val: Array): this; + + /** Specifies arguments for an `$nor` condition. */ + nor(array: Array>): this; + + /** Specifies arguments for an `$or` condition. */ + or(array: Array>): this; + + /** + * Make this query throw an error if no documents match the given `filter`. + * This is handy for integrating with async/await, because `orFail()` saves you + * an extra `if` statement to check if no document was found. + */ + orFail(err?: NativeError | (() => NativeError)): QueryWithHelpers, DocType, THelpers, RawDocType, QueryOp, TDocOverrides>; + + /** Specifies a `$polygon` condition */ + polygon(path: string, ...coordinatePairs: number[][]): this; + polygon(...coordinatePairs: number[][]): this; + + /** Specifies paths which should be populated with other documents. */ + populate( + path: string | string[], + select?: string | any, + model?: string | Model, + match?: any + ): QueryWithHelpers< + ResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + populate( + options: PopulateOptions | (PopulateOptions | string)[] + ): QueryWithHelpers< + ResultType, + DocType, + THelpers, + RawDocType, + QueryOp, + TDocOverrides + >; + populate( + path: string | string[], + select?: string | any, + model?: string | Model, + match?: any + ): QueryWithHelpers< + MergePopulatePaths, + DocType, + THelpers, + UnpackedIntersection, + QueryOp, + TDocOverrides + >; + populate( + options: PopulateOptions | (PopulateOptions | string)[] + ): QueryWithHelpers< + MergePopulatePaths, + DocType, + THelpers, + UnpackedIntersection, + QueryOp, + TDocOverrides + >; + + /** Add pre middleware to this query instance. Doesn't affect other queries. */ + pre(fn: Function): this; + + /** Add post middleware to this query instance. Doesn't affect other queries. */ + post(fn: Function): this; + + /** Get/set the current projection (AKA fields). Pass `null` to remove the current projection. */ + projection(fields?: ProjectionFields | string): ProjectionFields; + projection(fields: null): null; + projection(): ProjectionFields | null; + + /** Determines the MongoDB nodes from which to read. */ + read(mode: string | mongodb.ReadPreferenceMode, tags?: any[]): this; + + /** Sets the readConcern option for the query. */ + readConcern(level: string): this; + + /** Specifies a `$regex` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + regex(path: string, val: RegExp): this; + regex(val: string | RegExp): this; + + /** + * Declare and/or execute this query as a replaceOne() operation. Same as + * `update()`, except MongoDB will replace the existing document and will + * not accept any [atomic](https://www.mongodb.com/docs/manual/tutorial/model-data-for-atomic-operations/#pattern) operators (`$set`, etc.) + */ + replaceOne( + filter?: QueryFilter, + replacement?: DocType | AnyObject, + options?: QueryOptions | null + ): QueryWithHelpers; + replaceOne( + filter?: Query, + replacement?: DocType | AnyObject, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** + * Sets this query's `sanitizeProjection` option. With `sanitizeProjection()`, you can pass potentially untrusted user data to `.select()`. + */ + sanitizeProjection(value: boolean): this; + + /** + * Enable or disable schema level projections for this query. Enabled by default. + * Set to `false` to include fields with `select: false` in the query result by default. + */ + schemaLevelProjections(value: boolean): this; + + /** Specifies which document fields to include or exclude (also known as the query "projection") */ + select( + arg: string | readonly string[] | Record + ): QueryWithHelpers< + IfEquals< + RawDocTypeOverride, + {}, + ResultType, + ResultType extends any[] + ? ResultType extends HydratedDocument[] + ? HydratedDocument[] + : RawDocTypeOverride[] + : (ResultType extends HydratedDocument + ? HydratedDocument + : RawDocTypeOverride) | (null extends ResultType ? null : never) + >, + DocType, + THelpers, + IfEquals< + RawDocTypeOverride, + {}, + RawDocType, + RawDocTypeOverride + >, + QueryOp, + TDocOverrides + >; + + /** Determines if field selection has been made. */ + selected(): boolean; + + /** Determines if exclusive field selection has been made. */ + selectedExclusively(): boolean; + + /** Determines if inclusive field selection has been made. */ + selectedInclusively(): boolean; + + /** + * Sets the [MongoDB session](https://www.mongodb.com/docs/manual/reference/server-sessions/) + * associated with this query. Sessions are how you mark a query as part of a + * [transaction](/docs/transactions.html). + */ + session(session: mongodb.ClientSession | null): this; + + /** + * Adds a `$set` to this query's update without changing the operation. + * This is useful for query middleware so you can add an update regardless + * of whether you use `updateOne()`, `updateMany()`, `findOneAndUpdate()`, etc. + */ + set(path: string | Record, value?: any): this; + + /** Sets query options. Some options only make sense for certain operations. */ + setOptions(options: QueryOptions, overwrite?: boolean): this; + + /** Sets the query conditions to the provided JSON object. */ + setQuery(val: QueryFilter | null): void; + setQuery(val: Query | null): void; + + setUpdate(update: UpdateQuery | UpdateWithAggregationPipeline): void; + + /** Specifies an `$size` query condition. When called with one argument, the most recent path passed to `where()` is used. */ + size(path: string, val: number): this; + size(val: number): this; + + /** Specifies the number of documents to skip. */ + skip(val: number): this; + + /** Specifies a `$slice` projection for an array. */ + slice(path: string, val: number | Array): this; + slice(val: number | Array): this; + + /** Sets the sort order. If an object is passed, values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`. */ + sort( + arg?: string | Record | [string, SortOrder][] | undefined | null, + options?: { override?: boolean } + ): this; + + /** Sets the tailable option (for use with capped collections). */ + tailable(bool?: boolean, opts?: { + numberOfRetries?: number; + tailableRetryInterval?: number; + }): this; + + /** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + */ + then: Promise['then']; + + /** Converts this query to a customized, reusable query constructor with all arguments and options retained. */ + toConstructor(): RetType; + + /** + * Declare and/or execute this query as an updateMany() operation. Same as + * `update()`, except MongoDB will update _all_ documents that match `filter` + */ + updateMany( + filter: QueryFilter, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: QueryOptions | null + ): QueryWithHelpers; + updateMany( + filter: Query, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** + * Declare and/or execute this query as an updateOne() operation. Same as + * `update()`, except it does not support the `multi` or `overwrite` options. + */ + updateOne( + filter: QueryFilter, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: QueryOptions | null + ): QueryWithHelpers; + updateOne( + filter: Query, + update: UpdateQuery | UpdateWithAggregationPipeline, + options?: QueryOptions | null + ): QueryWithHelpers; + + /** + * Sets the specified number of `mongod` servers, or tag set of `mongod` servers, + * that must acknowledge this write before this write is considered successful. + */ + w(val: string | number | null): this; + + /** Specifies a path for use with chaining. */ + where(path: string, val?: any): this; + where(obj: object): this; + where(): this; + + /** Defines a `$within` or `$geoWithin` argument for geo-spatial queries. */ + within(val?: any): this; + + /** + * If [`w > 1`](/docs/api/query.html#query_Query-w), the maximum amount of time to + * wait for this write to propagate through the replica set before this + * operation fails. The default is `0`, which means no timeout. + */ + wtimeout(ms: number): this; + } +} diff --git a/backend/node_modules/mongoose/types/schemaoptions.d.ts b/backend/node_modules/mongoose/types/schemaoptions.d.ts new file mode 100644 index 0000000..846b1b9 --- /dev/null +++ b/backend/node_modules/mongoose/types/schemaoptions.d.ts @@ -0,0 +1,284 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + interface SchemaTimestampsConfig { + createdAt?: boolean | string; + updatedAt?: boolean | string; + currentTime?: () => (NativeDate | number); + } + + type SchemaOptionsStaticsPropertyType = IfEquals< + TStaticMethods, + {}, + Record unknown> & ThisType, + { [K in keyof TStaticMethods]: OmitThisParameter } & ThisType + >; + + type TypeKeyBaseType = string; + + type DefaultTypeKey = 'type'; + interface SchemaOptions< + DocType = unknown, + TInstanceMethods = {}, + QueryHelpers = {}, + TStaticMethods = {}, + TVirtuals = {}, + THydratedDocumentType = HydratedDocument, + TModelType = Model + > { + /** + * By default, Mongoose's init() function creates all the indexes defined in your model's schema by + * calling Model.createIndexes() after you successfully connect to MongoDB. If you want to disable + * automatic index builds, you can set autoIndex to false. + */ + autoIndex?: boolean; + /** + * Similar to autoIndex, except for automatically creates any Atlas search indexes defined in your + * schema. Unlike autoIndex, this option defaults to false. + */ + autoSearchIndex?: boolean; + /** + * If set to `true`, Mongoose will call Model.createCollection() to create the underlying collection + * in MongoDB if autoCreate is set to true. Calling createCollection() sets the collection's default + * collation based on the collation option and establishes the collection as a capped collection if + * you set the capped schema option. + */ + autoCreate?: boolean; + /** + * By default, mongoose buffers commands when the connection goes down until the driver manages to reconnect. + * To disable buffering, set bufferCommands to false. + */ + bufferCommands?: boolean; + /** + * If bufferCommands is on, this option sets the maximum amount of time Mongoose buffering will wait before + * throwing an error. If not specified, Mongoose will use 10000 (10 seconds). + */ + bufferTimeoutMS?: number; + /** + * Mongoose supports MongoDBs capped collections. To specify the underlying MongoDB collection be capped, set + * the capped option to the maximum size of the collection in bytes. + */ + capped?: boolean | number | { size?: number; max?: number; autoIndexId?: boolean; }; + /** Sets a default collation for every query and aggregation. */ + collation?: mongodb.CollationOptions; + + /** Arbitrary options passed to `createCollection()` */ + collectionOptions?: mongodb.CreateCollectionOptions; + + /** Default lean options for queries */ + lean?: boolean | LeanOptions; + + /** The timeseries option to use when creating the model's collection. */ + timeseries?: mongodb.TimeSeriesCollectionOptions; + + /** The number of seconds after which a document in a timeseries collection expires. */ + expireAfterSeconds?: number; + + /** The time after which a document in a timeseries collection expires. */ + expires?: number | string; + + /** + * Mongoose by default produces a collection name by passing the model name to the utils.toCollectionName + * method. This method pluralizes the name. Set this option if you need a different name for your collection. + */ + collection?: string; + /** + * When you define a [discriminator](/docs/discriminators.html), Mongoose adds a path to your + * schema that stores which discriminator a document is an instance of. By default, Mongoose + * adds an `__t` path, but you can set `discriminatorKey` to overwrite this default. + * + * @default '__t' + */ + discriminatorKey?: string; + + /** + * Option for nested Schemas. + * + * If true, skip building indexes on this schema's path. + * + * @default false + */ + excludeIndexes?: boolean; + /** + * Mongoose assigns each of your schemas an id virtual getter by default which returns the document's _id field + * cast to a string, or in the case of ObjectIds, its hexString. + */ + id?: boolean; + /** + * Mongoose assigns each of your schemas an _id field by default if one is not passed into the Schema + * constructor. The type assigned is an ObjectId to coincide with MongoDB's default behavior. If you + * don't want an _id added to your schema at all, you may disable it using this option. + */ + _id?: boolean; + /** + * Mongoose will, by default, "minimize" schemas by removing empty objects. This behavior can be + * overridden by setting minimize option to false. It will then store empty objects. + */ + minimize?: boolean; + /** + * Optimistic concurrency is a strategy to ensure the document you're updating didn't change between when you + * loaded it using find() or findOne(), and when you update it using save(). Set to `true` to enable + * optimistic concurrency for all fields. Set to a string array to enable optimistic concurrency only for + * the specified fields; note that this **replaces** the default array versioning behavior. Set to + * `{ exclude: string[] }` to enable optimistic concurrency for all fields except the specified ones; + * this also **replaces** the default array versioning. + */ + optimisticConcurrency?: boolean | string[] | { exclude: string[] }; + /** + * If `plugin()` called with tags, Mongoose will only apply plugins to schemas that have + * a matching tag in `pluginTags` + */ + pluginTags?: string[]; + /** + * Allows setting query#read options at the schema level, providing us a way to apply default ReadPreferences + * to all queries derived from a model. + */ + read?: string; + /** + * Set a default readConcern for all queries at the schema level + */ + readConcern?: { level: 'local' | 'available' | 'majority' | 'snapshot' | 'linearizable' } + /** Allows setting write concern at the schema level. */ + writeConcern?: WriteConcern; + /** defaults to true. */ + safe?: boolean | { w?: number | string; wtimeout?: number; j?: boolean }; + /** + * The shardKey option is used when we have a sharded MongoDB architecture. Each sharded collection is + * given a shard key which must be present in all insert/update operations. We just need to set this + * schema option to the same shard key and we'll be all set. + */ + shardKey?: Record; + /** + * The strict option, (enabled by default), ensures that values passed to our model constructor that were not + * specified in our schema do not get saved to the db. + */ + strict?: boolean | 'throw'; + /** + * equal to `strict` by default, may be `false`, `true`, or `'throw'`. Sets the default + * [strictQuery](https://mongoosejs.com/docs/guide.html#strictQuery) mode for schemas. + */ + strictQuery?: boolean | 'throw'; + /** Exactly the same as the toObject option but only applies when the document's toJSON method is called. */ + toJSON?: ToObjectOptions; + /** + * Documents have a toObject method which converts the mongoose document into a plain JavaScript object. + * This method accepts a few options. Instead of applying these options on a per-document basis, we may + * declare the options at the schema level and have them applied to all of the schema's documents by + * default. + */ + toObject?: ToObjectOptions; + /** + * By default, if you have an object with key 'type' in your schema, mongoose will interpret it as a + * type declaration. However, for applications like geoJSON, the 'type' property is important. If you want to + * control which key mongoose uses to find type declarations, set the 'typeKey' schema option. + */ + typeKey?: string; + + /** + * By default, documents are automatically validated before they are saved to the database. This is to + * prevent saving an invalid document. If you want to handle validation manually, and be able to save + * objects which don't pass validation, you can set validateBeforeSave to false. + */ + validateBeforeSave?: boolean; + /** + * By default, validation will run on modified and required paths before saving to the database. + * You can choose to have Mongoose only validate modified paths by setting validateModifiedOnly to true. + */ + validateModifiedOnly?: boolean; + /** + * The versionKey is a property set on each document when first created by Mongoose. This keys value + * contains the internal revision of the document. The versionKey option is a string that represents + * the path to use for versioning. The default is '__v'. + * + * @default '__v' + */ + versionKey?: string | false; + /** + * By default, Mongoose will automatically select() any populated paths for you, unless you explicitly exclude them. + * + * @default true + */ + selectPopulatedPaths?: boolean; + /** + * skipVersioning allows excluding paths from versioning (i.e., the internal revision will not be + * incremented even if these paths are updated). DO NOT do this unless you know what you're doing. + * For subdocuments, include this on the parent document using the fully qualified path. + */ + skipVersioning?: { [key: string]: boolean; }; + /** + * Validation errors in a single nested schema are reported + * both on the child and on the parent schema. + * Set storeSubdocValidationError to false on the child schema + * to make Mongoose only report the parent error. + */ + storeSubdocValidationError?: boolean; + /** + * The timestamps option tells mongoose to assign createdAt and updatedAt fields to your schema. The type + * assigned is Date. By default, the names of the fields are createdAt and updatedAt. Customize the + * field names by setting timestamps.createdAt and timestamps.updatedAt. + */ + timestamps?: boolean | SchemaTimestampsConfig; + + /** + * Using `save`, `isNew`, and other Mongoose reserved names as schema path names now triggers a warning, not an error. + * You can suppress the warning by setting { suppressReservedKeysWarning: true } schema options. Keep in mind that this + * can break plugins that rely on these reserved names. + */ + suppressReservedKeysWarning?: boolean, + + /** + * Model Statics methods. + */ + statics?: SchemaOptionsStaticsPropertyType + + /** + * Document instance methods. + */ + methods?: IfEquals< + TInstanceMethods, + {}, + Record unknown>, + AddThisParameter & AnyObject + > + + /** + * Query helper functions. + */ + query?: IfEquals< + QueryHelpers, + {}, + Record>(this: T, ...args: any) => T>, + QueryHelpers + > + + /** + * Set whether to cast non-array values to arrays. + * @default true + */ + castNonArrays?: boolean; + + /** + * Virtual paths. + */ + virtuals?: SchemaOptionsVirtualsPropertyType, + + /** + * Set to `true` to default to overwriting models with the same name when calling `mongoose.model()`, as opposed to throwing an `OverwriteModelError`. + * @default false + */ + overwriteModels?: boolean; + + /** + * Required when the schema is encrypted. + */ + encryptionType?: 'csfle' | 'queryableEncryption'; + } + + interface DefaultSchemaOptions { + typeKey: 'type'; + id: true; + _id: true; + timestamps: false; + versionKey: '__v' + } +} diff --git a/backend/node_modules/mongoose/types/schematypes.d.ts b/backend/node_modules/mongoose/types/schematypes.d.ts new file mode 100644 index 0000000..5aedcb1 --- /dev/null +++ b/backend/node_modules/mongoose/types/schematypes.d.ts @@ -0,0 +1,657 @@ +import * as BSON from 'bson'; + +declare module 'mongoose' { + /** The Mongoose Date [SchemaType](/docs/schematypes.html). */ + type Date = Schema.Types.Date; + + /** + * The Mongoose Decimal128 [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that should be + * [128-bit decimal floating points](http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-34-decimal.html). + * Do not use this to create a new Decimal128 instance, use `mongoose.Types.Decimal128` + * instead. + */ + type Decimal128 = Schema.Types.Decimal128; + + /** + * The Mongoose Int32 [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that should be + * 32-bit integers + * Do not use this to create a new Int32 instance, use `mongoose.Types.Int32` + * instead. + */ + type Int32 = Schema.Types.Int32; + + /** + * The Mongoose Mixed [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that Mongoose's change tracking, casting, + * and validation should ignore. + */ + type Mixed = Schema.Types.Mixed; + + /** + * The Mongoose Number [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that Mongoose should cast to numbers. + */ + type Number = Schema.Types.Number; + + /** + * The Mongoose Double [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that Mongoose should cast to doubles (IEEE 754-2008)/ + */ + type Double = Schema.Types.Double; + + /** + * The Mongoose ObjectId [SchemaType](/docs/schematypes.html). Used for + * declaring paths in your schema that should be + * [MongoDB ObjectIds](https://www.mongodb.com/docs/manual/reference/method/ObjectId/). + * Do not use this to create a new ObjectId instance, use `mongoose.Types.ObjectId` + * instead. + */ + type ObjectId = Schema.Types.ObjectId; + + /** The various Mongoose SchemaTypes. */ + const SchemaTypes: typeof Schema.Types; + + type DefaultType = T extends Schema.Types.Mixed ? any : Partial>; + + class SchemaTypeOptions> { + type?: T extends string ? StringSchemaDefinition + : T extends number ? NumberSchemaDefinition + : T extends boolean ? BooleanSchemaDefinition + : T extends NativeDate ? DateSchemaDefinition + : T extends Map ? SchemaDefinition + : T extends Buffer ? SchemaDefinition + : T extends Types.ObjectId ? ObjectIdSchemaDefinition + : T extends Types.ObjectId[] ? + AnyArray | AnyArray> + : T extends object[] ? + | AnyArray> + | AnyArray, EnforcedDocType, THydratedDocumentType>> + | AnyArray, EnforcedDocType, THydratedDocumentType>> + : T extends string[] ? + AnyArray | AnyArray> + : T extends number[] ? + AnyArray | AnyArray> + : T extends boolean[] ? + AnyArray | AnyArray> + : T extends Function[] ? + AnyArray | AnyArray, EnforcedDocType, THydratedDocumentType>> + : T | typeof SchemaType | Schema | SchemaDefinition | Function | AnyArray; + + /** Defines a virtual with the given name that gets/sets this path. */ + alias?: string | string[]; + + /** Function or object describing how to validate this schematype. See [validation docs](https://mongoosejs.com/docs/validation.html). */ + validate?: SchemaValidator | AnyArray>; + + /** Allows overriding casting logic for this individual path. If a string, the given string overwrites Mongoose's default cast error message. */ + cast?: + | string + | boolean + | ((value: any) => T) + | [(value: any) => T, string] + | [((value: any) => T) | null, (value: any, path: string, model: Model, kind: string) => string]; + + /** + * If true, attach a required validator to this path, which ensures this path + * cannot be set to a nullish value. If a function, Mongoose calls the + * function and only checks for nullish values if the function returns a truthy value. + */ + required?: + | boolean + | ((this: THydratedDocumentType) => boolean) + | [boolean, string] + | [(this: THydratedDocumentType) => boolean, string]; + + /** + * The default value for this path. If a function, Mongoose executes the function + * and uses the return value as the default. + */ + default?: DefaultType | ((this: THydratedDocumentType, doc: THydratedDocumentType) => DefaultType | null | undefined) | null; + + /** + * The model that `populate()` should use if populating this path. + */ + ref?: string | Model | ((this: any, doc: any) => string | Model); + + /** + * The path in the document that `populate()` should use to find the model + * to use. + */ + + refPath?: string | ((this: any, doc: any) => string); + + /** + * Whether to include or exclude this path by default when loading documents + * using `find()`, `findOne()`, etc. + */ + select?: boolean | number; + + /** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * build an index on this path when the model is compiled. + */ + index?: boolean | IndexDirection | IndexOptions; + + /** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose + * will build a unique index on this path when the + * model is compiled. [The `unique` option is **not** a validator](/docs/validation.html#the-unique-option-is-not-a-validator). + */ + unique?: boolean | number | [true, string]; + + /** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * disallow changes to this path once the document is saved to the database for the first time. Read more + * about [immutability in Mongoose here](http://thecodebarbarian.com/whats-new-in-mongoose-5-6-immutable-properties.html). + */ + immutable?: boolean | ((this: any, doc: any) => boolean); + + /** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose will + * build a sparse index on this path. + */ + sparse?: boolean | number; + + /** + * If [truthy](https://masteringjs.io/tutorials/fundamentals/truthy), Mongoose + * will build a text index on this path. + */ + text?: boolean | number | any; + + /** + * Define a transform function for this individual schema type. + * Only called when calling `toJSON()` or `toObject()`. + */ + transform?: (this: any, val: T) => any; + + /** defines a custom getter for this property using [`Object.defineProperty()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). */ + get?: (value: any, doc?: this) => T | undefined; + + /** defines a custom setter for this property using [`Object.defineProperty()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). */ + set?: (value: any, priorVal?: T, doc?: this) => any; + + /** array of allowed values for this path. Allowed for strings, numbers, and arrays of strings */ + enum?: + | Array + | ReadonlyArray + | { values: Array | ReadonlyArray; message?: string } + | { [path: string]: string | number | null }; + + /** The default [subtype](http://bsonspec.org/spec.html) associated with this buffer when it is stored in MongoDB. Only allowed for buffer paths */ + subtype?: number; + + /** The minimum value allowed for this path. Only allowed for numbers and dates. */ + min?: + | number + | NativeDate + | [number, string] + | [NativeDate, string] + | readonly [number, string] + | readonly [NativeDate, string]; + + /** The maximum value allowed for this path. Only allowed for numbers and dates. */ + max?: + | number + | NativeDate + | [number, string] + | [NativeDate, string] + | readonly [number, string] + | readonly [NativeDate, string]; + + /** Set to false to disable minimizing empty single nested subdocuments by default */ + minimize?: boolean; + + /** Defines a TTL index on this path. Only allowed for dates. */ + expires?: string | number; + + /** If `true`, Mongoose will skip gathering indexes on subpaths. Only allowed for subdocuments and subdocument arrays. */ + excludeIndexes?: boolean; + + /** If set, overrides the child schema's `_id` option. Only allowed for subdocuments and subdocument arrays. */ + _id?: boolean; + + /** Embedded discriminators for this path. Only allowed for subdocuments and subdocument arrays. */ + discriminators?: Record>; + + /** If set, specifies the type of this map's values. Mongoose will cast this map's values to the given type. */ + of?: Function | SchemaDefinitionProperty | (Function | SchemaDefinitionProperty)[]; + + /** If true, uses Mongoose's default `_id` settings. Only allowed for ObjectIds */ + auto?: boolean; + + /** Attaches a validator that succeeds if the data string matches the given regular expression, and fails otherwise. */ + match?: RegExp | [RegExp, string] | readonly [RegExp, string]; + + /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that lowercases this string using JavaScript's built-in `String#toLowerCase()`. */ + lowercase?: boolean; + + /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that removes leading and trailing whitespace using JavaScript's built-in `String#trim()`. */ + trim?: boolean; + + /** If truthy, Mongoose will add a [custom setter](https://mongoosejs.com/docs/api/schematype.html#SchemaType.prototype.set()) that uppercases this string using JavaScript's built-in `String#toUpperCase()`. */ + uppercase?: boolean; + + /** If set, Mongoose will add a custom validator that ensures the given string's `length` is at least the given number. */ + minlength?: number | [number, string] | readonly [number, string]; + minLength?: number | [number, string] | readonly [number, string]; + + /** If set, Mongoose will add a custom validator that ensures the given string's `length` is at most the given number. */ + maxlength?: number | [number, string] | readonly [number, string]; + maxLength?: number | [number, string] | readonly [number, string]; + + [other: string]: any; + + /** + * If set, configures the field for automatic encryption. + */ + encrypt?: EncryptSchemaTypeOptions; + } + + interface Validator { + message?: string | ((props: ValidatorProps) => string); + type?: string; + validator?: ValidatorFunction; + reason?: Error; + } + + type ValidatorFunction = (this: DocType, value: any, validatorProperties?: Validator) => any; + + interface QueryEncryptionEncryptOptions { + /** The id of the dataKey to use for encryption. Must be a BSON binary subtype 4 (UUID). */ + keyId: BSON.Binary; + + /** + * Specifies the type of queries that the field can be queried on the encrypted field. + */ + queries?: 'equality' | 'range'; + } + + interface ClientSideEncryptionEncryptOptions { + /** The id of the dataKey to use for encryption. Must be a BSON binary subtype 4 (UUID). */ + keyId: [BSON.Binary]; + + /** + * The algorithm to use for encryption. + */ + algorithm: 'AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic' | 'AEAD_AES_256_CBC_HMAC_SHA_512-Random'; + } + + export type EncryptSchemaTypeOptions = QueryEncryptionEncryptOptions | ClientSideEncryptionEncryptOptions; + + class SchemaType { + /** SchemaType constructor */ + constructor(path: string, options?: AnyObject, instance?: string); + + /** Get/set the function used to cast arbitrary values to this type. */ + static cast(caster?: Function | boolean): Function; + + static checkRequired(checkRequired?: (v: any) => boolean): (v: any) => boolean; + + /** Sets a default option for this schema type. */ + static set(option: string, value: any): void; + + /** Attaches a getter for all instances of this schema type. */ + static get(getter: (value: any) => any): void; + + /** Array containing default setters for all instances of this SchemaType */ + static setters: (( + val?: unknown, + priorVal?: unknown, + doc?: Document, + options?: Record | null + ) => unknown)[]; + + /** Contains the handlers for different query operators for this schema type. */ + $conditionalHandlers: { [op: string]: (val: any, context: any) => any }; + + /** The class that Mongoose uses internally to instantiate this SchemaType's `options` property. */ + OptionsConstructor: SchemaTypeOptions; + + /** Cast `val` to this schema type. Each class that inherits from schema type should implement this function. */ + cast(val: any, doc?: Document, init?: boolean, prev?: any, options?: any): any; + cast(val: any, doc?: Document, init?: boolean, prev?: any, options?: any): ResultType; + + /** Sets a default value for this SchemaType. */ + default(val: any): any; + + /** Adds a getter to this schematype. */ + get(fn: Function): this; + + /** Gets this SchemaType's embedded SchemaType, if any */ + getEmbeddedSchemaType(): SchemaType | undefined; + + /** + * Defines this path as immutable. Mongoose prevents you from changing + * immutable paths unless the parent document has [`isNew: true`](/docs/api/document.html#document_Document-isNew). + */ + immutable(bool: boolean): this; + + /** Declares the index options for this schematype. */ + index(options: any): this; + + /** String representation of what type this is, like 'ObjectID' or 'Number' */ + instance: string; + + /** True if this SchemaType has a required validator. False otherwise. */ + isRequired?: boolean; + + /** The options this SchemaType was instantiated with */ + options: AnyObject; + + /** The path to this SchemaType in a Schema. */ + path: string; + + /** + * Set the model that this path refers to. This is the option that [populate](https://mongoosejs.com/docs/populate.html) + * looks at to determine the foreign collection it should query. + */ + ref(ref: string | boolean | Model): this; + + /** + * Adds a required validator to this SchemaType. The validator gets added + * to the front of this SchemaType's validators array using unshift(). + */ + required(required: boolean, message?: string): this; + + /** If the SchemaType is a subdocument or document array, this is the schema of that subdocument */ + schema?: Schema; + + /** Sets default select() behavior for this path. */ + select(val: boolean): this; + + /** Adds a setter to this schematype. */ + set(fn: Function): this; + + /** Declares a sparse index. */ + sparse(bool: boolean): this; + + /** Declares a full text index. */ + text(bool: boolean): this; + + toJSONSchema(options?: { useBsonType?: boolean }): Record; + + /** Defines a custom function for transforming this path when converting a document to JSON. */ + transform(fn: (value: any) => any): this; + + /** Declares an unique index. */ + unique(bool: boolean): this; + + /** The validators that Mongoose should run to validate properties at this SchemaType's path. */ + validators: Validator[]; + + /** Adds validator(s) for this document path. */ + validate(obj: RegExp | ValidatorFunction | Validator, errorMsg?: string, type?: string): this; + + /** Adds multiple validators for this document path. */ + validateAll(validators: Array | Validator>): this; + + /** Default options for this SchemaType */ + static defaultOptions?: Record; + } + + namespace Schema { + namespace Types { + class Array extends SchemaType implements AcceptsDiscriminator { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Array'; + readonly schemaName: 'Array'; + + static options: { castNonArrays: boolean }; + + discriminator(name: string | number, schema: Schema, value?: string): U; + discriminator(name: string | number, schema: Schema, value?: string): Model; + + /** The schematype embedded in this array */ + embeddedSchemaType: SchemaType; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + + /** + * Adds an enum validator if this is an array of strings or numbers. Equivalent to + * `SchemaString.prototype.enum()` or `SchemaNumber.prototype.enum()` + */ + enum(vals: string[] | number[]): this; + } + + class BigInt extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'BigInt'; + readonly schemaName: 'BigInt'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Boolean extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Boolean'; + readonly schemaName: 'Boolean'; + + /** Configure which values get casted to `true`. */ + static convertToTrue: Set; + + /** Configure which values get casted to `false`. */ + static convertToFalse: Set; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Buffer extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Buffer'; + readonly schemaName: 'Buffer'; + + /** + * Sets the default [subtype](https://studio3t.com/whats-new/best-practices-uuid-mongodb/) + * for this buffer. You can find a [list of allowed subtypes here](http://api.mongodb.com/python/current/api/bson/binary.html). + */ + subtype(subtype: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 128): this; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Date extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Date'; + readonly schemaName: 'Date'; + + /** Declares a TTL index (rounded to the nearest second) for _Date_ types only. */ + expires(when: number | string): this; + + /** Sets a maximum date validator. */ + max(value: NativeDate, message?: string): this; + + /** Sets a minimum date validator. */ + min(value: NativeDate, message?: string): this; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Decimal128 extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Decimal128'; + readonly schemaName: 'Decimal128'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Int32 extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Int32'; + readonly schemaName: 'Int32'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class DocumentArray extends SchemaType implements AcceptsDiscriminator { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'DocumentArray'; + readonly schemaName: 'DocumentArray'; + + static options: { castNonArrays: boolean }; + + schemaOptions?: SchemaOptions; + + discriminator(name: string | number, schema: Schema, value?: string): Model; + discriminator(name: string | number, schema: Schema, value?: string): U; + + /** The schema used for documents in this array */ + schema: Schema; + + /** The schematype embedded in this array */ + embeddedSchemaType: Subdocument; + + /** The constructor used for subdocuments in this array */ + Constructor: typeof Types.Subdocument; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class DocumentArrayElement extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'DocumentArrayElement'; + readonly schemaName: 'DocumentArrayElement'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Map extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Map'; + readonly schemaName: 'Map'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Mixed extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Mixed'; + readonly schemaName: 'Mixed'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Number extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Number'; + readonly schemaName: 'Number'; + + /** Sets a enum validator */ + enum(vals: number[]): this; + + /** Sets a maximum number validator. */ + max(value: number, message?: string): this; + + /** Sets a minimum number validator. */ + min(value: number, message?: string): this; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Double extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'Double'; + readonly schemaName: 'Double'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class ObjectId extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'ObjectId'; + readonly schemaName: 'ObjectId'; + + /** Adds an auto-generated ObjectId default if turnOn is true. */ + auto(turnOn: boolean): this; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Subdocument extends SchemaType implements AcceptsDiscriminator { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: string; + + /** The document's schema */ + schema: Schema; + + /** The constructor used to create subdocuments based on this schematype */ + Constructor: typeof Types.Subdocument; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + + discriminator(name: string | number, schema: Schema, value?: string): U; + discriminator(name: string | number, schema: Schema, value?: string): Model; + + cast( + val: any, + doc?: Document, + init?: boolean, + prev?: any, + options?: any + ): HydratedSingleSubdocument; + } + + class String extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'String'; + readonly schemaName: 'String'; + + /** Adds an enum validator */ + enum(vals: string[] | any): this; + + /** Adds a lowercase [setter](http://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-set). */ + lowercase(shouldApply?: boolean): this; + + /** Sets a regexp validator. */ + match(value: RegExp, message: string): this; + + /** Sets a maximum length validator. */ + maxlength(value: number, message: string): this; + + /** Sets a minimum length validator. */ + minlength(value: number, message: string): this; + + /** Adds a trim [setter](http://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-set). */ + trim(shouldTrim?: boolean): this; + + /** Adds an uppercase [setter](http://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-set). */ + uppercase(shouldApply?: boolean): this; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class Union extends SchemaType { + static schemaName: 'Union'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + + class UUID extends SchemaType { + /** This schema type's name, to defend against minifiers that mangle function names. */ + static schemaName: 'UUID'; + readonly schemaName: 'UUID'; + + /** Default options for this SchemaType */ + static defaultOptions: Record; + } + } + } +} diff --git a/backend/node_modules/mongoose/types/session.d.ts b/backend/node_modules/mongoose/types/session.d.ts new file mode 100644 index 0000000..e7f41f7 --- /dev/null +++ b/backend/node_modules/mongoose/types/session.d.ts @@ -0,0 +1,32 @@ +declare module 'mongoose' { + import mongodb = require('mongodb'); + + type ClientSessionOptions = mongodb.ClientSessionOptions; + type ClientSession = mongodb.ClientSession; + + /** + * _Requires MongoDB >= 3.6.0._ Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + */ + function startSession(options?: ClientSessionOptions): Promise; + + interface SessionOperation { + /** Sets the session. Useful for [transactions](/docs/transactions.html). */ + session(session: mongodb.ClientSession | null): this; + } + + interface SessionStarter { + + /** + * Starts a [MongoDB session](https://www.mongodb.com/docs/manual/release-notes/3.6/#client-sessions) + * for benefits like causal consistency, [retryable writes](https://www.mongodb.com/docs/manual/core/retryable-writes/), + * and [transactions](http://thecodebarbarian.com/a-node-js-perspective-on-mongodb-4-transactions.html). + */ + startSession(options?: ClientSessionOptions): Promise; + } + + interface SessionOption { + session?: ClientSession | null; + } +} diff --git a/backend/node_modules/mongoose/types/types.d.ts b/backend/node_modules/mongoose/types/types.d.ts new file mode 100644 index 0000000..2a15ab9 --- /dev/null +++ b/backend/node_modules/mongoose/types/types.d.ts @@ -0,0 +1,109 @@ + +declare module 'mongoose' { + import mongodb = require('mongodb'); + + class NativeBuffer extends Buffer {} + + namespace Types { + class Array extends global.Array { + /** Pops the array atomically at most one time per document `save()`. */ + $pop(): T; + + /** Atomically shifts the array at most one time per document `save()`. */ + $shift(): T; + + /** Adds values to the array if not already present. */ + addToSet(...args: any[]): any[]; + + isMongooseArray: true; + + /** Pushes items to the array non-atomically. */ + nonAtomicPush(...args: any[]): number; + + /** Wraps [`Array#push`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/push) with proper change tracking. */ + push(...args: any[]): number; + + /** + * Pulls items from the array atomically. Equality is determined by casting + * the provided value to an embedded document and comparing using + * [the `Document.equals()` function.](./api/document.html#document_Document-equals) + */ + pull(...args: any[]): this; + + /** + * Alias of [pull](#mongoosearray_MongooseArray-pull) + */ + remove(...args: any[]): this; + + /** Sets the casted `val` at index `i` and marks the array modified. */ + set(index: number, val: T): this; + + /** Atomically shifts the array at most one time per document `save()`. */ + shift(): T; + + /** Returns a native js Array. */ + toObject(options?: ToObjectOptions): any; + toObject(options?: ToObjectOptions): T; + + /** Wraps [`Array#unshift`](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/unshift) with proper change tracking. */ + unshift(...args: any[]): number; + } + + class Buffer extends NativeBuffer { + /** Sets the subtype option and marks the buffer modified. */ + subtype(subtype: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 128 | ToObjectOptions): void; + + /** Converts this buffer to its Binary type representation. */ + toObject(subtype?: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 128): mongodb.Binary; + } + + class Decimal128 extends mongodb.Decimal128 { } + + class DocumentArray = Types.Subdocument, unknown, T> & T> extends Types.Array { + /** DocumentArray constructor */ + constructor(values: AnyObject[]); + + isMongooseDocumentArray: true; + + /** Creates a subdocument casted to this schema. */ + create(obj: any): THydratedDocumentType; + + /** Searches array items for the first document with a matching _id. */ + id(id: ObjectId | string | number | Buffer): THydratedDocumentType | null; + + push(...args: (AnyKeys & AnyObject)[]): number; + + splice(start: number, deleteCount?: number, ...args: (AnyKeys & AnyObject)[]): THydratedDocumentType[]; + } + + class Map extends global.Map { + /** Converts a Mongoose map into a vanilla JavaScript map. */ + toObject(options?: ToObjectOptions & { flattenMaps?: boolean }): any; + } + + class ObjectId extends mongodb.ObjectId { + } + + class Subdocument extends Document { + $isSingleNested: true; + + /** Returns the top level document of this sub-document. */ + ownerDocument(): Document; + + /** Returns this sub-documents parent document. */ + parent(): Document; + + /** Returns this sub-documents parent document. */ + $parent(): Document; + } + + class ArraySubdocument extends Subdocument { + /** Returns this sub-documents parent array. */ + parentArray(): Types.DocumentArray; + } + + class UUID extends mongodb.UUID {} + + class Double extends mongodb.Double {} + } +} diff --git a/backend/node_modules/mongoose/types/utility.d.ts b/backend/node_modules/mongoose/types/utility.d.ts new file mode 100644 index 0000000..e93f177 --- /dev/null +++ b/backend/node_modules/mongoose/types/utility.d.ts @@ -0,0 +1,175 @@ +declare module 'mongoose' { + type IfAny = 0 extends 1 & IFTYPE + ? THENTYPE + : ELSETYPE; + type IsUnknown = unknown extends T ? true : false; + + type IsNotNever = [T] extends [never] ? false : true; + type IsAny = 0 extends 1 & T ? true : false; + + type WithLevel1NestedPaths = IsItRecordAndNotAny extends true ? { + [P in K | NestedPaths, K>]: P extends K + // Handle top-level paths + // First, drill into documents so we don't end up surfacing `$assertPopulated`, etc. + ? Extract, Document> extends never + // If not a document, then return the type. Otherwise, get the DocType. + ? NonNullable + : Extract, Document> extends Document + ? DocType | Exclude, Document> + : never + // Handle nested paths + : P extends `${infer Key}.${infer Rest}` + ? Key extends keyof T + ? NonNullable extends (infer U)[] + ? NonNullable extends Types.DocumentArray + ? Rest extends keyof NonNullable + ? NonNullable[Rest] + : never + : Rest extends keyof NonNullable + ? NonNullable[Rest] + : never + : Rest extends keyof NonNullable + ? NonNullable[Rest] + : never + : never + : never; + } : T; + + type HasStringIndex = + string extends Extract ? true : false; + + type SafeObjectKeys = + HasStringIndex extends true ? never : Extract; + + type NestedPaths = + K extends string + ? T[K] extends TreatAsPrimitives + ? never + : Extract, Document> extends never + ? NonNullable extends Array + ? NonNullable extends Types.DocumentArray + ? SafeObjectKeys> extends never + ? never + : `${K}.${SafeObjectKeys>}` + : NonNullable extends Record + ? SafeObjectKeys> extends never + ? never + : `${K}.${SafeObjectKeys>}` + : never + : NonNullable extends object + ? SafeObjectKeys> extends never + ? never + : `${K}.${SafeObjectKeys>}` + : never + : Extract, Document> extends Document + ? DocType extends object + ? SafeObjectKeys> extends never + ? never + : `${K}.${SafeObjectKeys>}` + : never + : never + : never; + + type WithoutUndefined = T extends undefined ? never : T; + + /** + * @summary Removes keys from a type + * @description It helps to exclude keys from a type + * @param {T} T A generic type to be checked. + * @param {K} K Keys from T that are to be excluded from the generic type + * @returns T with the keys in K excluded + */ + type ExcludeKeys = { + [P in keyof T as P extends K ? never : P]: T[P]; + }; + + type Unpacked = T extends (infer U)[] + ? U + : T extends ReadonlyArray + ? U + : T; + + type DeepPartial = + T extends TreatAsPrimitives ? T : + T extends Array ? DeepPartial[] : + T extends Record ? { [K in keyof T]?: DeepPartial } : + T; + + type UnpackedIntersection = T extends null + ? null + : T extends (infer A)[] + ? (Omit & U)[] + : keyof U extends never + ? T + : Omit & U; + + type MergeType = A extends unknown ? Omit & B : never; + + /** + * @summary Converts Unions to one record "object". + * @description It makes intellisense dialog box easier to read as a single object instead of showing that in multiple object unions. + * @param {T} T The type to be converted. + */ + type FlatRecord = { [K in keyof T]: T[K] }; + + /** Force an operation like `{ a: 0 } & { b: 1 }` to be computed so that it displays `{ a: 0; b: 1 }`. */ + export type Show = { [k in keyof t]: t[k] } & unknown; + + /** + * @summary Checks if a type is "Record" or "any". + * @description It Helps to check if user has provided schema type "EnforcedDocType" + * @param {T} T A generic type to be checked. + * @returns true if {@link T} is Record OR false if {@link T} is of any type. + */ + type IsItRecordAndNotAny = IfEquals< + T, + any, + false, + T extends Record ? true : false + >; + + /** + * @summary Checks if two types are identical. + * @param {T} T The first type to be compared with {@link U}. + * @param {U} U The second type to be compared with {@link T}. + * @param {Y} Y A type to be returned if {@link T} & {@link U} are identical. + * @param {N} N A type to be returned if {@link T} & {@link U} are not identical. + */ + type IfEquals = (() => G extends T + ? 1 + : 0) extends () => G extends U ? 1 : 0 + ? Y + : N; + + /** + * @summary Extracts 'this' parameter from a function, if it exists. Otherwise, returns fallback. + * @param {T} T Function type to extract 'this' parameter from. + * @param {F} F Fallback type to return if 'this' parameter does not exist. + */ + type ThisParameter = T extends { (this: infer This, ...args: never): void } ? This : F; + + /** + * @summary Decorates all functions in an object with 'this' parameter. + * @param {T} T Object with functions as values to add 'D' parameter to as 'this'. {@link D} + * @param {D} D The type to be added as 'this' parameter to all functions in {@link T}. + */ + type AddThisParameter = { + [K in keyof T]: T[K] extends (...args: infer A) => infer R + ? IsUnknown> extends true + ? (this: D, ...args: A) => R + : T[K] + : T[K]; + }; + + /** + * @summary Adds timestamp fields to a type + * @description Adds createdAt and updatedAt fields of type Date, or custom timestamp fields if specified + * @param {T} T The type to add timestamp fields to + * @param {P} P Optional SchemaTimestampsConfig or boolean to customize timestamp field names + * @returns T with timestamp fields added + */ + export type WithTimestamps< + T, + P extends SchemaTimestampsConfig | boolean = true + > = ResolveTimestamps; +} diff --git a/backend/node_modules/mongoose/types/validation.d.ts b/backend/node_modules/mongoose/types/validation.d.ts new file mode 100644 index 0000000..1300afa --- /dev/null +++ b/backend/node_modules/mongoose/types/validation.d.ts @@ -0,0 +1,39 @@ +declare module 'mongoose' { + type SchemaValidator = RegExp | + [RegExp, string] | + Function | + [Function, string] | + ValidateOpts | + ValidateOpts[]; + + interface ValidatorProps { + path: string; + fullPath: string; + value: any; + reason?: Error; + } + + interface ValidatorMessageFn { + (props: ValidatorProps): string; + } + + type ValidateFn = ( + this: THydratedDocumentType | Query, + value: any, + props?: ValidatorProps & Record + ) => boolean; + + type AsyncValidateFn = ( + this: THydratedDocumentType | Query, + value: any, + props?: ValidatorProps & Record + ) => Promise; + + interface ValidateOpts { + msg?: string; + message?: string | ValidatorMessageFn; + type?: string; + validator: ValidateFn | AsyncValidateFn; + propsParameter?: boolean; + } +} diff --git a/backend/node_modules/mongoose/types/virtuals.d.ts b/backend/node_modules/mongoose/types/virtuals.d.ts new file mode 100644 index 0000000..de00830 --- /dev/null +++ b/backend/node_modules/mongoose/types/virtuals.d.ts @@ -0,0 +1,14 @@ +declare module 'mongoose' { + type VirtualPathFunctions = { + get?: TVirtualPathFN; + set?: TVirtualPathFN; + options?: VirtualTypeOptions; + }; + + type TVirtualPathFN = + (value: PathType, virtual: VirtualType, doc: THydratedDocumentType) => TReturn; + + type SchemaOptionsVirtualsPropertyType, THydratedDocumentType = any> = { + [K in keyof VirtualPaths]: VirtualPathFunctions + } & ThisType; +} diff --git a/backend/node_modules/mpath/.travis.yml b/backend/node_modules/mpath/.travis.yml new file mode 100644 index 0000000..9bdf212 --- /dev/null +++ b/backend/node_modules/mpath/.travis.yml @@ -0,0 +1,9 @@ +language: node_js +node_js: + - "4" + - "5" + - "6" + - "7" + - "8" + - "9" + - "10" diff --git a/backend/node_modules/mpath/History.md b/backend/node_modules/mpath/History.md new file mode 100644 index 0000000..5235d68 --- /dev/null +++ b/backend/node_modules/mpath/History.md @@ -0,0 +1,88 @@ +0.9.0 / 2022-04-17 +================== + * feat: export `stringToParts()` + +0.8.4 / 2021-09-01 +================== + * fix: throw error if `parts` contains an element that isn't a string or number #13 + +0.8.3 / 2020-12-30 +================== + * fix: use var instead of let/const for Node.js 4.x support + +0.8.2 / 2020-12-30 +================== + * fix(stringToParts): fall back to legacy treatment for square brackets if square brackets contents aren't a number Automattic/mongoose#9640 + * chore: add eslint + +0.8.1 / 2020-12-10 +================== + * fix(stringToParts): handle empty string and trailing dot the same way that `split()` does for backwards compat + +0.8.0 / 2020-11-14 +================== + * feat: support square bracket indexing for `get()`, `set()`, `has()`, and `unset()` + +0.7.0 / 2020-03-24 +================== + * BREAKING CHANGE: remove `component.json` #9 [AlexeyGrigorievBoost](https://github.com/AlexeyGrigorievBoost) + +0.6.0 / 2019-05-01 +================== + * feat: support setting dotted paths within nested arrays + +0.5.2 / 2019-04-25 +================== + * fix: avoid using subclassed array constructor when doing `map()` + +0.5.1 / 2018-08-30 +================== + * fix: prevent writing to constructor and prototype as well as __proto__ + +0.5.0 / 2018-08-30 +================== + * BREAKING CHANGE: disallow setting/unsetting __proto__ properties + * feat: re-add support for Node < 4 for this release + +0.4.1 / 2018-04-08 +================== + * fix: allow opting out of weird `$` set behavior re: Automattic/mongoose#6273 + +0.4.0 / 2018-03-27 +================== + * feat: add support for ES6 maps + * BREAKING CHANGE: drop support for Node < 4 + +0.3.0 / 2017-06-05 +================== + * feat: add has() and unset() functions + +0.2.1 / 2013-03-22 +================== + + * test; added for #5 + * fix typo that breaks set #5 [Contra](https://github.com/Contra) + +0.2.0 / 2013-03-15 +================== + + * added; adapter support for set + * added; adapter support for get + * add basic benchmarks + * add support for using module as a component #2 [Contra](https://github.com/Contra) + +0.1.1 / 2012-12-21 +================== + + * added; map support + +0.1.0 / 2012-12-13 +================== + + * added; set('array.property', val, object) support + * added; get('array.property', object) support + +0.0.1 / 2012-11-03 +================== + + * initial release diff --git a/backend/node_modules/mpath/LICENSE b/backend/node_modules/mpath/LICENSE new file mode 100644 index 0000000..38c529d --- /dev/null +++ b/backend/node_modules/mpath/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/mpath/README.md b/backend/node_modules/mpath/README.md new file mode 100644 index 0000000..9831dd0 --- /dev/null +++ b/backend/node_modules/mpath/README.md @@ -0,0 +1,278 @@ +#mpath + +{G,S}et javascript object values using MongoDB-like path notation. + +###Getting + +```js +var mpath = require('mpath'); + +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.get('comments.1.title', obj) // 'exciting!' +``` + +`mpath.get` supports array property notation as well. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.get('comments.title', obj) // ['funny', 'exciting!'] +``` + +Array property and indexing syntax, when used together, are very powerful. + +```js +var obj = { + array: [ + { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }} + , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: 'Turkey Day' }] }} + , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }} + , { o: { array: [{x: null }] }} + , { o: { array: [{y: 3 }] }} + , { o: { array: [3, 0, null] }} + , { o: { name: 'ha' }} + ]; +} + +var found = mpath.get('array.o.array.x.b.1', obj); + +console.log(found); // prints.. + + [ [6, undefined] + , [2, undefined, undefined] + , [null, 1] + , [null] + , [undefined] + , [undefined, undefined, undefined] + , undefined + ] + +``` + +#####Field selection rules: + +The following rules are iteratively applied to each `segment` in the passed `path`. For example: + +```js +var path = 'one.two.14'; // path +'one' // segment 0 +'two' // segment 1 +14 // segment 2 +``` + +- 1) when value of the segment parent is not an array, return the value of `parent.segment` +- 2) when value of the segment parent is an array + - a) if the segment is an integer, replace the parent array with the value at `parent[segment]` + - b) if not an integer, keep the array but replace each array `item` with the value returned from calling `get(remainingSegments, item)` or undefined if falsey. + +#####Maps + +`mpath.get` also accepts an optional `map` argument which receives each individual found value. The value returned from the `map` function will be used in the original found values place. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.get('comments.title', obj, function (val) { + return 'funny' == val + ? 'amusing' + : val; +}); +// ['amusing', 'exciting!'] +``` + +###Setting + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.set('comments.1.title', 'hilarious', obj) +console.log(obj.comments[1].title) // 'hilarious' +``` + +`mpath.set` supports the same array property notation as `mpath.get`. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.set('comments.title', ['hilarious', 'fruity'], obj); + +console.log(obj); // prints.. + + { comments: [ + { title: 'hilarious' }, + { title: 'fruity' } + ]} +``` + +Array property and indexing syntax can be used together also when setting. + +```js +var obj = { + array: [ + { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }} + , { o: { array: [{x: {b: [1,2,3]}}, { x: {z: 10 }}, { x: 'Turkey Day' }] }} + , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }} + , { o: { array: [{x: null }] }} + , { o: { array: [{y: 3 }] }} + , { o: { array: [3, 0, null] }} + , { o: { name: 'ha' }} + ] +} + +mpath.set('array.1.o', 'this was changed', obj); + +console.log(require('util').inspect(obj, false, 1000)); // prints.. + +{ + array: [ + { o: { array: [{x: {b: [4,6,8]}}, { y: 10} ] }} + , { o: 'this was changed' } + , { o: { array: [{x: {b: null }}, { x: { b: [null, 1]}}] }} + , { o: { array: [{x: null }] }} + , { o: { array: [{y: 3 }] }} + , { o: { array: [3, 0, null] }} + , { o: { name: 'ha' }} + ]; +} + +mpath.set('array.o.array.x', 'this was changed too', obj); + +console.log(require('util').inspect(obj, false, 1000)); // prints.. + +{ + array: [ + { o: { array: [{x: 'this was changed too'}, { y: 10, x: 'this was changed too'} ] }} + , { o: 'this was changed' } + , { o: { array: [{x: 'this was changed too'}, { x: 'this was changed too'}] }} + , { o: { array: [{x: 'this was changed too'}] }} + , { o: { array: [{x: 'this was changed too', y: 3 }] }} + , { o: { array: [3, 0, null] }} + , { o: { name: 'ha' }} + ]; +} +``` + +####Setting arrays + +By default, setting a property within an array to another array results in each element of the new array being set to the item in the destination array at the matching index. An example is helpful. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.set('comments.title', ['hilarious', 'fruity'], obj); + +console.log(obj); // prints.. + + { comments: [ + { title: 'hilarious' }, + { title: 'fruity' } + ]} +``` + +If we do not desire this destructuring-like assignment behavior we may instead specify the `$` operator in the path being set to force the array to be copied directly. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.set('comments.$.title', ['hilarious', 'fruity'], obj); + +console.log(obj); // prints.. + + { comments: [ + { title: ['hilarious', 'fruity'] }, + { title: ['hilarious', 'fruity'] } + ]} +``` + +####Field assignment rules + +The rules utilized mirror those used on `mpath.get`, meaning we can take values returned from `mpath.get`, update them, and reassign them using `mpath.set`. Note that setting nested arrays of arrays can get unweildy quickly. Check out the [tests](https://github.com/aheckmann/mpath/blob/master/test/index.js) for more extreme examples. + +#####Maps + +`mpath.set` also accepts an optional `map` argument which receives each individual value being set. The value returned from the `map` function will be used in the original values place. + +```js +var obj = { + comments: [ + { title: 'funny' }, + { title: 'exciting!' } + ] +} + +mpath.set('comments.title', ['hilarious', 'fruity'], obj, function (val) { + return val.length; +}); + +console.log(obj); // prints.. + + { comments: [ + { title: 9 }, + { title: 6 } + ]} +``` + +### Custom object types + +Sometimes you may want to enact the same functionality on custom object types that store all their real data internally, say for an ODM type object. No fear, `mpath` has you covered. Simply pass the name of the property being used to store the internal data and it will be traversed instead: + +```js +var mpath = require('mpath'); + +var obj = { + comments: [ + { title: 'exciting!', _doc: { title: 'great!' }} + ] +} + +mpath.get('comments.0.title', obj, '_doc') // 'great!' +mpath.set('comments.0.title', 'nov 3rd', obj, '_doc') +mpath.get('comments.0.title', obj, '_doc') // 'nov 3rd' +mpath.get('comments.0.title', obj) // 'exciting' +``` + +When used with a `map`, the `map` argument comes last. + +```js +mpath.get(path, obj, '_doc', map); +mpath.set(path, val, obj, '_doc', map); +``` + +[LICENSE](https://github.com/aheckmann/mpath/blob/master/LICENSE) + diff --git a/backend/node_modules/mpath/SECURITY.md b/backend/node_modules/mpath/SECURITY.md new file mode 100644 index 0000000..1c916a3 --- /dev/null +++ b/backend/node_modules/mpath/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting a Vulnerability + +Please report suspected security vulnerabilities to val [at] karpov [dot] io. +You will receive a response from us within 72 hours. +If the issue is confirmed, we will release a patch as soon as possible depending on complexity. diff --git a/backend/node_modules/mpath/index.js b/backend/node_modules/mpath/index.js new file mode 100644 index 0000000..47c17cd --- /dev/null +++ b/backend/node_modules/mpath/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = exports = require('./lib'); diff --git a/backend/node_modules/mpath/lib/index.js b/backend/node_modules/mpath/lib/index.js new file mode 100644 index 0000000..3f21cdc --- /dev/null +++ b/backend/node_modules/mpath/lib/index.js @@ -0,0 +1,336 @@ +/* eslint strict:off */ +/* eslint no-var: off */ +/* eslint no-redeclare: off */ + +var stringToParts = require('./stringToParts'); + +// These properties are special and can open client libraries to security +// issues +var ignoreProperties = ['__proto__', 'constructor', 'prototype']; + +/** + * Returns the value of object `o` at the given `path`. + * + * ####Example: + * + * var obj = { + * comments: [ + * { title: 'exciting!', _doc: { title: 'great!' }} + * , { title: 'number dos' } + * ] + * } + * + * mpath.get('comments.0.title', o) // 'exciting!' + * mpath.get('comments.0.title', o, '_doc') // 'great!' + * mpath.get('comments.title', o) // ['exciting!', 'number dos'] + * + * // summary + * mpath.get(path, o) + * mpath.get(path, o, special) + * mpath.get(path, o, map) + * mpath.get(path, o, special, map) + * + * @param {String} path + * @param {Object} o + * @param {String} [special] When this property name is present on any object in the path, walking will continue on the value of this property. + * @param {Function} [map] Optional function which receives each individual found value. The value returned from `map` is used in the original values place. + */ + +exports.get = function(path, o, special, map) { + var lookup; + + if ('function' == typeof special) { + if (special.length < 2) { + map = special; + special = undefined; + } else { + lookup = special; + special = undefined; + } + } + + map || (map = K); + + var parts = 'string' == typeof path + ? stringToParts(path) + : path; + + if (!Array.isArray(parts)) { + throw new TypeError('Invalid `path`. Must be either string or array'); + } + + var obj = o, + part; + + for (var i = 0; i < parts.length; ++i) { + part = parts[i]; + if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') { + throw new TypeError('Each segment of path to `get()` must be a string or number, got ' + typeof parts[i]); + } + + if (Array.isArray(obj) && !/^\d+$/.test(part)) { + // reading a property from the array items + var paths = parts.slice(i); + + // Need to `concat()` to avoid `map()` calling a constructor of an array + // subclass + return [].concat(obj).map(function(item) { + return item + ? exports.get(paths, item, special || lookup, map) + : map(undefined); + }); + } + + if (lookup) { + obj = lookup(obj, part); + } else { + var _from = special && obj[special] ? obj[special] : obj; + obj = _from instanceof Map ? + _from.get(part) : + _from[part]; + } + + if (!obj) return map(obj); + } + + return map(obj); +}; + +/** + * Returns true if `in` returns true for every piece of the path + * + * @param {String} path + * @param {Object} o + */ + +exports.has = function(path, o) { + var parts = typeof path === 'string' ? + stringToParts(path) : + path; + + if (!Array.isArray(parts)) { + throw new TypeError('Invalid `path`. Must be either string or array'); + } + + var len = parts.length; + var cur = o; + for (var i = 0; i < len; ++i) { + if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') { + throw new TypeError('Each segment of path to `has()` must be a string or number, got ' + typeof parts[i]); + } + if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) { + return false; + } + cur = cur[parts[i]]; + } + + return true; +}; + +/** + * Deletes the last piece of `path` + * + * @param {String} path + * @param {Object} o + */ + +exports.unset = function(path, o) { + var parts = typeof path === 'string' ? + stringToParts(path) : + path; + + if (!Array.isArray(parts)) { + throw new TypeError('Invalid `path`. Must be either string or array'); + } + + var len = parts.length; + var cur = o; + for (var i = 0; i < len; ++i) { + if (cur == null || typeof cur !== 'object' || !(parts[i] in cur)) { + return false; + } + if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') { + throw new TypeError('Each segment of path to `unset()` must be a string or number, got ' + typeof parts[i]); + } + // Disallow any updates to __proto__ or special properties. + if (ignoreProperties.indexOf(parts[i]) !== -1) { + return false; + } + if (i === len - 1) { + delete cur[parts[i]]; + return true; + } + cur = cur instanceof Map ? cur.get(parts[i]) : cur[parts[i]]; + } + + return true; +}; + +/** + * Sets the `val` at the given `path` of object `o`. + * + * @param {String} path + * @param {Anything} val + * @param {Object} o + * @param {String} [special] When this property name is present on any object in the path, walking will continue on the value of this property. + * @param {Function} [map] Optional function which is passed each individual value before setting it. The value returned from `map` is used in the original values place. + */ + +exports.set = function(path, val, o, special, map, _copying) { + var lookup; + + if ('function' == typeof special) { + if (special.length < 2) { + map = special; + special = undefined; + } else { + lookup = special; + special = undefined; + } + } + + map || (map = K); + + var parts = 'string' == typeof path + ? stringToParts(path) + : path; + + if (!Array.isArray(parts)) { + throw new TypeError('Invalid `path`. Must be either string or array'); + } + + if (null == o) return; + + for (var i = 0; i < parts.length; ++i) { + if (typeof parts[i] !== 'string' && typeof parts[i] !== 'number') { + throw new TypeError('Each segment of path to `set()` must be a string or number, got ' + typeof parts[i]); + } + // Silently ignore any updates to `__proto__`, these are potentially + // dangerous if using mpath with unsanitized data. + if (ignoreProperties.indexOf(parts[i]) !== -1) { + return; + } + } + + // the existance of $ in a path tells us if the user desires + // the copying of an array instead of setting each value of + // the array to the one by one to matching positions of the + // current array. Unless the user explicitly opted out by passing + // false, see Automattic/mongoose#6273 + var copy = _copying || (/\$/.test(path) && _copying !== false), + obj = o, + part; + + for (var i = 0, len = parts.length - 1; i < len; ++i) { + part = parts[i]; + + if ('$' == part) { + if (i == len - 1) { + break; + } else { + continue; + } + } + + if (Array.isArray(obj) && !/^\d+$/.test(part)) { + var paths = parts.slice(i); + if (!copy && Array.isArray(val)) { + for (var j = 0; j < obj.length && j < val.length; ++j) { + // assignment of single values of array + exports.set(paths, val[j], obj[j], special || lookup, map, copy); + } + } else { + for (var j = 0; j < obj.length; ++j) { + // assignment of entire value + exports.set(paths, val, obj[j], special || lookup, map, copy); + } + } + return; + } + + if (lookup) { + obj = lookup(obj, part); + } else { + var _to = special && obj[special] ? obj[special] : obj; + obj = _to instanceof Map ? + _to.get(part) : + _to[part]; + } + + if (!obj) return; + } + + // process the last property of the path + + part = parts[len]; + + // use the special property if exists + if (special && obj[special]) { + obj = obj[special]; + } + + // set the value on the last branch + if (Array.isArray(obj) && !/^\d+$/.test(part)) { + if (!copy && Array.isArray(val)) { + _setArray(obj, val, part, lookup, special, map); + } else { + for (var j = 0; j < obj.length; ++j) { + var item = obj[j]; + if (item) { + if (lookup) { + lookup(item, part, map(val)); + } else { + if (item[special]) item = item[special]; + item[part] = map(val); + } + } + } + } + } else { + if (lookup) { + lookup(obj, part, map(val)); + } else if (obj instanceof Map) { + obj.set(part, map(val)); + } else { + obj[part] = map(val); + } + } +}; + +/*! + * Split a string path into components delimited by '.' or + * '[\d+]' + * + * #### Example: + * stringToParts('foo[0].bar.1'); // ['foo', '0', 'bar', '1'] + */ + +exports.stringToParts = stringToParts; + +/*! + * Recursively set nested arrays + */ + +function _setArray(obj, val, part, lookup, special, map) { + for (var item, j = 0; j < obj.length && j < val.length; ++j) { + item = obj[j]; + if (Array.isArray(item) && Array.isArray(val[j])) { + _setArray(item, val[j], part, lookup, special, map); + } else if (item) { + if (lookup) { + lookup(item, part, map(val[j])); + } else { + if (item[special]) item = item[special]; + item[part] = map(val[j]); + } + } + } +} + +/*! + * Returns the value passed to it. + */ + +function K(v) { + return v; +} diff --git a/backend/node_modules/mpath/lib/stringToParts.js b/backend/node_modules/mpath/lib/stringToParts.js new file mode 100644 index 0000000..f70f333 --- /dev/null +++ b/backend/node_modules/mpath/lib/stringToParts.js @@ -0,0 +1,48 @@ +'use strict'; + +module.exports = function stringToParts(str) { + const result = []; + + let curPropertyName = ''; + let state = 'DEFAULT'; + for (let i = 0; i < str.length; ++i) { + // Fall back to treating as property name rather than bracket notation if + // square brackets contains something other than a number. + if (state === 'IN_SQUARE_BRACKETS' && !/\d/.test(str[i]) && str[i] !== ']') { + state = 'DEFAULT'; + curPropertyName = result[result.length - 1] + '[' + curPropertyName; + result.splice(result.length - 1, 1); + } + + if (str[i] === '[') { + if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') { + result.push(curPropertyName); + curPropertyName = ''; + } + state = 'IN_SQUARE_BRACKETS'; + } else if (str[i] === ']') { + if (state === 'IN_SQUARE_BRACKETS') { + state = 'IMMEDIATELY_AFTER_SQUARE_BRACKETS'; + result.push(curPropertyName); + curPropertyName = ''; + } else { + state = 'DEFAULT'; + curPropertyName += str[i]; + } + } else if (str[i] === '.') { + if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') { + result.push(curPropertyName); + curPropertyName = ''; + } + state = 'DEFAULT'; + } else { + curPropertyName += str[i]; + } + } + + if (state !== 'IMMEDIATELY_AFTER_SQUARE_BRACKETS') { + result.push(curPropertyName); + } + + return result; +}; \ No newline at end of file diff --git a/backend/node_modules/mpath/package.json b/backend/node_modules/mpath/package.json new file mode 100644 index 0000000..6d1242d --- /dev/null +++ b/backend/node_modules/mpath/package.json @@ -0,0 +1,144 @@ +{ + "name": "mpath", + "version": "0.9.0", + "description": "{G,S}et object values using MongoDB-like path notation", + "main": "index.js", + "scripts": { + "lint": "eslint .", + "test": "mocha test/*" + }, + "engines": { + "node": ">=4.0.0" + }, + "repository": "git://github.com/aheckmann/mpath.git", + "keywords": [ + "mongodb", + "path", + "get", + "set" + ], + "author": "Aaron Heckmann ", + "license": "MIT", + "devDependencies": { + "mocha": "5.x", + "benchmark": "~1.0.0", + "eslint": "7.16.0" + }, + "eslintConfig": { + "extends": [ + "eslint:recommended" + ], + "parserOptions": { + "ecmaVersion": 2015 + }, + "env": { + "node": true, + "es6": true + }, + "rules": { + "comma-style": "error", + "indent": [ + "error", + 2, + { + "SwitchCase": 1, + "VariableDeclarator": 2 + } + ], + "keyword-spacing": "error", + "no-whitespace-before-property": "error", + "no-buffer-constructor": "warn", + "no-console": "off", + "no-multi-spaces": "error", + "no-constant-condition": "off", + "func-call-spacing": "error", + "no-trailing-spaces": "error", + "no-undef": "error", + "no-unneeded-ternary": "error", + "no-const-assign": "error", + "no-useless-rename": "error", + "no-dupe-keys": "error", + "space-in-parens": [ + "error", + "never" + ], + "spaced-comment": [ + "error", + "always", + { + "block": { + "markers": [ + "!" + ], + "balanced": true + } + } + ], + "key-spacing": [ + "error", + { + "beforeColon": false, + "afterColon": true + } + ], + "comma-spacing": [ + "error", + { + "before": false, + "after": true + } + ], + "array-bracket-spacing": 1, + "arrow-spacing": [ + "error", + { + "before": true, + "after": true + } + ], + "object-curly-spacing": [ + "error", + "always" + ], + "comma-dangle": [ + "error", + "never" + ], + "no-unreachable": "error", + "quotes": [ + "error", + "single" + ], + "quote-props": [ + "error", + "as-needed" + ], + "semi": "error", + "no-extra-semi": "error", + "semi-spacing": "error", + "no-spaced-func": "error", + "no-throw-literal": "error", + "space-before-blocks": "error", + "space-before-function-paren": [ + "error", + "never" + ], + "space-infix-ops": "error", + "space-unary-ops": "error", + "no-var": "warn", + "prefer-const": "warn", + "strict": [ + "error", + "global" + ], + "no-restricted-globals": [ + "error", + { + "name": "context", + "message": "Don't use Mocha's global context" + } + ], + "no-prototype-builtins": "off" + } + } +} diff --git a/backend/node_modules/mpath/test/.eslintrc.yml b/backend/node_modules/mpath/test/.eslintrc.yml new file mode 100644 index 0000000..c0c6803 --- /dev/null +++ b/backend/node_modules/mpath/test/.eslintrc.yml @@ -0,0 +1,4 @@ +env: + mocha: true +rules: + no-unused-vars: off \ No newline at end of file diff --git a/backend/node_modules/mpath/test/index.js b/backend/node_modules/mpath/test/index.js new file mode 100644 index 0000000..ce07b19 --- /dev/null +++ b/backend/node_modules/mpath/test/index.js @@ -0,0 +1,1879 @@ +'use strict'; + +/** + * Test dependencies. + */ + +const mpath = require('../'); +const assert = require('assert'); + +/** + * logging helper + */ + +function log(o) { + console.log(); + console.log(require('util').inspect(o, false, 1000)); +} + +/** + * special path for override tests + */ + +const special = '_doc'; + +/** + * Tests + */ + +describe('mpath', function() { + + /** + * test doc creator + */ + + function doc() { + const o = { first: { second: { third: [3, { name: 'aaron' }, 9] } } }; + o.comments = [ + { name: 'one' }, + { name: 'two', _doc: { name: '2' } }, + { name: 'three', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: '3', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } } + ]; + o.name = 'jiro'; + o.array = [ + { o: { array: [{ x: { b: [4, 6, 8] } }, { y: 10 }] } }, + { o: { array: [{ x: { b: [1, 2, 3] } }, { x: { z: 10 } }, { x: { b: 'hi' } }] } }, + { o: { array: [{ x: { b: null } }, { x: { b: [null, 1] } }] } }, + { o: { array: [{ x: null }] } }, + { o: { array: [{ y: 3 }] } }, + { o: { array: [3, 0, null] } }, + { o: { name: 'ha' } } + ]; + o.arr = [ + { arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true } + ]; + return o; + } + + describe('get', function() { + const o = doc(); + + it('`path` must be a string or array', function(done) { + assert.throws(function() { + mpath.get({}, o); + }, /Must be either string or array/); + assert.throws(function() { + mpath.get(4, o); + }, /Must be either string or array/); + assert.throws(function() { + mpath.get(function() {}, o); + }, /Must be either string or array/); + assert.throws(function() { + mpath.get(/asdf/, o); + }, /Must be either string or array/); + assert.throws(function() { + mpath.get(Math, o); + }, /Must be either string or array/); + assert.throws(function() { + mpath.get(Buffer, o); + }, /Must be either string or array/); + assert.doesNotThrow(function() { + mpath.get('string', o); + }); + assert.doesNotThrow(function() { + mpath.get([], o); + }); + done(); + }); + + describe('without `special`', function() { + it('works', function(done) { + assert.equal('jiro', mpath.get('name', o)); + + assert.deepEqual( + { second: { third: [3, { name: 'aaron' }, 9] } } + , mpath.get('first', o) + ); + + assert.deepEqual( + { third: [3, { name: 'aaron' }, 9] } + , mpath.get('first.second', o) + ); + + assert.deepEqual( + [3, { name: 'aaron' }, 9] + , mpath.get('first.second.third', o) + ); + + assert.deepEqual( + 3 + , mpath.get('first.second.third.0', o) + ); + + assert.deepEqual( + 9 + , mpath.get('first.second.third.2', o) + ); + + assert.deepEqual( + { name: 'aaron' } + , mpath.get('first.second.third.1', o) + ); + + assert.deepEqual( + 'aaron' + , mpath.get('first.second.third.1.name', o) + ); + + assert.deepEqual([ + { name: 'one' }, + { name: 'two', _doc: { name: '2' } }, + { name: 'three', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: '3', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } }], + mpath.get('comments', o)); + + assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o)); + assert.deepEqual('one', mpath.get('comments.0.name', o)); + assert.deepEqual('two', mpath.get('comments.1.name', o)); + assert.deepEqual('three', mpath.get('comments.2.name', o)); + + assert.deepEqual([{}, { comments: [{ val: 'twoo' }] }] + , mpath.get('comments.2.comments', o)); + + assert.deepEqual({ comments: [{ val: 'twoo' }] } + , mpath.get('comments.2.comments.1', o)); + + assert.deepEqual('twoo', mpath.get('comments.2.comments.1.comments.0.val', o)); + + done(); + }); + + it('handles array.property dot-notation', function(done) { + assert.deepEqual( + ['one', 'two', 'three'] + , mpath.get('comments.name', o) + ); + done(); + }); + + it('handles array.array notation', function(done) { + assert.deepEqual( + [undefined, undefined, [{}, { comments: [{ val: 'twoo' }] }]] + , mpath.get('comments.comments', o) + ); + done(); + }); + + it('handles prop.prop.prop.arrayProperty notation', function(done) { + assert.deepEqual( + [undefined, 'aaron', undefined] + , mpath.get('first.second.third.name', o) + ); + assert.deepEqual( + [1, 'aaron', 1] + , mpath.get('first.second.third.name', o, function(v) { + return undefined === v ? 1 : v; + }) + ); + done(); + }); + + it('handles array.prop.array', function(done) { + assert.deepEqual( + [[{ x: { b: [4, 6, 8] } }, { y: 10 }], + [{ x: { b: [1, 2, 3] } }, { x: { z: 10 } }, { x: { b: 'hi' } }], + [{ x: { b: null } }, { x: { b: [null, 1] } }], + [{ x: null }], + [{ y: 3 }], + [3, 0, null], + undefined + ] + , mpath.get('array.o.array', o) + ); + done(); + }); + + it('handles array.prop.array.index', function(done) { + assert.deepEqual( + [{ x: { b: [4, 6, 8] } }, + { x: { b: [1, 2, 3] } }, + { x: { b: null } }, + { x: null }, + { y: 3 }, + 3, + undefined + ] + , mpath.get('array.o.array.0', o) + ); + done(); + }); + + it('handles array.prop.array.index.prop', function(done) { + assert.deepEqual( + [{ b: [4, 6, 8] }, + { b: [1, 2, 3] }, + { b: null }, + null, + undefined, + undefined, + undefined + ] + , mpath.get('array.o.array.0.x', o) + ); + done(); + }); + + it('handles array.prop.array.prop', function(done) { + assert.deepEqual( + [[undefined, 10], + [undefined, undefined, undefined], + [undefined, undefined], + [undefined], + [3], + [undefined, undefined, undefined], + undefined + ] + , mpath.get('array.o.array.y', o) + ); + assert.deepEqual( + [[{ b: [4, 6, 8] }, undefined], + [{ b: [1, 2, 3] }, { z: 10 }, { b: 'hi' }], + [{ b: null }, { b: [null, 1] }], + [null], + [undefined], + [undefined, undefined, undefined], + undefined + ] + , mpath.get('array.o.array.x', o) + ); + done(); + }); + + it('handles array.prop.array.prop.prop', function(done) { + assert.deepEqual( + [[[4, 6, 8], undefined], + [[1, 2, 3], undefined, 'hi'], + [null, [null, 1]], + [null], + [undefined], + [undefined, undefined, undefined], + undefined + ] + , mpath.get('array.o.array.x.b', o) + ); + done(); + }); + + it('handles array.prop.array.prop.prop.index', function(done) { + assert.deepEqual( + [[6, undefined], + [2, undefined, 'i'], // undocumented feature (string indexing) + [null, 1], + [null], + [undefined], + [undefined, undefined, undefined], + undefined + ] + , mpath.get('array.o.array.x.b.1', o) + ); + assert.deepEqual( + [[6, 0], + [2, 0, 'i'], // undocumented feature (string indexing) + [null, 1], + [null], + [0], + [0, 0, 0], + 0 + ] + , mpath.get('array.o.array.x.b.1', o, function(v) { + return undefined === v ? 0 : v; + }) + ); + done(); + }); + + it('handles array.index.prop.prop', function(done) { + assert.deepEqual( + [{ x: { b: [1, 2, 3] } }, { x: { z: 10 } }, { x: { b: 'hi' } }] + , mpath.get('array.1.o.array', o) + ); + assert.deepEqual( + ['hi', 'hi', 'hi'] + , mpath.get('array.1.o.array', o, function(v) { + if (Array.isArray(v)) { + return v.map(function(val) { + return 'hi'; + }); + } + return v; + }) + ); + done(); + }); + + it('handles array.array.index', function(done) { + assert.deepEqual( + [{ a: { c: 48 } }, undefined] + , mpath.get('arr.arr.1', o) + ); + assert.deepEqual( + ['woot', undefined] + , mpath.get('arr.arr.1', o, function(v) { + if (v && v.a && v.a.c) return 'woot'; + return v; + }) + ); + done(); + }); + + it('handles array.array.index.prop', function(done) { + assert.deepEqual( + [{ c: 48 }, 'woot'] + , mpath.get('arr.arr.1.a', o, function(v) { + if (undefined === v) return 'woot'; + return v; + }) + ); + assert.deepEqual( + [{ c: 48 }, undefined] + , mpath.get('arr.arr.1.a', o) + ); + mpath.set('arr.arr.1.a', [{ c: 49 }, undefined], o); + assert.deepEqual( + [{ c: 49 }, undefined] + , mpath.get('arr.arr.1.a', o) + ); + mpath.set('arr.arr.1.a', [{ c: 48 }, undefined], o); + done(); + }); + + it('handles array.array.index.prop.prop', function(done) { + assert.deepEqual( + [48, undefined] + , mpath.get('arr.arr.1.a.c', o) + ); + assert.deepEqual( + [48, 'woot'] + , mpath.get('arr.arr.1.a.c', o, function(v) { + if (undefined === v) return 'woot'; + return v; + }) + ); + done(); + }); + + }); + + describe('with `special`', function() { + describe('that is a string', function() { + it('works', function(done) { + assert.equal('jiro', mpath.get('name', o, special)); + + assert.deepEqual( + { second: { third: [3, { name: 'aaron' }, 9] } } + , mpath.get('first', o, special) + ); + + assert.deepEqual( + { third: [3, { name: 'aaron' }, 9] } + , mpath.get('first.second', o, special) + ); + + assert.deepEqual( + [3, { name: 'aaron' }, 9] + , mpath.get('first.second.third', o, special) + ); + + assert.deepEqual( + 3 + , mpath.get('first.second.third.0', o, special) + ); + + assert.deepEqual( + 4 + , mpath.get('first.second.third.0', o, special, function(v) { + return 3 === v ? 4 : v; + }) + ); + + assert.deepEqual( + 9 + , mpath.get('first.second.third.2', o, special) + ); + + assert.deepEqual( + { name: 'aaron' } + , mpath.get('first.second.third.1', o, special) + ); + + assert.deepEqual( + 'aaron' + , mpath.get('first.second.third.1.name', o, special) + ); + + assert.deepEqual([ + { name: 'one' }, + { name: 'two', _doc: { name: '2' } }, + { name: 'three', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: '3', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } }], + mpath.get('comments', o, special)); + + assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o, special)); + assert.deepEqual('one', mpath.get('comments.0.name', o, special)); + assert.deepEqual('2', mpath.get('comments.1.name', o, special)); + assert.deepEqual('3', mpath.get('comments.2.name', o, special)); + assert.deepEqual('nice', mpath.get('comments.2.name', o, special, function(v) { + return '3' === v ? 'nice' : v; + })); + + assert.deepEqual([{}, { _doc: { comments: [{ val: 2 }] } }] + , mpath.get('comments.2.comments', o, special)); + + assert.deepEqual({ _doc: { comments: [{ val: 2 }] } } + , mpath.get('comments.2.comments.1', o, special)); + + assert.deepEqual(2, mpath.get('comments.2.comments.1.comments.0.val', o, special)); + done(); + }); + + it('handles array.property dot-notation', function(done) { + assert.deepEqual( + ['one', '2', '3'] + , mpath.get('comments.name', o, special) + ); + assert.deepEqual( + ['one', 2, '3'] + , mpath.get('comments.name', o, special, function(v) { + return '2' === v ? 2 : v; + }) + ); + done(); + }); + + it('handles array.array notation', function(done) { + assert.deepEqual( + [undefined, undefined, [{}, { _doc: { comments: [{ val: 2 }] } }]] + , mpath.get('comments.comments', o, special) + ); + done(); + }); + + it('handles array.array.index.array', function(done) { + assert.deepEqual( + [undefined, undefined, [{ val: 2 }]] + , mpath.get('comments.comments.1.comments', o, special) + ); + done(); + }); + + it('handles array.array.index.array.prop', function(done) { + assert.deepEqual( + [undefined, undefined, [2]] + , mpath.get('comments.comments.1.comments.val', o, special) + ); + assert.deepEqual( + ['nil', 'nil', [2]] + , mpath.get('comments.comments.1.comments.val', o, special, function(v) { + return undefined === v ? 'nil' : v; + }) + ); + done(); + }); + }); + + describe('that is a function', function() { + const special = function(obj, key) { + return obj[key]; + }; + + it('works', function(done) { + assert.equal('jiro', mpath.get('name', o, special)); + + assert.deepEqual( + { second: { third: [3, { name: 'aaron' }, 9] } } + , mpath.get('first', o, special) + ); + + assert.deepEqual( + { third: [3, { name: 'aaron' }, 9] } + , mpath.get('first.second', o, special) + ); + + assert.deepEqual( + [3, { name: 'aaron' }, 9] + , mpath.get('first.second.third', o, special) + ); + + assert.deepEqual( + 3 + , mpath.get('first.second.third.0', o, special) + ); + + assert.deepEqual( + 4 + , mpath.get('first.second.third.0', o, special, function(v) { + return 3 === v ? 4 : v; + }) + ); + + assert.deepEqual( + 9 + , mpath.get('first.second.third.2', o, special) + ); + + assert.deepEqual( + { name: 'aaron' } + , mpath.get('first.second.third.1', o, special) + ); + + assert.deepEqual( + 'aaron' + , mpath.get('first.second.third.1.name', o, special) + ); + + assert.deepEqual([ + { name: 'one' }, + { name: 'two', _doc: { name: '2' } }, + { name: 'three', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: '3', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } }], + mpath.get('comments', o, special)); + + assert.deepEqual({ name: 'one' }, mpath.get('comments.0', o, special)); + assert.deepEqual('one', mpath.get('comments.0.name', o, special)); + assert.deepEqual('two', mpath.get('comments.1.name', o, special)); + assert.deepEqual('three', mpath.get('comments.2.name', o, special)); + assert.deepEqual('nice', mpath.get('comments.2.name', o, special, function(v) { + return 'three' === v ? 'nice' : v; + })); + + assert.deepEqual([{}, { comments: [{ val: 'twoo' }] }] + , mpath.get('comments.2.comments', o, special)); + + assert.deepEqual({ comments: [{ val: 'twoo' }] } + , mpath.get('comments.2.comments.1', o, special)); + + assert.deepEqual('twoo', mpath.get('comments.2.comments.1.comments.0.val', o, special)); + + let overide = false; + assert.deepEqual('twoo', mpath.get('comments.8.comments.1.comments.0.val', o, function(obj, path) { + if (Array.isArray(obj) && 8 == path) { + overide = true; + return obj[2]; + } + return obj[path]; + })); + assert.ok(overide); + + done(); + }); + + it('in combination with map', function(done) { + const special = function(obj, key) { + if (Array.isArray(obj)) return obj[key]; + return obj.mpath; + }; + const map = function(val) { + return 'convert' == val + ? 'mpath' + : val; + }; + const o = { mpath: [{ mpath: 'converse' }, { mpath: 'convert' }] }; + + assert.equal('mpath', mpath.get('something.1.kewl', o, special, map)); + done(); + }); + }); + }); + }); + + describe('set', function() { + it('prevents writing to __proto__', function() { + const obj = {}; + mpath.set('__proto__.x', 'foobar', obj); + assert.ok(!({}.x)); + + mpath.set('constructor.prototype.x', 'foobar', obj); + assert.ok(!({}.x)); + }); + + describe('without `special`', function() { + const o = doc(); + + it('works', function(done) { + mpath.set('name', 'a new val', o, function(v) { + return 'a new val' === v ? 'changed' : v; + }); + assert.deepEqual('changed', o.name); + + mpath.set('name', 'changed', o); + assert.deepEqual('changed', o.name); + + mpath.set('first.second.third', [1, { name: 'x' }, 9], o); + assert.deepEqual([1, { name: 'x' }, 9], o.first.second.third); + + mpath.set('first.second.third.1.name', 'y', o); + assert.deepEqual([1, { name: 'y' }, 9], o.first.second.third); + + mpath.set('comments.1.name', 'ttwwoo', o); + assert.deepEqual({ name: 'ttwwoo', _doc: { name: '2' } }, o.comments[1]); + + mpath.set('comments.2.comments.1.comments.0.expand', 'added', o); + assert.deepEqual( + { val: 'twoo', expand: 'added' } + , o.comments[2].comments[1].comments[0]); + + mpath.set('comments.2.comments.1.comments.2', 'added', o); + assert.equal(3, o.comments[2].comments[1].comments.length); + assert.deepEqual( + { val: 'twoo', expand: 'added' } + , o.comments[2].comments[1].comments[0]); + assert.deepEqual( + undefined + , o.comments[2].comments[1].comments[1]); + assert.deepEqual( + 'added' + , o.comments[2].comments[1].comments[2]); + + done(); + }); + + describe('array.path', function() { + describe('with single non-array value', function() { + it('works', function(done) { + mpath.set('arr.yep', false, o, function(v) { + return false === v ? true : v; + }); + assert.deepEqual([ + { yep: true, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true } + ], o.arr); + + mpath.set('arr.yep', false, o); + + assert.deepEqual([ + { yep: false, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: false } + ], o.arr); + + done(); + }); + }); + describe('with array of values', function() { + it('that are equal in length', function(done) { + mpath.set('arr.yep', ['one', 2], o, function(v) { + return 'one' === v ? 1 : v; + }); + assert.deepEqual([ + { yep: 1, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 2 } + ], o.arr); + mpath.set('arr.yep', ['one', 2], o); + + assert.deepEqual([ + { yep: 'one', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 2 } + ], o.arr); + + done(); + }); + + it('that is less than length', function(done) { + mpath.set('arr.yep', [47], o, function(v) { + return 47 === v ? 4 : v; + }); + assert.deepEqual([ + { yep: 4, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 2 } + ], o.arr); + + mpath.set('arr.yep', [47], o); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 2 } + ], o.arr); + + done(); + }); + + it('that is greater than length', function(done) { + mpath.set('arr.yep', [5, 6, 7], o, function(v) { + return 5 === v ? 'five' : v; + }); + assert.deepEqual([ + { yep: 'five', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 6 } + ], o.arr); + + mpath.set('arr.yep', [5, 6, 7], o); + assert.deepEqual([ + { yep: 5, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 6 } + ], o.arr); + + done(); + }); + }); + }); + + describe('array.$.path', function() { + describe('with single non-array value', function() { + it('copies the value to each item in array', function(done) { + mpath.set('arr.$.yep', { xtra: 'double good' }, o, function(v) { + return v && v.xtra ? 'hi' : v; + }); + assert.deepEqual([ + { yep: 'hi', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 'hi' } + ], o.arr); + + mpath.set('arr.$.yep', { xtra: 'double good' }, o); + assert.deepEqual([ + { yep: { xtra: 'double good' }, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: { xtra: 'double good' } } + ], o.arr); + + done(); + }); + }); + describe('with array of values', function() { + it('copies the value to each item in array', function(done) { + mpath.set('arr.$.yep', [15], o, function(v) { + return v.length === 1 ? [] : v; + }); + assert.deepEqual([ + { yep: [], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: [] } + ], o.arr); + + mpath.set('arr.$.yep', [15], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: [15] } + ], o.arr); + + done(); + }); + }); + }); + + describe('array.index.path', function() { + it('works', function(done) { + mpath.set('arr.1.yep', 0, o, function(v) { + return 0 === v ? 'zero' : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 'zero' } + ], o.arr); + + mpath.set('arr.1.yep', 0, o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.e', 35, o, function(v) { + return 35 === v ? 3 : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 }, e: 3 }, { a: { c: 48 }, e: 3 }, { d: 'yep', e: 3 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.e', 35, o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 }, e: 35 }, { a: { c: 48 }, e: 35 }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.e', ['a', 'b'], o, function(v) { + return 'a' === v ? 'x' : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 }, e: 'x' }, { a: { c: 48 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.e', ['a', 'b'], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 47 }, e: 'a' }, { a: { c: 48 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.path.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.a.b', 36, o, function(v) { + return 36 === v ? 3 : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 3 }, e: 'a' }, { a: { c: 48, b: 3 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.a.b', 36, o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 36 }, e: 'a' }, { a: { c: 48, b: 36 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.a.b', [1, 2, 3, 4], o, function(v) { + return 2 === v ? 'two' : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 1 }, e: 'a' }, { a: { c: 48, b: 'two' }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.a.b', [1, 2, 3, 4], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 1 }, e: 'a' }, { a: { c: 48, b: 2 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.$.path.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.$.a.b', '$', o, function(v) { + return '$' === v ? 'dolla billz' : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: 'dolla billz' }, e: 'a' }, { a: { c: 48, b: 'dolla billz' }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.$.a.b', '$', o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: '$' }, e: 'a' }, { a: { c: 48, b: '$' }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.$.a.b', [1], o, function(v) { + return Array.isArray(v) ? {} : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: {} }, e: 'a' }, { a: { c: 48, b: {} }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.0.arr.$.a.b', [1], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: { b: [1] }, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + }); + + describe('array.array.index.path', function() { + it('with single value', function(done) { + mpath.set('arr.arr.0.a', 'single', o, function(v) { + return 'single' === v ? 'double' : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: 'double', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.arr.0.a', 'single', o); + assert.deepEqual([ + { yep: [15], arr: [{ a: 'single', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.arr.0.a', [4, 8, 15, 16, 23, 42], o, function(v) { + return 4 === v ? 3 : v; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: 3, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: false } + ], o.arr); + + mpath.set('arr.arr.0.a', [4, 8, 15, 16, 23, 42], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: 4, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: false } + ], o.arr); + + done(); + }); + }); + + describe('array.array.$.index.path', function() { + it('with single value', function(done) { + mpath.set('arr.arr.$.0.a', 'singles', o, function(v) { + return 0; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: 0, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.arr.$.0.a', 'singles', o); + assert.deepEqual([ + { yep: [15], arr: [{ a: 'singles', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('$.arr.arr.0.a', 'single', o); + assert.deepEqual([ + { yep: [15], arr: [{ a: 'single', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.arr.$.0.a', [4, 8, 15, 16, 23, 42], o, function(v) { + return 'nope'; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: 'nope', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.arr.$.0.a', [4, 8, 15, 16, 23, 42], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42], e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.$.arr.0.a', [4, 8, 15, 16, 23, 42, 108], o); + assert.deepEqual([ + { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108], e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + }); + + describe('array.array.path.index', function() { + it('with single value', function(done) { + mpath.set('arr.arr.a.7', 47, o, function(v) { + return 1; + }); + assert.deepEqual([ + { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, 1], e: 'a' }, { a: { c: 48, b: [1], 7: 1 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + mpath.set('arr.arr.a.7', 47, o); + assert.deepEqual([ + { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, 47], e: 'a' }, { a: { c: 48, b: [1], 7: 47 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0 } + ], o.arr); + + done(); + }); + it('with array', function(done) { + o.arr[1].arr = [{ a: [] }, { a: [] }, { a: null }]; + mpath.set('arr.arr.a.7', [[null, 46], [undefined, 'woot']], o); + + const a1 = []; + const a2 = []; + a1[7] = undefined; + a2[7] = 'woot'; + + assert.deepEqual([ + { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, null], e: 'a' }, { a: { c: 48, b: [1], 7: 46 }, e: 'b' }, { d: 'yep', e: 35 }] }, + { yep: 0, arr: [{ a: a1 }, { a: a2 }, { a: null }] } + ], o.arr); + + done(); + }); + }); + + describe('handles array.array.path', function() { + it('with single', function(done) { + o.arr[1].arr = [{}, {}]; + assert.deepEqual([{}, {}], o.arr[1].arr); + o.arr.push({ arr: 'something else' }); + o.arr.push({ arr: ['something else'] }); + o.arr.push({ arr: [[]] }); + o.arr.push({ arr: [5] }); + + const weird = []; + weird.e = 'xmas'; + + // test + mpath.set('arr.arr.e', 47, o, function(v) { + return 'xmas'; + }); + assert.deepEqual([ + { yep: [15], arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 'xmas' }, + { a: { c: 48, b: [1], 7: 46 }, e: 'xmas' }, + { d: 'yep', e: 'xmas' } + ] + }, + { yep: 0, arr: [{ e: 'xmas' }, { e: 'xmas' }] }, + { arr: 'something else' }, + { arr: ['something else'] }, + { arr: [weird] }, + { arr: [5] } + ] + , o.arr); + + weird.e = 47; + + mpath.set('arr.arr.e', 47, o); + assert.deepEqual([ + { yep: [15], arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 47 }, + { a: { c: 48, b: [1], 7: 46 }, e: 47 }, + { d: 'yep', e: 47 } + ] + }, + { yep: 0, arr: [{ e: 47 }, { e: 47 }] }, + { arr: 'something else' }, + { arr: ['something else'] }, + { arr: [weird] }, + { arr: [5] } + ] + , o.arr); + + done(); + }); + it('with arrays', function(done) { + mpath.set('arr.arr.e', [[1, 2, 3], [4, 5], null, [], [6], [7, 8, 9]], o, function(v) { + return 10; + }); + + const weird = []; + weird.e = 10; + + assert.deepEqual([ + { yep: [15], arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 10 }, + { a: { c: 48, b: [1], 7: 46 }, e: 10 }, + { d: 'yep', e: 10 } + ] + }, + { yep: 0, arr: [{ e: 10 }, { e: 10 }] }, + { arr: 'something else' }, + { arr: ['something else'] }, + { arr: [weird] }, + { arr: [5] } + ] + , o.arr); + + mpath.set('arr.arr.e', [[1, 2, 3], [4, 5], null, [], [6], [7, 8, 9]], o); + + weird.e = 6; + + assert.deepEqual([ + { yep: [15], arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 1 }, + { a: { c: 48, b: [1], 7: 46 }, e: 2 }, + { d: 'yep', e: 3 } + ] + }, + { yep: 0, arr: [{ e: 4 }, { e: 5 }] }, + { arr: 'something else' }, + { arr: ['something else'] }, + { arr: [weird] }, + { arr: [5] } + ] + , o.arr); + + done(); + }); + }); + }); + + describe('with `special`', function() { + const o = doc(); + + it('works', function(done) { + mpath.set('name', 'chan', o, special, function(v) { + return 'hi'; + }); + assert.deepEqual('hi', o.name); + + mpath.set('name', 'changer', o, special); + assert.deepEqual('changer', o.name); + + mpath.set('first.second.third', [1, { name: 'y' }, 9], o, special); + assert.deepEqual([1, { name: 'y' }, 9], o.first.second.third); + + mpath.set('first.second.third.1.name', 'z', o, special); + assert.deepEqual([1, { name: 'z' }, 9], o.first.second.third); + + mpath.set('comments.1.name', 'ttwwoo', o, special); + assert.deepEqual({ name: 'two', _doc: { name: 'ttwwoo' } }, o.comments[1]); + + mpath.set('comments.2.comments.1.comments.0.expander', 'adder', o, special, function(v) { + return 'super'; + }); + assert.deepEqual( + { val: 2, expander: 'super' } + , o.comments[2]._doc.comments[1]._doc.comments[0]); + + mpath.set('comments.2.comments.1.comments.0.expander', 'adder', o, special); + assert.deepEqual( + { val: 2, expander: 'adder' } + , o.comments[2]._doc.comments[1]._doc.comments[0]); + + mpath.set('comments.2.comments.1.comments.2', 'set', o, special); + assert.equal(3, o.comments[2]._doc.comments[1]._doc.comments.length); + assert.deepEqual( + { val: 2, expander: 'adder' } + , o.comments[2]._doc.comments[1]._doc.comments[0]); + assert.deepEqual( + undefined + , o.comments[2]._doc.comments[1]._doc.comments[1]); + assert.deepEqual( + 'set' + , o.comments[2]._doc.comments[1]._doc.comments[2]); + done(); + }); + + describe('array.path', function() { + describe('with single non-array value', function() { + it('works', function(done) { + o.arr[1]._doc = { special: true }; + + mpath.set('arr.yep', false, o, special, function(v) { + return 'yes'; + }); + assert.deepEqual([ + { yep: 'yes', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: 'yes' } } + ], o.arr); + + mpath.set('arr.yep', false, o, special); + assert.deepEqual([ + { yep: false, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: false } } + ], o.arr); + + done(); + }); + }); + describe('with array of values', function() { + it('that are equal in length', function(done) { + mpath.set('arr.yep', ['one', 2], o, special, function(v) { + return 2 === v ? 20 : v; + }); + assert.deepEqual([ + { yep: 'one', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: 20 } } + ], o.arr); + + mpath.set('arr.yep', ['one', 2], o, special); + assert.deepEqual([ + { yep: 'one', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: 2 } } + ], o.arr); + + done(); + }); + + it('that is less than length', function(done) { + mpath.set('arr.yep', [47], o, special, function(v) { + return 80; + }); + assert.deepEqual([ + { yep: 80, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: 2 } } + ], o.arr); + + mpath.set('arr.yep', [47], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }, + { yep: true, _doc: { special: true, yep: 2 } } + ], o.arr); + + // add _doc to first element + o.arr[0]._doc = { yep: 46, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] }; + + mpath.set('arr.yep', [20], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], _doc: { yep: 20, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 2 } } + ], o.arr); + + done(); + }); + + it('that is greater than length', function(done) { + mpath.set('arr.yep', [5, 6, 7], o, special, function() { + return 'x'; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], _doc: { yep: 'x', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 'x' } } + ], o.arr); + + mpath.set('arr.yep', [5, 6, 7], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], _doc: { yep: 5, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 6 } } + ], o.arr); + + done(); + }); + }); + }); + + describe('array.$.path', function() { + describe('with single non-array value', function() { + it('copies the value to each item in array', function(done) { + mpath.set('arr.$.yep', { xtra: 'double good' }, o, special, function(v) { + return 9; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: 9, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 9 } } + ], o.arr); + + mpath.set('arr.$.yep', { xtra: 'double good' }, o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: { xtra: 'double good' }, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: { xtra: 'double good' } } } + ], o.arr); + + done(); + }); + }); + describe('with array of values', function() { + it('copies the value to each item in array', function(done) { + mpath.set('arr.$.yep', [15], o, special, function(v) { + return 'array'; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: 'array', arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 'array' } } + ], o.arr); + + mpath.set('arr.$.yep', [15], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: [15] } } + ], o.arr); + + done(); + }); + }); + }); + + describe('array.index.path', function() { + it('works', function(done) { + mpath.set('arr.1.yep', 0, o, special, function(v) { + return 1; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 1 } } + ], o.arr); + + mpath.set('arr.1.yep', 0, o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.e', 35, o, special, function(v) { + return 30; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 30 }, { a: { c: 48 }, e: 30 }, { d: 'yep', e: 30 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.e', 35, o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 35 }, { a: { c: 48 }, e: 35 }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.e', ['a', 'b'], o, special, function(v) { + return 'a' === v ? 'A' : v; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 'A' }, { a: { c: 48 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.e', ['a', 'b'], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 47 }, e: 'a' }, { a: { c: 48 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.path.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.a.b', 36, o, special, function(v) { + return 20; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 20 }, e: 'a' }, { a: { c: 48, b: 20 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.a.b', 36, o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 36 }, e: 'a' }, { a: { c: 48, b: 36 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.a.b', [1, 2, 3, 4], o, special, function(v) { + return v * 2; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 2 }, e: 'a' }, { a: { c: 48, b: 4 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.a.b', [1, 2, 3, 4], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 1 }, e: 'a' }, { a: { c: 48, b: 2 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.index.array.$.path.path', function() { + it('with single value', function(done) { + mpath.set('arr.0.arr.$.a.b', '$', o, special, function(v) { + return 'dollaz'; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: 'dollaz' }, e: 'a' }, { a: { c: 48, b: 'dollaz' }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.$.a.b', '$', o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: '$' }, e: 'a' }, { a: { c: 48, b: '$' }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.0.arr.$.a.b', [1], o, special, function(v) { + return {}; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: {} }, e: 'a' }, { a: { c: 48, b: {} }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.0.arr.$.a.b', [1], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: { b: [1] }, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.array.index.path', function() { + it('with single value', function(done) { + mpath.set('arr.arr.0.a', 'single', o, special, function(v) { + return 88; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 88, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.0.a', 'single', o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 'single', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.arr.0.a', [4, 8, 15, 16, 23, 42], o, special, function(v) { + return v * 2; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 8, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.0.a', [4, 8, 15, 16, 23, 42], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 4, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.array.$.index.path', function() { + it('with single value', function(done) { + mpath.set('arr.arr.$.0.a', 'singles', o, special, function(v) { + return v.toUpperCase(); + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 'SINGLES', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.$.0.a', 'singles', o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 'singles', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('$.arr.arr.0.a', 'single', o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: 'single', e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + mpath.set('arr.arr.$.0.a', [4, 8, 15, 16, 23, 42], o, special, function(v) { + return Array; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: Array, e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.$.0.a', [4, 8, 15, 16, 23, 42], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42], e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.$.arr.0.a', [4, 8, 15, 16, 23, 42, 108], o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108], e: 'a' }, { a: { c: 48, b: [1] }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('array.array.path.index', function() { + it('with single value', function(done) { + mpath.set('arr.arr.a.7', 47, o, special, function(v) { + return Object; + }); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, Object], e: 'a' }, { a: { c: 48, b: [1], 7: Object }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.a.7', 47, o, special); + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, 47], e: 'a' }, { a: { c: 48, b: [1], 7: 47 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { special: true, yep: 0 } } + ], o.arr); + + done(); + }); + it('with array', function(done) { + o.arr[1]._doc.arr = [{ a: [] }, { a: [] }, { a: null }]; + mpath.set('arr.arr.a.7', [[null, 46], [undefined, 'woot']], o, special, function(v) { + return undefined === v ? 'nope' : v; + }); + + const a1 = []; + const a2 = []; + a1[7] = 'nope'; + a2[7] = 'woot'; + + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, null], e: 'a' }, { a: { c: 48, b: [1], 7: 46 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { arr: [{ a: a1 }, { a: a2 }, { a: null }], special: true, yep: 0 } } + ], o.arr); + + mpath.set('arr.arr.a.7', [[null, 46], [undefined, 'woot']], o, special); + + a1[7] = undefined; + + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { yep: [15], arr: [{ a: [4, 8, 15, 16, 23, 42, 108, null], e: 'a' }, { a: { c: 48, b: [1], 7: 46 }, e: 'b' }, { d: 'yep', e: 35 }] } }, + { yep: true, _doc: { arr: [{ a: a1 }, { a: a2 }, { a: null }], special: true, yep: 0 } } + ], o.arr); + + done(); + }); + }); + + describe('handles array.array.path', function() { + it('with single', function(done) { + o.arr[1]._doc.arr = [{}, {}]; + assert.deepEqual([{}, {}], o.arr[1]._doc.arr); + o.arr.push({ _doc: { arr: 'something else' } }); + o.arr.push({ _doc: { arr: ['something else'] } }); + o.arr.push({ _doc: { arr: [[]] } }); + o.arr.push({ _doc: { arr: [5] } }); + + // test + mpath.set('arr.arr.e', 47, o, special); + + const weird = []; + weird.e = 47; + + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { + yep: [15], + arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 47 }, + { a: { c: 48, b: [1], 7: 46 }, e: 47 }, + { d: 'yep', e: 47 } + ] + } + }, + { yep: true, + _doc: { + arr: [ + { e: 47 }, + { e: 47 } + ], + special: true, + yep: 0 + } + }, + { _doc: { arr: 'something else' } }, + { _doc: { arr: ['something else'] } }, + { _doc: { arr: [weird] } }, + { _doc: { arr: [5] } } + ] + , o.arr); + + done(); + }); + it('with arrays', function(done) { + mpath.set('arr.arr.e', [[1, 2, 3], [4, 5], null, [], [6], [7, 8, 9]], o, special); + + const weird = []; + weird.e = 6; + + assert.deepEqual([ + { yep: 47, arr: [{ a: { b: 47 } }, { a: { c: 48 } }, { d: 'yep' }], + _doc: { + yep: [15], + arr: [ + { a: [4, 8, 15, 16, 23, 42, 108, null], e: 1 }, + { a: { c: 48, b: [1], 7: 46 }, e: 2 }, + { d: 'yep', e: 3 } + ] + } + }, + { yep: true, + _doc: { + arr: [ + { e: 4 }, + { e: 5 } + ], + special: true, + yep: 0 + } + }, + { _doc: { arr: 'something else' } }, + { _doc: { arr: ['something else'] } }, + { _doc: { arr: [weird] } }, + { _doc: { arr: [5] } } + ] + , o.arr); + + done(); + }); + }); + + describe('that is a function', function() { + describe('without map', function() { + it('works on array value', function(done) { + const o = { hello: { world: [{ how: 'are' }, { you: '?' }] } }; + const special = function(obj, key, val) { + if (val) { + obj[key] = val; + } else { + return 'thing' == key + ? obj.world + : obj[key]; + } + }; + mpath.set('hello.thing.how', 'arrrr', o, special); + assert.deepEqual(o, { hello: { world: [{ how: 'arrrr' }, { you: '?', how: 'arrrr' }] } }); + done(); + }); + it('works on non-array value', function(done) { + const o = { hello: { world: { how: 'are you' } } }; + const special = function(obj, key, val) { + if (val) { + obj[key] = val; + } else { + return 'thing' == key + ? obj.world + : obj[key]; + } + }; + mpath.set('hello.thing.how', 'RU', o, special); + assert.deepEqual(o, { hello: { world: { how: 'RU' } } }); + done(); + }); + }); + it('works with map', function(done) { + const o = { hello: { world: [{ how: 'are' }, { you: '?' }] } }; + const special = function(obj, key, val) { + if (val) { + obj[key] = val; + } else { + return 'thing' == key + ? obj.world + : obj[key]; + } + }; + const map = function(val) { + return 'convert' == val + ? 'ºº' + : val; + }; + mpath.set('hello.thing.how', 'convert', o, special, map); + assert.deepEqual(o, { hello: { world: [{ how: 'ºº' }, { you: '?', how: 'ºº' }] } }); + done(); + }); + }); + + }); + + describe('get/set integration', function() { + const o = doc(); + + it('works', function(done) { + const vals = mpath.get('array.o.array.x.b', o); + + vals[0][0][2] = 10; + vals[1][0][1] = 0; + vals[1][1] = 'Rambaldi'; + vals[1][2] = [12, 14]; + vals[2] = [{ changed: true }, [null, ['changed', 'to', 'array']]]; + + mpath.set('array.o.array.x.b', vals, o); + + const t = [ + { o: { array: [{ x: { b: [4, 6, 10] } }, { y: 10 }] } }, + { o: { array: [{ x: { b: [1, 0, 3] } }, { x: { b: 'Rambaldi', z: 10 } }, { x: { b: [12, 14] } }] } }, + { o: { array: [{ x: { b: { changed: true } } }, { x: { b: [null, ['changed', 'to', 'array']] } }] } }, + { o: { array: [{ x: null }] } }, + { o: { array: [{ y: 3 }] } }, + { o: { array: [3, 0, null] } }, + { o: { name: 'ha' } } + ]; + assert.deepEqual(t, o.array); + done(); + }); + + it('array.prop', function(done) { + mpath.set('comments.name', ['this', 'was', 'changed'], o); + + assert.deepEqual([ + { name: 'this' }, + { name: 'was', _doc: { name: '2' } }, + { name: 'changed', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: '3', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } } + ], o.comments); + + mpath.set('comments.name', ['also', 'changed', 'this'], o, special); + + assert.deepEqual([ + { name: 'also' }, + { name: 'was', _doc: { name: 'changed' } }, + { name: 'changed', + comments: [{}, { comments: [{ val: 'twoo' }] }], + _doc: { name: 'this', comments: [{}, { _doc: { comments: [{ val: 2 }] } }] } } + ], o.comments); + + done(); + }); + + it('nested array', function(done) { + const obj = { arr: [[{ test: 41 }]] }; + mpath.set('arr.test', [[42]], obj); + assert.deepEqual(obj.arr, [[{ test: 42 }]]); + done(); + }); + }); + + describe('multiple $ use', function() { + const o = doc(); + it('is ok', function(done) { + assert.doesNotThrow(function() { + mpath.set('arr.$.arr.$.a', 35, o); + }); + done(); + }); + }); + + it('has', function(done) { + assert.ok(mpath.has('a', { a: 1 })); + assert.ok(mpath.has('a', { a: undefined })); + assert.ok(!mpath.has('a', {})); + assert.ok(!mpath.has('a', null)); + + assert.ok(mpath.has('a.b', { a: { b: 1 } })); + assert.ok(mpath.has('a.b', { a: { b: undefined } })); + assert.ok(!mpath.has('a.b', { a: 1 })); + assert.ok(!mpath.has('a.b', { a: null })); + + done(); + }); + + it('underneath a map', function(done) { + if (!global.Map) { + done(); + return; + } + assert.equal(mpath.get('a.b', { a: new Map([['b', 1]]) }), 1); + + const m = new Map([['b', 1]]); + const obj = { a: m }; + mpath.set('a.c', 2, obj); + assert.equal(m.get('c'), 2); + + done(); + }); + + it('unset', function(done) { + let o = { a: 1 }; + mpath.unset('a', o); + assert.deepEqual(o, {}); + + o = { a: { b: 1 } }; + mpath.unset('a.b', o); + assert.deepEqual(o, { a: {} }); + + o = { a: null }; + mpath.unset('a.b', o); + assert.deepEqual(o, { a: null }); + + done(); + }); + + it('unset with __proto__', function(done) { + // Should refuse to set __proto__ + function Clazz() {} + Clazz.prototype.foobar = true; + + mpath.unset('__proto__.foobar', new Clazz()); + assert.ok(Clazz.prototype.foobar); + + mpath.unset('constructor.prototype.foobar', new Clazz()); + assert.ok(Clazz.prototype.foobar); + + done(); + }); + + it('get() underneath subclassed array', function(done) { + class MyArray extends Array {} + + const obj = { + arr: new MyArray() + }; + obj.arr.push({ test: 2 }); + + const arr = mpath.get('arr.test', obj); + assert.equal(arr.constructor.name, 'Array'); + assert.ok(!(arr instanceof MyArray)); + + done(); + }); + + it('ignores setting a nested path that doesnt exist', function(done) { + const o = doc(); + assert.doesNotThrow(function() { + mpath.set('thing.that.is.new', 10, o); + }); + done(); + }); + }); +}); diff --git a/backend/node_modules/mpath/test/stringToParts.js b/backend/node_modules/mpath/test/stringToParts.js new file mode 100644 index 0000000..09940df --- /dev/null +++ b/backend/node_modules/mpath/test/stringToParts.js @@ -0,0 +1,30 @@ +'use strict'; + +const assert = require('assert'); +const stringToParts = require('../lib/stringToParts'); + +describe('stringToParts', function() { + it('handles brackets for numbers', function() { + assert.deepEqual(stringToParts('list[0].name'), ['list', '0', 'name']); + assert.deepEqual(stringToParts('list[0][1].name'), ['list', '0', '1', 'name']); + }); + + it('handles dot notation', function() { + assert.deepEqual(stringToParts('a.b.c'), ['a', 'b', 'c']); + assert.deepEqual(stringToParts('a..b.d'), ['a', '', 'b', 'd']); + }); + + it('ignores invalid numbers in square brackets', function() { + assert.deepEqual(stringToParts('foo[1mystring]'), ['foo[1mystring]']); + assert.deepEqual(stringToParts('foo[1mystring].bar[1]'), ['foo[1mystring]', 'bar', '1']); + assert.deepEqual(stringToParts('foo[1mystring][2]'), ['foo[1mystring]', '2']); + }); + + it('handles empty string', function() { + assert.deepEqual(stringToParts(''), ['']); + }); + + it('handles trailing dot', function() { + assert.deepEqual(stringToParts('a.b.'), ['a', 'b', '']); + }); +}); \ No newline at end of file diff --git a/backend/node_modules/mquery/.github/ISSUE_TEMPLATE.md b/backend/node_modules/mquery/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..20412e6 --- /dev/null +++ b/backend/node_modules/mquery/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,11 @@ + + +**Do you want to request a *feature* or report a *bug*?** + +**What is the current behavior?** + +**If the current behavior is a bug, please provide the steps to reproduce.** + +**What is the expected behavior?** + +**What are the versions of Node.js and mquery are you are using? Note that "latest" is not a version.** diff --git a/backend/node_modules/mquery/.github/PULL_REQUEST_TEMPLATE.md b/backend/node_modules/mquery/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..eb1a69d --- /dev/null +++ b/backend/node_modules/mquery/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,9 @@ + + +**Summary** + + + +**Examples** + + diff --git a/backend/node_modules/mquery/History.md b/backend/node_modules/mquery/History.md new file mode 100644 index 0000000..4aac5e3 --- /dev/null +++ b/backend/node_modules/mquery/History.md @@ -0,0 +1,393 @@ +6.0.0 / 2025-11-18 +================== + * BREAKING CHANGE: Make calling updateOne(), updateMany(), and findOneAndX() with one arg set the query filter rather than the update to line up with modern expectations of optional arguments + * BREAKING CHANGE: remove debug dependency, use node debuglog instead + * BREAKING CHANGE: remove count() and findOneAndRemove(): use countDocuments() and findOneAndDelete() instead + * BREAKING CHANGE: require Node >= 20.19.0 + * feat: add findOneAndDelete, findOneAndReplace, countDocuments, estimatedDocumentCount + * feat: use mongodb driver 6 in tests + +5.0.0 / 2023-02-23 +================== + * BREAKING CHANGE: drop callback support #137 [hasezoey](https://github.com/hasezoey) + * BREAKING CHANGE: remove custom promise library support #137 [hasezoey](https://github.com/hasezoey) + * BREAKING CHANGE: remove long deprecated `update`, `remove` functions #136 [hasezoey](https://github.com/hasezoey) + * BREAKING CHANGE: remove collection ducktyping: first param to `mquery()` is now always the query filter #138 + * feat: support MongoDB Node driver 5 #137 [hasezoey](https://github.com/hasezoey) + +4.0.3 / 2022-05-17 +================== + * fix: allow using `comment` with `findOneAndUpdate()`, `count()`, `distinct()` and `hint` with `findOneAndUpdate()` Automattic/mongoose#11793 + +4.0.2 / 2022-01-23 +================== + * perf: replace regexp-clone with native functionality #131 [Uzlopak](https://github.com/Uzlopak) + +4.0.1 / 2022-01-20 +================== + * perf: remove sliced, add various microoptimizations #130 [Uzlopak](https://github.com/Uzlopak) + * refactor: convert NodeCollection to a class #128 [jimmywarting](https://github.com/jimmywarting) + +4.0.0 / 2021-08-24 + +4.0.0-rc0 / 2021-08-19 +====================== + * BREAKING CHANGE: drop support for Node < 12 #123 + * BREAKING CHANGE: upgrade to mongodb driver 4.x: drop support for `findAndModify()`, use native `findOneAndUpdate/Delete` #124 + * BREAKING CHANGE: rename findStream -> findCursor #124 + * BREAKING CHANGE: use native ES6 promises by default, remove bluebird dependency #123 + +3.2.5 / 2021-03-29 +================== + * fix(utils): make `mergeClone()` skip special properties like `__proto__` #121 [zpbrent](https://github.com/zpbrent) + +3.2.4 / 2021-02-12 +================== + * fix(utils): make clone() only copy own properties Automattic/mongoose#9876 + +3.2.3 / 2020-12-10 +================== + * fix(utils): avoid copying special properties like `__proto__` when merging and cloning. Fix CVE-2020-35149 + +3.2.2 / 2019-09-22 +================== + * fix: dont re-call setOptions() when pulling base class options Automattic/mongoose#8159 + +3.2.1 / 2018-08-24 +================== + * chore: upgrade deps + +3.2.0 / 2018-08-24 +================== + * feat: add $useProjection to opt in to using `projection` instead of `fields` re: MongoDB deprecation warnings Automattic/mongoose#6880 + +3.1.2 / 2018-08-01 +================== + * chore: move eslint to devDependencies #110 [jakesjews](https://github.com/jakesjews) + +3.1.1 / 2018-07-30 +================== + * chore: add eslint #107 [Fonger](https://github.com/Fonger) + * docs: clean up readConcern docs #106 [Fonger](https://github.com/Fonger) + +3.1.0 / 2018-07-29 +================== + * feat: add `readConcern()` helper #105 [Fonger](https://github.com/Fonger) + * feat: add `maxTimeMS()` as alias of `maxTime()` #105 [Fonger](https://github.com/Fonger) + * feat: add `collation()` helper #105 [Fonger](https://github.com/Fonger) + +3.0.1 / 2018-07-02 +================== + * fix: parse sort array options correctly #103 #102 [Fonger](https://github.com/Fonger) + +3.0.0 / 2018-01-20 +================== + * chore: upgrade deps and add nsp + +3.0.0-rc0 / 2017-12-06 +====================== + * BREAKING CHANGE: remove support for node < 4 + * BREAKING CHANGE: remove support for retainKeyOrder, will always be true by default re: Automattic/mongoose#2749 + +2.3.3 / 2017-11-19 +================== + * fixed; catch sync errors in cursor.toArray() re: Automattic/mongoose#5812 + +2.3.2 / 2017-09-27 +================== + * fixed; bumped debug -> 2.6.9 re: #89 + +2.3.1 / 2017-05-22 +================== + * fixed; bumped debug -> 2.6.7 re: #86 + +2.3.0 / 2017-03-05 +================== + * added; replaceOne function + * added; deleteOne and deleteMany functions + +2.2.3 / 2017-01-31 +================== + * fixed; throw correct error when passing incorrectly formatted array to sort() + +2.2.2 / 2017-01-31 +================== + * fixed; allow passing maps to sort() + +2.2.1 / 2017-01-29 +================== + * fixed; allow passing string to hint() + +2.2.0 / 2017-01-08 +================== + * added; updateOne and updateMany functions + +2.1.0 / 2016-12-22 +================== + * added; ability to pass an array to select() #81 [dciccale](https://github.com/dciccale) + +2.0.0 / 2016-09-25 +================== + * added; support for mongodb driver 2.0 streams + +1.12.0 / 2016-09-25 +=================== + * added; `retainKeyOrder` option re: Automattic/mongoose#4542 + +1.11.0 / 2016-06-04 +=================== + * added; `.minDistance()` helper and minDistance for `.near()` Automattic/mongoose#4179 + +1.10.1 / 2016-04-26 +=================== + * fixed; ensure conditions is an object before assigning #75 + +1.10.0 / 2016-03-16 +================== + + * updated; bluebird to latest 2.10.2 version #74 [matskiv](https://github.com/matskiv) + +1.9.0 / 2016-03-15 +================== + * added; `.eq` as a shortcut for `.equals` #72 [Fonger](https://github.com/Fonger) + * added; ability to use array syntax for sort re: https://jira.mongodb.org/browse/NODE-578 #67 + +1.8.0 / 2016-03-01 +================== + * fixed; dont throw an error if count used with sort or select Automattic/mongoose#3914 + +1.7.0 / 2016-02-23 +================== + * fixed; don't treat objects with a length property as argument objects #70 + * added; `.findCursor()` method #69 [nswbmw](https://github.com/nswbmw) + * added; `_compiledUpdate` property #68 [nswbmw](https://github.com/nswbmw) + +1.6.2 / 2015-07-12 +================== + + * fixed; support exec cb being called synchronously #66 + +1.6.1 / 2015-06-16 +================== + + * fixed; do not treat $meta projection as inclusive [vkarpov15](https://github.com/vkarpov15) + +1.6.0 / 2015-05-27 +================== + + * update dependencies #65 [bachp](https://github.com/bachp) + +1.5.0 / 2015-03-31 +================== + + * fixed; debug output + * fixed; allow hint usage with count #61 [trueinsider](https://github.com/trueinsider) + +1.4.0 / 2015-03-29 +================== + + * added; object support to slice() #60 [vkarpov15](https://github.com/vkarpov15) + * debug; improved output #57 [flyvictor](https://github.com/flyvictor) + +1.3.0 / 2014-11-06 +================== + + * added; setTraceFunction() #53 from [jlai](https://github.com/jlai) + +1.2.1 / 2014-09-26 +================== + + * fixed; distinct assignment in toConstructor() #51 from [esco](https://github.com/esco) + +1.2.0 / 2014-09-18 +================== + + * added; stream() support for find() + +1.1.0 / 2014-09-15 +================== + + * add #then for co / koa support + * start checking code coverage + +1.0.0 / 2014-07-07 +================== + + * Remove broken require() calls until they're actually implemented #48 [vkarpov15](https://github.com/vkarpov15) + +0.9.0 / 2014-05-22 +================== + + * added; thunk() support + * release 0.8.0 + +0.8.0 / 2014-05-15 +================== + + * added; support for maxTimeMS #44 [yoitsro](https://github.com/yoitsro) + * updated; devDependency (driver to 1.4.4) + +0.7.0 / 2014-05-02 +================== + + * fixed; pass $maxDistance in $near object as described in docs #43 [vkarpov15](https://github.com/vkarpov15) + * fixed; cloning buffers #42 [gjohnson](https://github.com/gjohnson) + * tests; a little bit more `mongodb` agnostic #34 [refack](https://github.com/refack) + +0.6.0 / 2014-04-01 +================== + + * fixed; Allow $meta args in sort() so text search sorting works #37 [vkarpov15](https://github.com/vkarpov15) + +0.5.3 / 2014-02-22 +================== + + * fixed; cloning mongodb.Binary + +0.5.2 / 2014-01-30 +================== + + * fixed; cloning ObjectId constructors + * fixed; cloning of ReadPreferences #30 [ashtuchkin](https://github.com/ashtuchkin) + * tests; use specific mongodb version #29 [AvianFlu](https://github.com/AvianFlu) + * tests; remove dependency on ObjectId #28 [refack](https://github.com/refack) + * tests; add failing ReadPref test + +0.5.1 / 2014-01-17 +================== + + * added; deprecation notice to tags parameter #27 [ashtuchkin](https://github.com/ashtuchkin) + * readme; add links + +0.5.0 / 2014-01-16 +================== + + * removed; mongodb driver dependency #26 [ashtuchkin](https://github.com/ashtuchkin) + * removed; first class support of read preference tags #26 (still supported though) [ashtuchkin](https://github.com/ashtuchkin) + * added; better ObjectId clone support + * fixed; cloning objects that have no constructor #21 + * docs; cleaned up [ashtuchkin](https://github.com/ashtuchkin) + +0.4.2 / 2014-01-08 +================== + + * updated; debug module 0.7.4 [refack](https://github.com/refack) + +0.4.1 / 2014-01-07 +================== + + * fixed; inclusive/exclusive logic + +0.4.0 / 2014-01-06 +================== + + * added; selected() + * added; selectedInclusively() + * added; selectedExclusively() + +0.3.3 / 2013-11-14 +================== + + * Fix Mongo DB Dependency #20 [rschmukler](https://github.com/rschmukler) + +0.3.2 / 2013-09-06 +================== + + * added; geometry support for near() + +0.3.1 / 2013-08-22 +================== + + * fixed; update retains key order #19 + +0.3.0 / 2013-08-22 +================== + + * less hardcoded isNode env detection #18 [vshulyak](https://github.com/vshulyak) + * added; validation of findAndModify varients + * clone update doc before execution + * stricter env checks + +0.2.7 / 2013-08-2 +================== + + * Now support GeoJSON point values for Query#near + +0.2.6 / 2013-07-30 +================== + + * internally, 'asc' and 'desc' for sorts are now converted into 1 and -1, respectively + +0.2.5 / 2013-07-30 +================== + + * updated docs + * changed internal representation of `sort` to use objects instead of arrays + +0.2.4 / 2013-07-25 +================== + + * updated; sliced to 0.0.5 + +0.2.3 / 2013-07-09 +================== + + * now using a callback in collection.find instead of directly calling toArray() on the cursor [ebensing](https://github.com/ebensing) + +0.2.2 / 2013-07-09 +================== + + * now exposing mongodb export to allow for better testing [ebensing](https://github.com/ebensing) + +0.2.1 / 2013-07-08 +================== + + * select no longer accepts arrays as parameters [ebensing](https://github.com/ebensing) + +0.2.0 / 2013-07-05 +================== + + * use $geoWithin by default + +0.1.2 / 2013-07-02 +================== + + * added use$geoWithin flag [ebensing](https://github.com/ebensing) + * fix read preferences typo [ebensing](https://github.com/ebensing) + * fix reference to old param name in exists() [ebensing](https://github.com/ebensing) + +0.1.1 / 2013-06-24 +================== + + * fixed; $intersects -> $geoIntersects #14 [ebensing](https://github.com/ebensing) + * fixed; Retain key order when copying objects #15 [ebensing](https://github.com/ebensing) + * bump mongodb dev dep + +0.1.0 / 2013-05-06 +================== + + * findAndModify; return the query + * move mquery.proto.canMerge to mquery.canMerge + * overwrite option now works with non-empty objects + * use strict mode + * validate count options + * validate distinct options + * add aggregate to base collection methods + * clone merge arguments + * clone merged update arguments + * move subclass to mquery.prototype.toConstructor + * fixed; maxScan casing + * use regexp-clone + * added; geometry/intersects support + * support $and + * near: do not use "radius" + * callbacks always fire on next turn of loop + * defined collection interface + * remove time from tests + * clarify goals + * updated docs; + +0.0.1 / 2012-12-15 +================== + + * initial release diff --git a/backend/node_modules/mquery/LICENSE b/backend/node_modules/mquery/LICENSE new file mode 100644 index 0000000..38c529d --- /dev/null +++ b/backend/node_modules/mquery/LICENSE @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2012 [Aaron Heckmann](aaron.heckmann+github@gmail.com) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/mquery/README.md b/backend/node_modules/mquery/README.md new file mode 100644 index 0000000..86a06eb --- /dev/null +++ b/backend/node_modules/mquery/README.md @@ -0,0 +1,1387 @@ +# mquery + +`mquery` is a fluent mongodb query builder designed to run in multiple environments. + +[![Build Status](https://travis-ci.org/aheckmann/mquery.svg?branch=master)](https://travis-ci.org/aheckmann/mquery) +[![NPM version](https://badge.fury.io/js/mquery.svg)](http://badge.fury.io/js/mquery) + +[![npm](https://nodei.co/npm/mquery.png)](https://www.npmjs.com/package/mquery) + +## Features + +- fluent query builder api +- custom base query support +- MongoDB 2.4 geoJSON support +- method + option combinations validation +- node.js driver compatibility +- environment detection +- [debug](https://github.com/visionmedia/debug) support +- separated collection implementations for maximum flexibility + +## Use + +```js +const mongo = require('mongodb'); + +const client = new mongo.MongoClient(uri); +await client.connect(); +// get a collection +const collection = client.collection('artists'); + +// pass it to the constructor +await mquery(collection).find({...}); + +// or pass it to the collection method +const docs = await mquery().find({...}).collection(collection); + +// or better yet, create a custom query constructor that has it always set +const Artist = mquery(collection).toConstructor(); +const docs = await Artist().find(...).where(...); +``` + +`mquery` requires a collection object to work with. In the example above we just pass the collection object created using the official [MongoDB driver](https://github.com/mongodb/node-mongodb-native). + +## Fluent API + +- [mquery](#mquery) + - [Features](#features) + - [Use](#use) + - [Fluent API](#fluent-api) + - [Helpers](#helpers) + - [find()](#find) + - [findOne()](#findone) + - [countDocuments()](#countdocuments) + - [estimatedDocumentCount()](#estimateddocumentcount) + - [findOneAndUpdate()](#findoneandupdate) + - [findOneAndUpdate() options](#findoneandupdate-options) + - [findOneAndReplace()](#findoneandreplace) + - [findOneAndReplace() options](#findoneandreplace-options) + - [findOneAndRemove()](#findoneandremove) + - [findOneAndRemove() options](#findoneandremove-options) + - [distinct()](#distinct) + - [updateMany()](#updatemany) + - [updateOne()](#updateone) + - [replaceOne()](#replaceone) + - [deleteOne()](#deleteone) + - [deleteMany()](#deletemany) + - [exec()](#exec) + - [cursor()](#cursor) + - [stream()](#stream) + - [all()](#all) + - [and()](#and) + - [box()](#box) + - [circle()](#circle) + - [elemMatch()](#elemmatch) + - [equals()](#equals) + - [eq()](#eq) + - [exists()](#exists) + - [geometry()](#geometry) + - [gt()](#gt) + - [gte()](#gte) + - [in()](#in) + - [intersects()](#intersects) + - [lt()](#lt) + - [lte()](#lte) + - [maxDistance()](#maxdistance) + - [mod()](#mod) + - [ne()](#ne) + - [nin()](#nin) + - [nor()](#nor) + - [near()](#near) + - [Example](#example) + - [or()](#or) + - [polygon()](#polygon) + - [regex()](#regex) + - [select()](#select) + - [String syntax](#string-syntax) + - [selected()](#selected) + - [selectedInclusively()](#selectedinclusively) + - [selectedExclusively()](#selectedexclusively) + - [size()](#size) + - [slice()](#slice) + - [within()](#within) + - [where()](#where) + - [$where()](#where-1) + - [batchSize()](#batchsize) + - [collation()](#collation) + - [comment()](#comment) + - [hint()](#hint) + - [j()](#j) + - [limit()](#limit) + - [maxTime()](#maxtime) + - [skip()](#skip) + - [sort()](#sort) + - [read()](#read) + - [Preferences:](#preferences) + - [Preference Tags:](#preference-tags) + - [readConcern()](#readconcern) + - [Read Concern Level:](#read-concern-level) + - [writeConcern()](#writeconcern) + - [Write Concern:](#write-concern) + - [slaveOk()](#slaveok) + - [tailable()](#tailable) + - [wtimeout()](#wtimeout) + - [Helpers](#helpers-1) + - [collection()](#collection) + - [then()](#then) + - [merge(object)](#mergeobject) + - [setOptions(options)](#setoptionsoptions) + - [setOptions() options](#setoptions-options) + - [setTraceFunction(func)](#settracefunctionfunc) + - [mquery.setGlobalTraceFunction(func)](#mquerysetglobaltracefunctionfunc) + - [mquery.canMerge(conditions)](#mquerycanmergeconditions) + - [mquery.use$geoWithin](#mqueryusegeowithin) + - [Custom Base Queries](#custom-base-queries) + - [Validation](#validation) + - [Debug support](#debug-support) + - [General compatibility](#general-compatibility) + - [ObjectIds](#objectids) + - [Read Preferences](#read-preferences) + - [Future goals](#future-goals) + - [Installation](#installation) + - [License](#license) + +## Helpers + +- [collection](#collection) +- [then](#then) +- [merge](#mergeobject) +- [setOptions](#setoptionsoptions) +- [setTraceFunction](#settracefunctionfunc) +- [mquery.setGlobalTraceFunction](#mquerysetglobaltracefunctionfunc) +- [mquery.canMerge](#mquerycanmergeconditions) +- [mquery.use$geoWithin](#mqueryusegeowithin) + +### find() + +Declares this query a _find_ query. Optionally pass a match clause. + +```js +mquery().find() +mquery().find(match) +await mquery().find() +const docs = await mquery().find(match); +assert(Array.isArray(docs)); +``` + +### findOne() + +Declares this query a _findOne_ query. Optionally pass a match clause. + +```js +mquery().findOne() +mquery().findOne(match) +await mquery().findOne() +const doc = await mquery().findOne(match); +if (doc) { + // the document may not be found + console.log(doc); +} +``` + +### countDocuments() + +Declares this query a _countDocuments_ query. Optionally pass a match clause. + +```js +mquery().countDocuments() +mquery().countDocuments(match) +await mquery().countDocuments() +const number = await mquery().countDocuments(match); +console.log('we found %d matching documents', number); +``` + +### estimatedDocumentCount() + +Declares this query an _estimatedDocumentCount_ query. Gets an estimated count of documents in a collection using collection metadata. + +```js +mquery().estimatedDocumentCount() +const number = await mquery().estimatedDocumentCount(); +console.log('estimated documents: %d', number); +``` + +### findOneAndUpdate() + +Declares this query a _findAndModify_ with update query. Optionally pass a match clause, update document, options. + +When executed, the first matching document (if found) is modified according to the update document and passed back. + +#### findOneAndUpdate() options + +Options are passed to the `setOptions()` method. + +- `new`: boolean - true to return the modified document rather than the original. defaults to false +- `upsert`: boolean - creates the object if it doesn't exist. defaults to false +- `sort`: if multiple docs are found by the match condition, sets the sort order to choose which doc to update + +```js +query.findOneAndUpdate() +query.findOneAndUpdate(updateDocument) +query.findOneAndUpdate(match, updateDocument) +query.findOneAndUpdate(match, updateDocument, options) + +// the following all execute the command +await query.findOneAndUpdate() +await query.findOneAndUpdate(updateDocument) +await query.findOneAndUpdate(match, updateDocument) +const doc = await query.findOneAndUpdate(match, updateDocument, options); +if (doc) { + // the document may not be found + console.log(doc); +} +``` + +### findOneAndReplace() + +Declares this query a _findOneAndReplace_ query. Finds a matching document, replaces it with the provided replacement, and returns the found document (if any). + +#### findOneAndReplace() options + +Options are passed to the `setOptions()` method. + +- `new`: boolean - true to return the modified document rather than the original. defaults to false +- `upsert`: boolean - creates the object if it doesn't exist. defaults to false +- `sort`: if multiple docs are found by the match condition, sets the sort order to choose which doc to replace + +```js +query.findOneAndReplace() +query.findOneAndReplace(replacement) +query.findOneAndReplace(match, replacement) +query.findOneAndReplace(match, replacement, options) + +// the following all execute the command +await query.findOneAndReplace() +await query.findOneAndReplace(replacement) +await query.findOneAndReplace(match, replacement) +const doc = await query.findOneAndReplace(match, replacement, options); +if (doc) { + // the document may not be found + console.log(doc); +} +``` + +### findOneAndRemove() + +Declares this query a _findAndModify_ with remove query. Alias of findOneAndDelete. +Optionally pass a match clause, options. + +When executed, the first matching document (if found) is modified according to the update document, removed from the collection and passed as a result. + +#### findOneAndRemove() options + +Options are passed to the `setOptions()` method. + +- `sort`: if multiple docs are found by the condition, sets the sort order to choose which doc to modify and remove + +```js +A.where().findOneAndDelete() +A.where().findOneAndRemove() +A.where().findOneAndRemove(match) +A.where().findOneAndRemove(match, options) + +// the following all execute the command +await A.where().findOneAndRemove() +await A.where().findOneAndRemove(match) +const doc = await A.where().findOneAndRemove(match, options); +if (doc) { + // the document may not be found + console.log(doc); +} +``` + +### distinct() + +Declares this query a _distinct_ query. Optionally pass the distinct field, a match clause. + +```js +mquery().distinct() +mquery().distinct(match) +mquery().distinct(match, field) +mquery().distinct(field) + +// the following all execute the command +await mquery().distinct() +await mquery().distinct(field) +await mquery().distinct(match) +const result = await mquery().distinct(match, field); +console.log(result); +``` + +### updateMany() + +Declares this query an _updateMany_ query. Updates all documents that match `criteria`. + +When executed, the first argument is the query, and the second argument is the update document. + +_All paths passed that are not $atomic operations will become $set ops._ + +```js +mquery().updateMany({ name: /^match/ }, { field: 'value' }) +await mquery().updateMany({ name: /^match/ }, { field: 'value' }) +await mquery().where({ name: /^match/ }).updateMany({ field: 'value' }) +``` + +### updateOne() + +Declares this query an _updateOne_ query. Updates only the first document that matches `criteria`. + +When executed, the first argument is the query, and the second argument is the update document. + +_All paths passed that are not $atomic operations will become $set ops._ + +```js +mquery().updateOne({ name: 'match' }, { field: 'value' }) +await mquery().updateOne({ name: 'match' }, { field: 'value' }) +await mquery().where({ name: 'match' }).updateOne({ field: 'value' }) +``` + +### replaceOne() + +Declares this query a _replaceOne_ query. Replaces the first document that matches `criteria` with the provided replacement document. + +Similar to `updateOne()`, except `replaceOne()` is not allowed to use atomic modifiers (`$set`, `$push`, etc.). Calling `replaceOne()` will always replace the existing doc. + +```js +mquery().replaceOne({ _id: 1 }, { name: 'new name', age: 25 }) +await mquery().replaceOne({ _id: 1 }, { name: 'new name', age: 25 }) +``` + +### deleteOne() + +Declares this query a _deleteOne_ query. Deletes the first document that matches `criteria`. + +```js +mquery().deleteOne({ name: 'match' }) +await mquery().deleteOne({ name: 'match' }) +await mquery().where({ name: 'match' }).deleteOne() +``` + +### deleteMany() + +Declares this query a _deleteMany_ query. Deletes all documents that match `criteria`. + +```js +mquery().deleteMany({ name: /^match/ }) +await mquery().deleteMany({ name: /^match/ }) +await mquery().where({ name: /^match/ }).deleteMany() +``` + +### exec() + +Executes the query. + +```js +const docs = await mquery().findOne().where('route').intersects(polygon).exec() +``` + +### cursor() + +Returns a cursor for the given `find` query. + +```js +const cursor = mquery().find({ name: /^match/ }).cursor(); +cursor.on('data', function(doc) { + console.log(doc); +}); +cursor.on('end', function() { + console.log('done'); +}); +``` + +Note: this only works with `find()` operations. + +### stream() + +Executes the query and returns a stream. + +```js +var stream = mquery().find().stream(options); +stream.on('data', cb); +stream.on('close', fn); +``` + +Note: this only works with `find()` operations. + +Note: returns the stream object directly from the node-mongodb-native driver. (currently streams1 type stream). Any options will be passed along to the [driver method](http://mongodb.github.io/node-mongodb-native/api-generated/cursor.html#stream). + +--- + +### all() + +Specifies an `$all` query condition + +```js +mquery().where('permission').all(['read', 'write']) +``` + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/all/) + +### and() + +Specifies arguments for an `$and` condition + +```js +mquery().and([{ color: 'green' }, { status: 'ok' }]) +``` + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/and/) + +### box() + +Specifies a `$box` condition + +```js +var lowerLeft = [40.73083, -73.99756] +var upperRight= [40.741404, -73.988135] + +mquery().where('location').within().box(lowerLeft, upperRight) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/box/) + +### circle() + +Specifies a `$center` or `$centerSphere` condition. + +```js +var area = { center: [50, 50], radius: 10, unique: true } +query.where('loc').within().circle(area) +query.circle('loc', area); + +// for spherical calculations +var area = { center: [50, 50], radius: 10, unique: true, spherical: true } +query.where('loc').within().circle(area) +query.circle('loc', area); +``` + +- [MongoDB Documentation - center](http://docs.mongodb.org/manual/reference/operator/center/) +- [MongoDB Documentation - centerSphere](http://docs.mongodb.org/manual/reference/operator/centerSphere/) + +### elemMatch() + +Specifies an `$elemMatch` condition + +```js +query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}}) + +query.elemMatch('comment', function (elem) { + elem.where('author').equals('autobot'); + elem.where('votes').gte(5); +}) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/elemMatch/) + +### equals() + +Specifies the complementary comparison value for the path specified with `where()`. + +```js +mquery().where('age').equals(49); + +// is the same as + +mquery().where({ 'age': 49 }); +``` + +### eq() + +Alias of `equals()`. Specifies the complementary comparison value for the path specified with `where()`. + +```js +mquery().where('age').eq(49); + +// is the same as + +mquery().where('age').equals(49); + +// is the same as + +mquery().where({ 'age': 49 }); +``` + +### exists() + +Specifies an `$exists` condition + +```js +// { name: { $exists: true }} +mquery().where('name').exists() +mquery().where('name').exists(true) +mquery().exists('name') + +// { name: { $exists: false }} +mquery().where('name').exists(false); +mquery().exists('name', false); +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/exists/) + +### geometry() + +Specifies a `$geometry` condition + +```js +var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]] +query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA }) + +// or +var polyB = [[ 0, 0 ], [ 1, 1 ]] +query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB }) + +// or +var polyC = [ 0, 0 ] +query.where('loc').within().geometry({ type: 'Point', coordinates: polyC }) + +// or +query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC }) + +// or +query.where('loc').near().geometry({ type: 'Point', coordinates: [3,5] }) +``` + +`geometry()` **must** come after `intersects()`, `within()`, or `near()`. + +The `object` argument must contain `type` and `coordinates` properties. + +- type `String` +- coordinates `Array` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geometry/) + +### gt() + +Specifies a `$gt` query condition. + +```js +mquery().where('clicks').gt(999) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/gt/) + +### gte() + +Specifies a `$gte` query condition. + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/gte/) + +```js +mquery().where('clicks').gte(1000) +``` + +### in() + +Specifies an `$in` query condition. + +```js +mquery().where('author_id').in([3, 48901, 761]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/in/) + +### intersects() + +Declares an `$geoIntersects` query for `geometry()`. + +```js +query.where('path').intersects().geometry({ + type: 'LineString' + , coordinates: [[180.0, 11.0], [180, 9.0]] +}) + +// geometry arguments are supported +query.where('path').intersects({ + type: 'LineString' + , coordinates: [[180.0, 11.0], [180, 9.0]] +}) +``` + +**Must** be used after `where()`. + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geoIntersects/) + +### lt() + +Specifies a `$lt` query condition. + +```js +mquery().where('clicks').lt(50) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/lt/) + +### lte() + +Specifies a `$lte` query condition. + +```js +mquery().where('clicks').lte(49) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/lte/) + +### maxDistance() + +Specifies a `$maxDistance` query condition. + +```js +mquery().where('location').near({ center: [139, 74.3] }).maxDistance(5) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/maxDistance/) + +### mod() + +Specifies a `$mod` condition + +```js +mquery().where('count').mod(2, 0) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/mod/) + +### ne() + +Specifies a `$ne` query condition. + +```js +mquery().where('status').ne('ok') +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/ne/) + +### nin() + +Specifies an `$nin` query condition. + +```js +mquery().where('author_id').nin([3, 48901, 761]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/nin/) + +### nor() + +Specifies arguments for an `$nor` condition. + +```js +mquery().nor([{ color: 'green' }, { status: 'ok' }]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/nor/) + +### near() + +Specifies arguments for a `$near` or `$nearSphere` condition. + +These operators return documents sorted by distance. + +#### Example + +```js +query.where('loc').near({ center: [10, 10] }); +query.where('loc').near({ center: [10, 10], maxDistance: 5 }); +query.near('loc', { center: [10, 10], maxDistance: 5 }); + +// GeoJSON +query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }}); +query.where('loc').near({ center: { type: 'Point', coordinates: [10, 10] }, maxDistance: 5, spherical: true }); +query.where('loc').near().geometry({ type: 'Point', coordinates: [10, 10] }); + +// For a $nearSphere condition, pass the `spherical` option. +query.near({ center: [10, 10], maxDistance: 5, spherical: true }); +``` + +[MongoDB Documentation](http://www.mongodb.org/display/DOCS/Geospatial+Indexing) + +### or() + +Specifies arguments for an `$or` condition. + +```js +mquery().or([{ color: 'red' }, { status: 'emergency' }]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/or/) + +### polygon() + +Specifies a `$polygon` condition + +```js +mquery().where('loc').within().polygon([10,20], [13, 25], [7,15]) +mquery().polygon('loc', [10,20], [13, 25], [7,15]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/polygon/) + +### regex() + +Specifies a `$regex` query condition. + +```js +mquery().where('name').regex(/^sixstepsrecords/) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/regex/) + +### select() + +Specifies which document fields to include or exclude + +```js +// 1 means include, 0 means exclude +mquery().select({ name: 1, address: 1, _id: 0 }) + +// or + +mquery().select('name address -_id') +``` + +#### String syntax + +When passing a string, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. + +```js +// include a and b, exclude c +query.select('a b -c'); + +// or you may use object notation, useful when +// you have keys already prefixed with a "-" +query.select({a: 1, b: 1, c: 0}); +``` + +_Cannot be used with `distinct()`._ + +### selected() + +Determines if the query has selected any fields. + +```js +var query = mquery(); +query.selected() // false +query.select('-name'); +query.selected() // true +``` + +### selectedInclusively() + +Determines if the query has selected any fields inclusively. + +```js +var query = mquery().select('name'); +query.selectedInclusively() // true + +var query = mquery(); +query.selected() // false +query.select('-name'); +query.selectedInclusively() // false +query.selectedExclusively() // true +``` + +### selectedExclusively() + +Determines if the query has selected any fields exclusively. + +```js +var query = mquery().select('-name'); +query.selectedExclusively() // true + +var query = mquery(); +query.selected() // false +query.select('name'); +query.selectedExclusively() // false +query.selectedInclusively() // true +``` + +### size() + +Specifies a `$size` query condition. + +```js +mquery().where('someArray').size(6) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/size/) + +### slice() + +Specifies a `$slice` projection for a `path` + +```js +mquery().where('comments').slice(5) +mquery().where('comments').slice(-5) +mquery().where('comments').slice([-10, 5]) +``` + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/projection/slice/) + +### within() + +Sets a `$geoWithin` or `$within` argument for geo-spatial queries. + +```js +mquery().within().box() +mquery().within().circle() +mquery().within().geometry() + +mquery().where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true }); +mquery().where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] }); +mquery().where('loc').within({ polygon: [[],[],[],[]] }); + +mquery().where('loc').within([], [], []) // polygon +mquery().where('loc').within([], []) // box +mquery().where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry +``` + +As of mquery 2.0, `$geoWithin` is used by default. This impacts you if running MongoDB < 2.4. To alter this behavior, see [mquery.use$geoWithin](#mqueryusegeowithin). + +**Must** be used after `where()`. + +[MongoDB Documentation](http://docs.mongodb.org/manual/reference/operator/geoWithin/) + +### where() + +Specifies a `path` for use with chaining + +```js +// instead of writing: +mquery().find({age: {$gte: 21, $lte: 65}}); + +// we can instead write: +mquery().where('age').gte(21).lte(65); + +// passing query conditions is permitted too +mquery().find().where({ name: 'vonderful' }) + +// chaining +await mquery() + .where('age').gte(21).lte(65) + .where({ 'name': /^vonderful/i }) + .where('friends').slice(10) + .exec() +``` + +### $where() + +Specifies a `$where` condition. + +Use `$where` when you need to select documents using a JavaScript expression. + +```js +await query.$where('this.comments.length > 10 || this.name.length > 5').exec() + +query.$where(function () { + return this.comments.length > 10 || this.name.length > 5; +}) +``` + +Only use `$where` when you have a condition that cannot be met using other MongoDB operators like `$lt`. Be sure to read about all of [its caveats](http://docs.mongodb.org/manual/reference/operator/where/) before using. + +--- + +### batchSize() + +Specifies the batchSize option. + +```js +query.batchSize(100) +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.batchSize/) + +### collation() + +Specifies the collation option. + +```js +query.collation({ locale: "en_US", strength: 1 }) +``` + +[MongoDB documentation](https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation) + +### comment() + +Specifies the comment option. + +```js +query.comment('login query'); +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/) + +### hint() + +Sets query hints. + +```js +mquery().hint({ indexA: 1, indexB: -1 }) +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/operator/hint/) + +### j() + +Requests acknowledgement that this operation has been persisted to MongoDB's on-disk journal. + +This option is only valid for operations that write to the database: + +- `deleteOne()` +- `deleteMany()` +- `findOneAndDelete()` +- `findOneAndUpdate()` +- `updateOne()` +- `updateMany()` + +Defaults to the `j` value if it is specified in [writeConcern](#writeconcern) + +```js +mquery().j(true); +``` + +### limit() + +Specifies the limit option. + +```js +query.limit(20) +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.limit/) + +### maxTime() + +Specifies the maxTimeMS option. + +```js +query.maxTime(100) +query.maxTimeMS(100) +``` + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.maxTimeMS/) + +### skip() + +Specifies the skip option. + +```js +query.skip(100).limit(20) +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.skip/) + +### sort() + +Sets the query sort order. + +If an object is passed, key values allowed are `asc`, `desc`, `ascending`, `descending`, `1`, and `-1`. + +If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending. + +```js +// these are equivalent +query.sort({ field: 'asc', test: -1 }); +query.sort('field -test'); +``` + +_Cannot be used with `distinct()`._ + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/cursor.sort/) + +### read() + +Sets the readPreference option for the query. + +```js +mquery().read('primary') +mquery().read('p') // same as primary + +mquery().read('primaryPreferred') +mquery().read('pp') // same as primaryPreferred + +mquery().read('secondary') +mquery().read('s') // same as secondary + +mquery().read('secondaryPreferred') +mquery().read('sp') // same as secondaryPreferred + +mquery().read('nearest') +mquery().read('n') // same as nearest + +mquery().setReadPreference('primary') // alias of .read() +``` + +#### Preferences: + +- `primary` - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags. +- `secondary` - Read from secondary if available, otherwise error. +- `primaryPreferred` - Read from primary if available, otherwise a secondary. +- `secondaryPreferred` - Read from a secondary if available, otherwise read from the primary. +- `nearest` - All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection. + +Aliases + +- `p` primary +- `pp` primaryPreferred +- `s` secondary +- `sp` secondaryPreferred +- `n` nearest + +#### Preference Tags: + +To keep the separation of concerns between `mquery` and your driver +clean, `mquery#read()` no longer handles specifying a second `tags` argument as of version 0.5. +If you need to specify tags, pass any non-string argument as the first argument. +`mquery` will pass this argument untouched to your collections methods later. +For example: + +```js +// example of specifying tags using the Node.js driver +var ReadPref = require('mongodb').ReadPreference; +var preference = new ReadPref('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }]); +mquery(...).read(preference).exec(); +``` + +Read more about how to use read preferences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences). + +### readConcern() + +Sets the readConcern option for the query. + +```js +// local +mquery().readConcern('local') +mquery().readConcern('l') +mquery().r('l') + +// available +mquery().readConcern('available') +mquery().readConcern('a') +mquery().r('a') + +// majority +mquery().readConcern('majority') +mquery().readConcern('m') +mquery().r('m') + +// linearizable +mquery().readConcern('linearizable') +mquery().readConcern('lz') +mquery().r('lz') + +// snapshot +mquery().readConcern('snapshot') +mquery().readConcern('s') +mquery().r('s') +``` + +#### Read Concern Level: + +- `local` - The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). (MongoDB 3.2+) +- `available` - The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). (MongoDB 3.6+) +- `majority` - The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure. (MongoDB 3.2+) +- `linearizable` - The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results. (MongoDB 3.4+) +- `snapshot` - Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data. (MongoDB 4.0+) + +Aliases + +- `l` local +- `a` available +- `m` majority +- `lz` linearizable +- `s` snapshot + +Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/). + +### writeConcern() + +Sets the writeConcern option for the query. + +This option is only valid for operations that write to the database: + +- `deleteOne()` +- `deleteMany()` +- `findOneAndDelete()` +- `findOneAndUpdate()` +- `updateOne()` +- `updateMany()` + +```js +mquery().writeConcern(0) +mquery().writeConcern(1) +mquery().writeConcern({ w: 1, j: true, wtimeout: 2000 }) +mquery().writeConcern('majority') +mquery().writeConcern('m') // same as majority +mquery().writeConcern('tagSetName') // if the tag set is 'm', use .writeConcern({ w: 'm' }) instead +mquery().w(1) // w is alias of writeConcern +``` + +#### Write Concern: + +writeConcern({ w: ``, j: ``, wtimeout: `` }`) + +- the w option to request acknowledgement that the write operation has propagated to a specified number of mongod instances or to mongod instances with specified tags +- the j option to request acknowledgement that the write operation has been written to the journal +- the wtimeout option to specify a time limit to prevent write operations from blocking indefinitely + +Can be break down to use the following syntax: + +mquery().w(``).j(``).wtimeout(``) + +Read more about how to use write concern [here](https://docs.mongodb.com/manual/reference/write-concern/) + +### slaveOk() + +Sets the slaveOk option. `true` allows reading from secondaries. + +**deprecated** use [read()](#read) preferences instead if on mongodb >= 2.2 + +```js +query.slaveOk() // true +query.slaveOk(true) +query.slaveOk(false) +``` + +[MongoDB documentation](http://docs.mongodb.org/manual/reference/method/rs.slaveOk/) + +### tailable() + +Sets tailable option. + +```js +mquery().tailable() <== true +mquery().tailable(true) +mquery().tailable(false) +``` + +_Cannot be used with `distinct()`._ + +[MongoDB Documentation](http://docs.mongodb.org/manual/tutorial/create-tailable-cursor/) + +### wtimeout() + +Specifies a time limit, in milliseconds, for the write concern. If `w > 1`, it is maximum amount of time to +wait for this write to propagate through the replica set before this operation fails. The default is `0`, which means no timeout. + +This option is only valid for operations that write to the database: + +- `deleteOne()` +- `deleteMany()` +- `findOneAndDelete()` +- `findOneAndUpdate()` +- `updateOne()` +- `updateMany()` + +Defaults to `wtimeout` value if it is specified in [writeConcern](#writeconcern) + +```js +mquery().wtimeout(2000) +mquery().wTimeout(2000) +``` + +## Helpers + +### collection() + +Sets the querys collection. + +```js +mquery().collection(aCollection) +``` + +### then() + +Executes the query and returns a promise which will be resolved with the query results or rejected if the query responds with an error. + +```js +mquery().find(..).then(success, error); +``` + +This is very useful when combined with [co](https://github.com/visionmedia/co) or [koa](https://github.com/koajs/koa), which automatically resolve promise-like objects for you. + +```js +co(function*(){ + var doc = yield mquery().findOne({ _id: 499 }); + console.log(doc); // { _id: 499, name: 'amazing', .. } +})(); +``` + +_NOTE_: +The returned promise is a [bluebird](https://github.com/petkaantonov/bluebird/) promise but this is customizable. If you want to +use your favorite promise library, simply set `mquery.Promise = YourPromiseConstructor`. +Your `Promise` must be [promises A+](http://promisesaplus.com/) compliant. + +### merge(object) + +Merges other mquery or match condition objects into this one. When an mquery instance is passed, its match conditions, field selection and options are merged. + +```js +const drum = mquery({ type: 'drum' }).collection(instruments); +const redDrum = mquery({ color: 'red' }).merge(drum); +const n = await redDrum.count(); +console.log('there are %d red drums', n); +``` + +Internally uses `mquery.canMerge` to determine validity. + +### setOptions(options) + +Sets query options. + +```js +mquery().setOptions({ collection: coll, limit: 20 }) +``` + +#### setOptions() options + +- [tailable](#tailable) * +- [sort](#sort) * +- [limit](#limit) * +- [skip](#skip) * +- [maxTime](#maxtime) * +- [batchSize](#batchsize) * +- [comment](#comment) * +- [hint](#hint) * +- [collection](#collection): the collection to query against + +_* denotes a query helper method is also available_ + +### setTraceFunction(func) + +Set a function to trace this query. Useful for profiling or logging. + +```js +function traceFunction (method, queryInfo, query) { + console.log('starting ' + method + ' query'); + + return function (err, result, millis) { + console.log('finished ' + method + ' query in ' + millis + 'ms'); + }; +} + +mquery().setTraceFunction(traceFunction).findOne({name: 'Joe'}, cb); +``` + +The trace function is passed (method, queryInfo, query) + +- method is the name of the method being called (e.g. findOne) +- queryInfo contains information about the query: + - conditions: query conditions/criteria + - options: options such as sort, fields, etc + - doc: document being updated +- query is the query object + +The trace function should return a callback function which accepts: +- err: error, if any +- result: result, if any +- millis: time spent waiting for query result + +NOTE: stream requests are not traced. + +### mquery.setGlobalTraceFunction(func) + +Similar to `setTraceFunction()` but automatically applied to all queries. + +```js +mquery.setTraceFunction(traceFunction); +``` + +### mquery.canMerge(conditions) + +Determines if `conditions` can be merged using `mquery().merge()`. + +```js +var query = mquery({ type: 'drum' }); +var okToMerge = mquery.canMerge(anObject) +if (okToMerge) { + query.merge(anObject); +} +``` + +## mquery.use$geoWithin + +MongoDB 2.4 introduced the `$geoWithin` operator which replaces and is 100% backward compatible with `$within`. As of mquery 0.2, we default to using `$geoWithin` for all `within()` calls. + +If you are running MongoDB < 2.4 this will be problematic. To force `mquery` to be backward compatible and always use `$within`, set the `mquery.use$geoWithin` flag to `false`. + +```js +mquery.use$geoWithin = false; +``` + +## Custom Base Queries + +Often times we want custom base queries that encapsulate predefined criteria. With `mquery` this is easy. First create the query you want to reuse and call its `toConstructor()` method which returns a new subclass of `mquery` that retains all options and criteria of the original. + +```js +var greatMovies = mquery(movieCollection).where('rating').gte(4.5).toConstructor(); + +// use it! +const n = await greatMovies().count(); +console.log('There are %d great movies', n); + +const docs = await greatMovies().where({ name: /^Life/ }).select('name').find(); +console.log(docs); +``` + +## Validation + +Method and options combinations are checked for validity at runtime to prevent creation of invalid query constructs. For example, a `distinct` query does not support specifying options like `hint` or field selection. In this case an error will be thrown so you can catch these mistakes in development. + +## Debug support + +Debug mode is provided through the use of the [debug](https://github.com/visionmedia/debug) module. To enable: + +```sh +DEBUG=mquery node yourprogram.js +``` + +Read the debug module documentation for more details. + +## General compatibility + +### ObjectIds + +`mquery` clones query arguments before passing them to a `collection` method for execution. +This prevents accidental side-affects to the objects you pass. +To clone `ObjectIds` we need to make some assumptions. + +First, to check if an object is an `ObjectId`, we check its constructors name. If it matches either +`ObjectId` or `ObjectID` we clone it. + +To clone `ObjectIds`, we call its optional `clone` method. If a `clone` method does not exist, we fall +back to calling `new obj.constructor(obj.id)`. We assume, for compatibility with the +Node.js driver, that the `ObjectId` instance has a public `id` property and that +when creating an `ObjectId` instance we can pass that `id` as an argument. + +#### Read Preferences + +`mquery` supports specifying [Read Preferences](https://www.mongodb.com/docs/manual/core/read-preference/) to control from which MongoDB node your query will read. +The Read Preferences spec also support specifying tags. To pass tags, some +drivers (Node.js driver) require passing a special constructor that handles both the read preference and its tags. +If you need to specify tags, pass an instance of your drivers ReadPreference constructor or roll your own. `mquery` will store whatever you provide and pass later to your collection during execution. + +## Future goals + +- mongo shell compatibility +- browser compatibility + +## Installation + +```sh +npm install mquery +``` + +## License + +[MIT](https://github.com/aheckmann/mquery/blob/master/LICENSE) diff --git a/backend/node_modules/mquery/SECURITY.md b/backend/node_modules/mquery/SECURITY.md new file mode 100644 index 0000000..41b89d8 --- /dev/null +++ b/backend/node_modules/mquery/SECURITY.md @@ -0,0 +1 @@ +Please follow the instructions on [Tidelift's security page](https://tidelift.com/docs/security) to report a security issue. diff --git a/backend/node_modules/mquery/lib/collection/collection.js b/backend/node_modules/mquery/lib/collection/collection.js new file mode 100644 index 0000000..10366ca --- /dev/null +++ b/backend/node_modules/mquery/lib/collection/collection.js @@ -0,0 +1,47 @@ +'use strict'; + +/** + * methods a collection must implement + */ + +const methods = [ + 'find', + 'findOne', + 'updateMany', + 'updateOne', + 'replaceOne', + 'countDocuments', + 'estimatedDocumentCount', + 'distinct', + 'findOneAndDelete', + 'findOneAndReplace', + 'findOneAndUpdate', + 'aggregate', + 'findCursor', + 'deleteOne', + 'deleteMany' +]; + +/** + * Collection base class from which implementations inherit + */ + +function Collection() {} + +for (let i = 0, len = methods.length; i < len; ++i) { + const method = methods[i]; + Collection.prototype[method] = notImplemented(method); +} + +module.exports = exports = Collection; +Collection.methods = methods; + +/** + * creates a function which throws an implementation error + */ + +function notImplemented(method) { + return function() { + throw new Error('collection.' + method + ' not implemented'); + }; +} diff --git a/backend/node_modules/mquery/lib/collection/index.js b/backend/node_modules/mquery/lib/collection/index.js new file mode 100644 index 0000000..4faa032 --- /dev/null +++ b/backend/node_modules/mquery/lib/collection/index.js @@ -0,0 +1,13 @@ +'use strict'; + +const env = require('../env'); + +if ('unknown' == env.type) { + throw new Error('Unknown environment'); +} + +module.exports = + env.isNode ? require('./node') : + env.isMongo ? require('./collection') : + require('./collection'); + diff --git a/backend/node_modules/mquery/lib/collection/node.js b/backend/node_modules/mquery/lib/collection/node.js new file mode 100644 index 0000000..d12983c --- /dev/null +++ b/backend/node_modules/mquery/lib/collection/node.js @@ -0,0 +1,128 @@ +'use strict'; + +/** + * Module dependencies + */ + +const Collection = require('./collection'); + +class NodeCollection extends Collection { + constructor(col) { + super(); + + this.collection = col; + this.collectionName = col.collectionName; + } + + /** + * find(match, options) + */ + async find(match, options) { + const cursor = this.collection.find(match, options); + + return cursor.toArray(); + } + + /** + * findOne(match, options) + */ + async findOne(match, options) { + return this.collection.findOne(match, options); + } + + /** + * countDocuments(match, options) + */ + async countDocuments(match, options) { + return this.collection.countDocuments(match, options); + } + + /** + * estimatedDocumentCount(match, options) + */ + async estimatedDocumentCount(match, options) { + return this.collection.estimatedDocumentCount(match, options); + } + + /** + * distinct(prop, match, options) + */ + async distinct(prop, match, options) { + return this.collection.distinct(prop, match, options); + } + + /** + * updateMany(match, update, options) + */ + async updateMany(match, update, options) { + return this.collection.updateMany(match, update, options); + } + + /** + * updateOne(match, update, options) + */ + async updateOne(match, update, options) { + return this.collection.updateOne(match, update, options); + } + + /** + * replaceOne(match, update, options) + */ + async replaceOne(match, update, options) { + return this.collection.replaceOne(match, update, options); + } + + /** + * deleteOne(match, options) + */ + async deleteOne(match, options) { + return this.collection.deleteOne(match, options); + } + + /** + * deleteMany(match, options) + */ + async deleteMany(match, options) { + return this.collection.deleteMany(match, options); + } + + /** + * findOneAndDelete(match, options, function(err[, result]) + */ + async findOneAndDelete(match, options) { + return this.collection.findOneAndDelete(match, options); + } + + /** + * findOneAndUpdate(match, update, options) + */ + async findOneAndUpdate(match, update, options) { + return this.collection.findOneAndUpdate(match, update, options); + } + + /** + * findOneAndReplace(match, update, options) + */ + async findOneAndReplace(match, update, options) { + return this.collection.findOneAndReplace(match, update, options); + } + + /** + * var cursor = findCursor(match, options) + */ + findCursor(match, options) { + return this.collection.find(match, options); + } + + /** + * aggregation(operators...) + * TODO + */ +} + + +/** + * Expose + */ + +module.exports = exports = NodeCollection; diff --git a/backend/node_modules/mquery/lib/env.js b/backend/node_modules/mquery/lib/env.js new file mode 100644 index 0000000..d3d225b --- /dev/null +++ b/backend/node_modules/mquery/lib/env.js @@ -0,0 +1,22 @@ +'use strict'; + +exports.isNode = 'undefined' != typeof process + && 'object' == typeof module + && 'object' == typeof global + && 'function' == typeof Buffer + && process.argv; + +exports.isMongo = !exports.isNode + && 'function' == typeof printjson + && 'function' == typeof ObjectId + && 'function' == typeof rs + && 'function' == typeof sh; + +exports.isBrowser = !exports.isNode + && !exports.isMongo + && 'undefined' != typeof window; + +exports.type = exports.isNode ? 'node' + : exports.isMongo ? 'mongo' + : exports.isBrowser ? 'browser' + : 'unknown'; diff --git a/backend/node_modules/mquery/lib/mquery.js b/backend/node_modules/mquery/lib/mquery.js new file mode 100644 index 0000000..3aae9f4 --- /dev/null +++ b/backend/node_modules/mquery/lib/mquery.js @@ -0,0 +1,2748 @@ +'use strict'; + +/** + * Dependencies + */ + +const assert = require('assert'); +const util = require('util'); +const utils = require('./utils'); + +const debug = util.debuglog('mquery'); + +/** + * Query constructor used for building queries. + * + * #### Example: + * + * var query = new Query({ name: 'mquery' }); + * query.setOptions({ collection: moduleCollection }) + * await query.where('age').gte(21).exec(); + * + * @param {Object} [criteria] criteria for the query OR the collection instance to use + * @param {Object} [options] + * @api public + */ + +function Query(criteria, options) { + if (!(this instanceof Query)) + return new Query(criteria, options); + + const proto = this.constructor.prototype; + + this.op = proto.op || undefined; + + this.options = Object.assign({}, proto.options); + + this._conditions = proto._conditions + ? utils.clone(proto._conditions) + : {}; + + this._fields = proto._fields + ? utils.clone(proto._fields) + : undefined; + + this._updateDoc = proto._updateDoc + ? utils.clone(proto._updateDoc) + : undefined; + + this._path = proto._path || undefined; + this._distinctDoc = proto._distinctDoc || undefined; + this._collection = proto._collection || undefined; + this._traceFunction = proto._traceFunction || undefined; + + if (options) { + this.setOptions(options); + } + + if (criteria) { + this.find(criteria); + } +} + +/** + * Converts this query to a constructor function with all arguments and options retained. + * + * #### Example: + * + * // Create a query that will read documents with a "video" category from + * // `aCollection` on the primary node in the replica-set unless it is down, + * // in which case we'll read from a secondary node. + * var query = mquery({ category: 'video' }) + * query.setOptions({ collection: aCollection, read: 'primaryPreferred' }); + * + * // create a constructor based off these settings + * var Video = query.toConstructor(); + * + * // Video is now a subclass of mquery() and works the same way but with the + * // default query parameters and options set. + * + * // run a query with the previous settings but filter for movies with names + * // that start with "Life". + * Video().where({ name: /^Life/ }).exec(cb); + * + * @return {Query} new Query + * @api public + */ + +Query.prototype.toConstructor = function toConstructor() { + function CustomQuery(criteria, options) { + if (!(this instanceof CustomQuery)) + return new CustomQuery(criteria, options); + Query.call(this, criteria, options); + } + + utils.inherits(CustomQuery, Query); + + // set inherited defaults + const p = CustomQuery.prototype; + + p.options = {}; + p.setOptions(this.options); + + p.op = this.op; + p._conditions = utils.clone(this._conditions); + p._fields = utils.clone(this._fields); + p._updateDoc = utils.clone(this._updateDoc); + p._path = this._path; + p._distinctDoc = this._distinctDoc; + p._collection = this._collection; + p._traceFunction = this._traceFunction; + + return CustomQuery; +}; + +/** + * Sets query options. + * + * #### Options: + * + * - [tailable](http://www.mongodb.org/display/DOCS/Tailable+Cursors) * + * - [sort](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bsort(\)%7D%7D) * + * - [limit](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D) * + * - [skip](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D) * + * - [maxTime](http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS) * + * - [batchSize](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D) * + * - [comment](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment) * + * - [hint](http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint) * + * - [slaveOk](http://docs.mongodb.org/manual/applications/replication/#read-preference) * + * - [safe](http://www.mongodb.org/display/DOCS/getLastError+Command) + * - collection the collection to query against + * + * _* denotes a query helper method is also available_ + * + * @param {Object} options + * @api public + */ + +Query.prototype.setOptions = function(options) { + if (!(options && utils.isObject(options))) + return this; + + // set arbitrary options + const methods = utils.keys(options); + let method; + + for (let i = 0; i < methods.length; ++i) { + method = methods[i]; + + // use methods if exist (safer option manipulation) + if ('function' == typeof this[method]) { + const args = Array.isArray(options[method]) + ? options[method] + : [options[method]]; + this[method].apply(this, args); + } else { + this.options[method] = options[method]; + } + } + + return this; +}; + +/** + * Sets this Querys collection. + * + * @param {Collection} coll + * @return {Query} this + */ + +Query.prototype.collection = function collection(coll) { + this._collection = new Query.Collection(coll); + + return this; +}; + +/** + * Adds a collation to this op (MongoDB 3.4 and up) + * + * #### Example: + * + * query.find().collation({ locale: "en_US", strength: 1 }) + * + * @param {Object} value + * @return {Query} this + * @see MongoDB docs https://docs.mongodb.com/manual/reference/method/cursor.collation/#cursor.collation + * @api public + */ + +Query.prototype.collation = function(value) { + this.options.collation = value; + return this; +}; + +/** + * Specifies a `$where` condition + * + * Use `$where` when you need to select documents using a JavaScript expression. + * + * #### Example: + * + * query.$where('this.comments.length > 10 || this.name.length > 5') + * + * query.$where(function () { + * return this.comments.length > 10 || this.name.length > 5; + * }) + * + * @param {String|Function} js javascript string or function + * @return {Query} this + * @memberOf Query + * @method $where + * @api public + */ + +Query.prototype.$where = function(js) { + this._conditions.$where = js; + return this; +}; + +/** + * Specifies a `path` for use with chaining. + * + * #### Example: + * + * // instead of writing: + * await User.find({age: {$gte: 21, $lte: 65}}); + * + * // we can instead write: + * User.where('age').gte(21).lte(65); + * + * // passing query conditions is permitted + * User.find().where({ name: 'vonderful' }) + * + * // chaining + * await User + * .where('age').gte(21).lte(65) + * .where('name', /^vonderful/i) + * .where('friends').slice(10) + * .exec() + * + * @param {String} [path] + * @param {Object} [val] + * @return {Query} this + * @api public + */ + +Query.prototype.where = function() { + if (!arguments.length) return this; + if (!this.op) this.op = 'find'; + + const type = typeof arguments[0]; + + if ('string' == type) { + this._path = arguments[0]; + + if (2 === arguments.length) { + this._conditions[this._path] = arguments[1]; + } + + return this; + } + + if ('object' == type && !Array.isArray(arguments[0])) { + return this.merge(arguments[0]); + } + + throw new TypeError('path must be a string or object'); +}; + +/** + * Specifies the complementary comparison value for paths specified with `where()` + * + * #### Example: + * + * User.where('age').equals(49); + * + * // is the same as + * + * User.where('age', 49); + * + * @param {Object} val + * @return {Query} this + * @api public + */ + +Query.prototype.equals = function equals(val) { + this._ensurePath('equals'); + const path = this._path; + this._conditions[path] = val; + return this; +}; + +/** + * Specifies the complementary comparison value for paths specified with `where()` + * This is alias of `equals` + * + * #### Example: + * + * User.where('age').eq(49); + * + * // is the same as + * + * User.shere('age').equals(49); + * + * // is the same as + * + * User.where('age', 49); + * + * @param {Object} val + * @return {Query} this + * @api public + */ + +Query.prototype.eq = function eq(val) { + this._ensurePath('eq'); + const path = this._path; + this._conditions[path] = val; + return this; +}; + +/** + * Specifies arguments for an `$or` condition. + * + * #### Example: + * + * query.or([{ color: 'red' }, { status: 'emergency' }]) + * + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.or = function or(array) { + const or = this._conditions.$or || (this._conditions.$or = []); + if (!Array.isArray(array)) array = [array]; + or.push.apply(or, array); + return this; +}; + +/** + * Specifies arguments for a `$nor` condition. + * + * #### Example: + * + * query.nor([{ color: 'green' }, { status: 'ok' }]) + * + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.nor = function nor(array) { + const nor = this._conditions.$nor || (this._conditions.$nor = []); + if (!Array.isArray(array)) array = [array]; + nor.push.apply(nor, array); + return this; +}; + +/** + * Specifies arguments for a `$and` condition. + * + * #### Example: + * + * query.and([{ color: 'green' }, { status: 'ok' }]) + * + * @see $and http://docs.mongodb.org/manual/reference/operator/and/ + * @param {Array} array array of conditions + * @return {Query} this + * @api public + */ + +Query.prototype.and = function and(array) { + const and = this._conditions.$and || (this._conditions.$and = []); + if (!Array.isArray(array)) array = [array]; + and.push.apply(and, array); + return this; +}; + +/** + * Specifies a $gt query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * #### Example: + * + * Thing.find().where('age').gt(21) + * + * // or + * Thing.find().gt('age', 21) + * + * @method gt + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $gte query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method gte + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $lt query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lt + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $lte query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method lte + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $ne query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method ne + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies an $in query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method in + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies an $nin query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method nin + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies an $all query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method all + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $size query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method size + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/** + * Specifies a $regex query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method regex + * @memberOf Query + * @param {String} [path] + * @param {String|RegExp} val + * @api public + */ + +/** + * Specifies a $maxDistance query condition. + * + * When called with one argument, the most recent path passed to `where()` is used. + * + * @method maxDistance + * @memberOf Query + * @param {String} [path] + * @param {Number} val + * @api public + */ + +/*! + * gt, gte, lt, lte, ne, in, nin, all, regex, size, maxDistance + * + * Thing.where('type').nin(array) + */ + +'gt gte lt lte ne in nin all regex size maxDistance minDistance'.split(' ').forEach(function($conditional) { + Query.prototype[$conditional] = function() { + let path, val; + + if (1 === arguments.length) { + this._ensurePath($conditional); + val = arguments[0]; + path = this._path; + } else { + val = arguments[1]; + path = arguments[0]; + } + + const conds = this._conditions[path] === null || typeof this._conditions[path] === 'object' ? + this._conditions[path] : + (this._conditions[path] = {}); + conds['$' + $conditional] = val; + return this; + }; +}); + +/** + * Specifies a `$mod` condition + * + * @param {String} [path] + * @param {Number} val + * @return {Query} this + * @api public + */ + +Query.prototype.mod = function() { + let val, path; + + if (1 === arguments.length) { + this._ensurePath('mod'); + val = arguments[0]; + path = this._path; + } else if (2 === arguments.length && !Array.isArray(arguments[1])) { + this._ensurePath('mod'); + val = [arguments[0], arguments[1]]; + path = this._path; + } else if (3 === arguments.length) { + val = [arguments[1], arguments[2]]; + path = arguments[0]; + } else { + val = arguments[1]; + path = arguments[0]; + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$mod = val; + return this; +}; + +/** + * Specifies an `$exists` condition + * + * #### Example: + * + * // { name: { $exists: true }} + * Thing.where('name').exists() + * Thing.where('name').exists(true) + * Thing.find().exists('name') + * + * // { name: { $exists: false }} + * Thing.where('name').exists(false); + * Thing.find().exists('name', false); + * + * @param {String} [path] + * @param {Number} val + * @return {Query} this + * @api public + */ + +Query.prototype.exists = function() { + let path, val; + + if (0 === arguments.length) { + this._ensurePath('exists'); + path = this._path; + val = true; + } else if (1 === arguments.length) { + if ('boolean' === typeof arguments[0]) { + this._ensurePath('exists'); + path = this._path; + val = arguments[0]; + } else { + path = arguments[0]; + val = true; + } + } else if (2 === arguments.length) { + path = arguments[0]; + val = arguments[1]; + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$exists = val; + return this; +}; + +/** + * Specifies an `$elemMatch` condition + * + * #### Example: + * + * query.elemMatch('comment', { author: 'autobot', votes: {$gte: 5}}) + * + * query.where('comment').elemMatch({ author: 'autobot', votes: {$gte: 5}}) + * + * query.elemMatch('comment', function (elem) { + * elem.where('author').equals('autobot'); + * elem.where('votes').gte(5); + * }) + * + * query.where('comment').elemMatch(function (elem) { + * elem.where({ author: 'autobot' }); + * elem.where('votes').gte(5); + * }) + * + * @param {String|Object|Function} path + * @param {Object|Function} criteria + * @return {Query} this + * @api public + */ + +Query.prototype.elemMatch = function() { + if (null == arguments[0]) + throw new TypeError('Invalid argument'); + + let fn, path, criteria; + + if ('function' === typeof arguments[0]) { + this._ensurePath('elemMatch'); + path = this._path; + fn = arguments[0]; + } else if (utils.isObject(arguments[0])) { + this._ensurePath('elemMatch'); + path = this._path; + criteria = arguments[0]; + } else if ('function' === typeof arguments[1]) { + path = arguments[0]; + fn = arguments[1]; + } else if (arguments[1] && utils.isObject(arguments[1])) { + path = arguments[0]; + criteria = arguments[1]; + } else { + throw new TypeError('Invalid argument'); + } + + if (fn) { + criteria = new Query; + fn(criteria); + criteria = criteria._conditions; + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds.$elemMatch = criteria; + return this; +}; + +// Spatial queries + +/** + * Sugar for geo-spatial queries. + * + * #### Example: + * + * query.within().box() + * query.within().circle() + * query.within().geometry() + * + * query.where('loc').within({ center: [50,50], radius: 10, unique: true, spherical: true }); + * query.where('loc').within({ box: [[40.73, -73.9], [40.7, -73.988]] }); + * query.where('loc').within({ polygon: [[],[],[],[]] }); + * + * query.where('loc').within([], [], []) // polygon + * query.where('loc').within([], []) // box + * query.where('loc').within({ type: 'LineString', coordinates: [...] }); // geometry + * + * #### Note: + * + * Must be used after `where()`. + * + * @memberOf Query + * @return {Query} this + * @api public + */ + +Query.prototype.within = function within() { + // opinionated, must be used after where + this._ensurePath('within'); + this._geoComparison = '$geoWithin'; + + if (0 === arguments.length) { + return this; + } + + if (2 === arguments.length) { + return this.box.apply(this, arguments); + } else if (2 < arguments.length) { + return this.polygon.apply(this, arguments); + } + + const area = arguments[0]; + + if (!area) + throw new TypeError('Invalid argument'); + + if (area.center) + return this.circle(area); + + if (area.box) + return this.box.apply(this, area.box); + + if (area.polygon) + return this.polygon.apply(this, area.polygon); + + if (area.type && area.coordinates) + return this.geometry(area); + + throw new TypeError('Invalid argument'); +}; + +/** + * Specifies a $box condition + * + * #### Example: + * + * var lowerLeft = [40.73083, -73.99756] + * var upperRight= [40.741404, -73.988135] + * + * query.where('loc').within().box(lowerLeft, upperRight) + * query.box('loc', lowerLeft, upperRight ) + * + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @see Query#within #query_Query-within + * @param {String} path + * @param {Object} val + * @return {Query} this + * @api public + */ + +Query.prototype.box = function() { + let path, box; + + if (3 === arguments.length) { + // box('loc', [], []) + path = arguments[0]; + box = [arguments[1], arguments[2]]; + } else if (2 === arguments.length) { + // box([], []) + this._ensurePath('box'); + path = this._path; + box = [arguments[0], arguments[1]]; + } else { + throw new TypeError('Invalid argument'); + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds[this._geoComparison || '$geoWithin'] = { $box: box }; + return this; +}; + +/** + * Specifies a $polygon condition + * + * #### Example: + * + * query.where('loc').within().polygon([10,20], [13, 25], [7,15]) + * query.polygon('loc', [10,20], [13, 25], [7,15]) + * + * @param {String|Array} [path] + * @param {Array|Object} [val] + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.polygon = function() { + let val, path; + + if ('string' == typeof arguments[0]) { + // polygon('loc', [],[],[]) + val = Array.from(arguments); + path = val.shift(); + } else { + // polygon([],[],[]) + this._ensurePath('polygon'); + path = this._path; + val = Array.from(arguments); + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds[this._geoComparison || '$geoWithin'] = { $polygon: val }; + return this; +}; + +/** + * Specifies a $center or $centerSphere condition. + * + * #### Example: + * + * var area = { center: [50, 50], radius: 10, unique: true } + * query.where('loc').within().circle(area) + * query.center('loc', area); + * + * // for spherical calculations + * var area = { center: [50, 50], radius: 10, unique: true, spherical: true } + * query.where('loc').within().circle(area) + * query.center('loc', area); + * + * @param {String} [path] + * @param {Object} area + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.circle = function() { + let path, val; + + if (1 === arguments.length) { + this._ensurePath('circle'); + path = this._path; + val = arguments[0]; + } else if (2 === arguments.length) { + path = arguments[0]; + val = arguments[1]; + } else { + throw new TypeError('Invalid argument'); + } + + if (!('radius' in val && val.center)) + throw new Error('center and radius are required'); + + const conds = this._conditions[path] || (this._conditions[path] = {}); + + const type = val.spherical + ? '$centerSphere' + : '$center'; + + const wKey = this._geoComparison || '$geoWithin'; + conds[wKey] = {}; + conds[wKey][type] = [val.center, val.radius]; + + if ('unique' in val) + conds[wKey].$uniqueDocs = !!val.unique; + + return this; +}; + +/** + * Specifies a `$near` or `$nearSphere` condition + * + * These operators return documents sorted by distance. + * + * #### Example: + * + * query.where('loc').near({ center: [10, 10] }); + * query.where('loc').near({ center: [10, 10], maxDistance: 5 }); + * query.where('loc').near({ center: [10, 10], maxDistance: 5, spherical: true }); + * query.near('loc', { center: [10, 10], maxDistance: 5 }); + * query.near({ center: { type: 'Point', coordinates: [..] }}) + * query.near().geometry({ type: 'Point', coordinates: [..] }) + * + * @param {String} [path] + * @param {Object} val + * @return {Query} this + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @api public + */ + +Query.prototype.near = function near() { + let path, val; + + this._geoComparison = '$near'; + + if (0 === arguments.length) { + return this; + } else if (1 === arguments.length) { + this._ensurePath('near'); + path = this._path; + val = arguments[0]; + } else if (2 === arguments.length) { + path = arguments[0]; + val = arguments[1]; + } else { + throw new TypeError('Invalid argument'); + } + + if (!val.center) { + throw new Error('center is required'); + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + + const type = val.spherical + ? '$nearSphere' + : '$near'; + + // center could be a GeoJSON object or an Array + if (Array.isArray(val.center)) { + conds[type] = val.center; + + const radius = 'maxDistance' in val + ? val.maxDistance + : null; + + if (null != radius) { + conds.$maxDistance = radius; + } + if (null != val.minDistance) { + conds.$minDistance = val.minDistance; + } + } else { + // GeoJSON? + if (val.center.type != 'Point' || !Array.isArray(val.center.coordinates)) { + throw new Error(util.format('Invalid GeoJSON specified for %s', type)); + } + conds[type] = { $geometry: val.center }; + + // MongoDB 2.6 insists on maxDistance being in $near / $nearSphere + if ('maxDistance' in val) { + conds[type]['$maxDistance'] = val.maxDistance; + } + if ('minDistance' in val) { + conds[type]['$minDistance'] = val.minDistance; + } + } + + return this; +}; + +/** + * Declares an intersects query for `geometry()`. + * + * #### Example: + * + * query.where('path').intersects().geometry({ + * type: 'LineString' + * , coordinates: [[180.0, 11.0], [180, 9.0]] + * }) + * + * query.where('path').intersects({ + * type: 'LineString' + * , coordinates: [[180.0, 11.0], [180, 9.0]] + * }) + * + * @param {Object} [arg] + * @return {Query} this + * @api public + */ + +Query.prototype.intersects = function intersects() { + // opinionated, must be used after where + this._ensurePath('intersects'); + + this._geoComparison = '$geoIntersects'; + + if (0 === arguments.length) { + return this; + } + + const area = arguments[0]; + + if (null != area && area.type && area.coordinates) + return this.geometry(area); + + throw new TypeError('Invalid argument'); +}; + +/** + * Specifies a `$geometry` condition + * + * #### Example: + * + * var polyA = [[[ 10, 20 ], [ 10, 40 ], [ 30, 40 ], [ 30, 20 ]]] + * query.where('loc').within().geometry({ type: 'Polygon', coordinates: polyA }) + * + * // or + * var polyB = [[ 0, 0 ], [ 1, 1 ]] + * query.where('loc').within().geometry({ type: 'LineString', coordinates: polyB }) + * + * // or + * var polyC = [ 0, 0 ] + * query.where('loc').within().geometry({ type: 'Point', coordinates: polyC }) + * + * // or + * query.where('loc').intersects().geometry({ type: 'Point', coordinates: polyC }) + * + * #### Note: + * + * `geometry()` **must** come after either `intersects()` or `within()`. + * + * The `object` argument must contain `type` and `coordinates` properties. + * - type {String} + * - coordinates {Array} + * + * The most recent path passed to `where()` is used. + * + * @param {Object} object Must contain a `type` property which is a String and a `coordinates` property which is an Array. See the examples. + * @return {Query} this + * @see http://docs.mongodb.org/manual/release-notes/2.4/#new-geospatial-indexes-with-geojson-and-improved-spherical-geometry + * @see http://www.mongodb.org/display/DOCS/Geospatial+Indexing + * @see $geometry http://docs.mongodb.org/manual/reference/operator/geometry/ + * @api public + */ + +Query.prototype.geometry = function geometry() { + if (!('$within' == this._geoComparison || + '$geoWithin' == this._geoComparison || + '$near' == this._geoComparison || + '$geoIntersects' == this._geoComparison)) { + throw new Error('geometry() must come after `within()`, `intersects()`, or `near()'); + } + + let val, path; + + if (1 === arguments.length) { + this._ensurePath('geometry'); + path = this._path; + val = arguments[0]; + } else { + throw new TypeError('Invalid argument'); + } + + if (!(val.type && Array.isArray(val.coordinates))) { + throw new TypeError('Invalid argument'); + } + + const conds = this._conditions[path] || (this._conditions[path] = {}); + conds[this._geoComparison] = { $geometry: val }; + + return this; +}; + +// end spatial + +/** + * Specifies which document fields to include or exclude + * + * #### String syntax + * + * When passing a string, prefixing a path with `-` will flag that path as excluded. When a path does not have the `-` prefix, it is included. + * + * #### Example: + * + * // include a and b, exclude c + * query.select('a b -c'); + * + * // or you may use object notation, useful when + * // you have keys already prefixed with a "-" + * query.select({a: 1, b: 1, c: 0}); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @param {Object|String} arg + * @return {Query} this + * @see SchemaType + * @api public + */ + +Query.prototype.select = function select() { + let arg = arguments[0]; + if (!arg) return this; + + if (arguments.length !== 1) { + throw new Error('Invalid select: select only takes 1 argument'); + } + + this._validate('select'); + + const fields = this._fields || (this._fields = {}); + const type = typeof arg; + let i, len; + + if (('string' == type || utils.isArgumentsObject(arg)) && + 'number' == typeof arg.length || Array.isArray(arg)) { + if ('string' == type) + arg = arg.split(/\s+/); + + for (i = 0, len = arg.length; i < len; ++i) { + let field = arg[i]; + if (!field) continue; + const include = '-' == field[0] ? 0 : 1; + if (include === 0) field = field.substring(1); + fields[field] = include; + } + + return this; + } + + if (utils.isObject(arg)) { + const keys = utils.keys(arg); + for (i = 0; i < keys.length; ++i) { + fields[keys[i]] = arg[keys[i]]; + } + return this; + } + + throw new TypeError('Invalid select() argument. Must be string or object.'); +}; + +/** + * Specifies a $slice condition for a `path` + * + * #### Example: + * + * query.slice('comments', 5) + * query.slice('comments', -5) + * query.slice('comments', [10, 5]) + * query.where('comments').slice(5) + * query.where('comments').slice([-10, 5]) + * + * @param {String} [path] + * @param {Number} val number/range of elements to slice + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields#RetrievingaSubsetofFields-RetrievingaSubrangeofArrayElements + * @api public + */ + +Query.prototype.slice = function() { + if (0 === arguments.length) + return this; + + this._validate('slice'); + + let path, val; + + if (1 === arguments.length) { + const arg = arguments[0]; + if (typeof arg === 'object' && !Array.isArray(arg)) { + const keys = Object.keys(arg); + const numKeys = keys.length; + for (let i = 0; i < numKeys; ++i) { + this.slice(keys[i], arg[keys[i]]); + } + return this; + } + this._ensurePath('slice'); + path = this._path; + val = arguments[0]; + } else if (2 === arguments.length) { + if ('number' === typeof arguments[0]) { + this._ensurePath('slice'); + path = this._path; + val = [arguments[0], arguments[1]]; + } else { + path = arguments[0]; + val = arguments[1]; + } + } else if (3 === arguments.length) { + path = arguments[0]; + val = [arguments[1], arguments[2]]; + } + + const myFields = this._fields || (this._fields = {}); + myFields[path] = { $slice: val }; + return this; +}; + +/** + * Sets the sort order + * + * If an object is passed, values allowed are 'asc', 'desc', 'ascending', 'descending', 1, and -1. + * + * If a string is passed, it must be a space delimited list of path names. The sort order of each path is ascending unless the path name is prefixed with `-` which will be treated as descending. + * + * #### Example: + * + * // these are equivalent + * query.sort({ field: 'asc', test: -1 }); + * query.sort('field -test'); + * query.sort([['field', 1], ['test', -1]]); + * + * #### Note: + * + * - The array syntax `.sort([['field', 1], ['test', -1]])` can only be used with [mongodb driver >= 2.0.46](https://github.com/mongodb/node-mongodb-native/blob/2.1/HISTORY.md#2046-2015-10-15). + * - Cannot be used with `distinct()` + * + * @param {Object|String|Array} arg + * @return {Query} this + * @api public + */ + +Query.prototype.sort = function(arg) { + if (!arg) return this; + let i, len, field; + + this._validate('sort'); + + const type = typeof arg; + + // .sort([['field', 1], ['test', -1]]) + if (Array.isArray(arg)) { + len = arg.length; + for (i = 0; i < arg.length; ++i) { + if (!Array.isArray(arg[i])) { + throw new Error('Invalid sort() argument, must be array of arrays'); + } + _pushArr(this.options, arg[i][0], arg[i][1]); + } + return this; + } + + // .sort('field -test') + if (1 === arguments.length && 'string' == type) { + arg = arg.split(/\s+/); + len = arg.length; + for (i = 0; i < len; ++i) { + field = arg[i]; + if (!field) continue; + const ascend = '-' == field[0] ? -1 : 1; + if (ascend === -1) field = field.substring(1); + push(this.options, field, ascend); + } + + return this; + } + + // .sort({ field: 1, test: -1 }) + if (utils.isObject(arg)) { + const keys = utils.keys(arg); + for (i = 0; i < keys.length; ++i) { + field = keys[i]; + push(this.options, field, arg[field]); + } + + return this; + } + + if (typeof Map !== 'undefined' && arg instanceof Map) { + _pushMap(this.options, arg); + return this; + } + throw new TypeError('Invalid sort() argument. Must be a string, object, or array.'); +}; + +/*! + * @ignore + */ + +const _validSortValue = { + 1: 1, + '-1': -1, + asc: 1, + ascending: 1, + desc: -1, + descending: -1 +}; + +function push(opts, field, value) { + if (Array.isArray(opts.sort)) { + throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' + + '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' + + '\n- `.sort({ field: 1, test: -1 })`'); + } + + let s; + if (value && value.$meta) { + s = opts.sort || (opts.sort = {}); + s[field] = { $meta: value.$meta }; + return; + } + + s = opts.sort || (opts.sort = {}); + let val = String(value || 1).toLowerCase(); + val = _validSortValue[val]; + if (!val) throw new TypeError('Invalid sort value: { ' + field + ': ' + value + ' }'); + + s[field] = val; +} + +function _pushArr(opts, field, value) { + opts.sort = opts.sort || []; + if (!Array.isArray(opts.sort)) { + throw new TypeError('Can\'t mix sort syntaxes. Use either array or object:' + + '\n- `.sort([[\'field\', 1], [\'test\', -1]])`' + + '\n- `.sort({ field: 1, test: -1 })`'); + } + + let val = String(value || 1).toLowerCase(); + val = _validSortValue[val]; + if (!val) throw new TypeError('Invalid sort value: [ ' + field + ', ' + value + ' ]'); + + opts.sort.push([field, val]); +} + +function _pushMap(opts, map) { + opts.sort = opts.sort || new Map(); + if (!(opts.sort instanceof Map)) { + throw new TypeError('Can\'t mix sort syntaxes. Use either array or ' + + 'object or map consistently'); + } + map.forEach(function(value, key) { + let val = String(value || 1).toLowerCase(); + val = _validSortValue[val]; + if (!val) throw new TypeError('Invalid sort value: < ' + key + ': ' + value + ' >'); + + opts.sort.set(key, val); + }); +} + + +/** + * Specifies the limit option. + * + * #### Example: + * + * query.limit(20) + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method limit + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Blimit%28%29%7D%7D + * @api public + */ +/** + * Specifies the skip option. + * + * #### Example: + * + * query.skip(100).limit(20) + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method skip + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7Bskip%28%29%7D%7D + * @api public + */ +/** + * Specifies the batchSize option. + * + * #### Example: + * + * query.batchSize(100) + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method batchSize + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%7B%7BbatchSize%28%29%7D%7D + * @api public + */ +/** + * Specifies the `comment` option. + * + * #### Example: + * + * query.comment('login query') + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @method comment + * @memberOf Query + * @param {Number} val + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24comment + * @api public + */ + +/*! + * limit, skip, batchSize, comment + * + * Sets these associated options. + * + * query.comment('feed query'); + */ + +['limit', 'skip', 'batchSize', 'comment'].forEach(function(method) { + Query.prototype[method] = function(v) { + this._validate(method); + this.options[method] = v; + return this; + }; +}); + +/** + * Specifies the maxTimeMS option. + * + * #### Example: + * + * query.maxTime(100) + * query.maxTimeMS(100) + * + * @method maxTime + * @memberOf Query + * @param {Number} ms + * @see mongodb http://docs.mongodb.org/manual/reference/operator/meta/maxTimeMS/#op._S_maxTimeMS + * @api public + */ + +Query.prototype.maxTime = Query.prototype.maxTimeMS = function(ms) { + this._validate('maxTime'); + this.options.maxTimeMS = ms; + return this; +}; + +/** + * Sets query hints. + * + * #### Example: + * + * query.hint({ indexA: 1, indexB: -1}); + * query.hint('indexA_1_indexB_1'); + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @param {Object|string} val a hint object or the index name + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Advanced+Queries#AdvancedQueries-%24hint + * @api public + */ + +Query.prototype.hint = function() { + if (0 === arguments.length) return this; + + this._validate('hint'); + + const arg = arguments[0]; + if (utils.isObject(arg)) { + const hint = this.options.hint || (this.options.hint = {}); + + // must keep object keys in order so don't use Object.keys() + for (const k in arg) { + hint[k] = arg[k]; + } + + return this; + } + if (typeof arg === 'string') { + this.options.hint = arg; + return this; + } + + throw new TypeError('Invalid hint. ' + arg); +}; + +/** + * Requests acknowledgement that this operation has been persisted to MongoDB's + * on-disk journal. + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the `j` value if it is specified in writeConcern options + * + * #### Example: + * + * mquery().w(2).j(true).wtimeout(2000); + * + * @method j + * @memberOf Query + * @instance + * @param {boolean} val + * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#j-option + * @return {Query} this + * @api public + */ + +Query.prototype.j = function j(val) { + this.options.j = val; + return this; +}; + +/** + * Sets the slaveOk option. _Deprecated_ in MongoDB 2.2 in favor of read preferences. + * + * #### Example: + * + * query.slaveOk() // true + * query.slaveOk(true) + * query.slaveOk(false) + * + * @deprecated use read() preferences instead if on mongodb >= 2.2 + * @param {Boolean} v defaults to true + * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference + * @see read() + * @return {Query} this + * @api public + */ + +Query.prototype.slaveOk = function(v) { + this.options.slaveOk = arguments.length ? !!v : true; + return this; +}; + +/** + * Sets the readPreference option for the query. + * + * #### Example: + * + * new Query().read('primary') + * new Query().read('p') // same as primary + * + * new Query().read('primaryPreferred') + * new Query().read('pp') // same as primaryPreferred + * + * new Query().read('secondary') + * new Query().read('s') // same as secondary + * + * new Query().read('secondaryPreferred') + * new Query().read('sp') // same as secondaryPreferred + * + * new Query().read('nearest') + * new Query().read('n') // same as nearest + * + * // you can also use mongodb.ReadPreference class to also specify tags + * new Query().read(mongodb.ReadPreference('secondary', [{ dc:'sf', s: 1 },{ dc:'ma', s: 2 }])) + * + * new Query().setReadPreference('primary') // alias of .read() + * + * #### Preferences: + * + * primary - (default) Read from primary only. Operations will produce an error if primary is unavailable. Cannot be combined with tags. + * secondary Read from secondary if available, otherwise error. + * primaryPreferred Read from primary if available, otherwise a secondary. + * secondaryPreferred Read from a secondary if available, otherwise read from the primary. + * nearest All operations read from among the nearest candidates, but unlike other modes, this option will include both the primary and all secondaries in the random selection. + * + * Aliases + * + * p primary + * pp primaryPreferred + * s secondary + * sp secondaryPreferred + * n nearest + * + * Read more about how to use read preferences [here](http://docs.mongodb.org/manual/applications/replication/#read-preference) and [here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences). + * + * @param {String|ReadPreference} pref one of the listed preference options or their aliases + * @see mongodb http://docs.mongodb.org/manual/applications/replication/#read-preference + * @see driver http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences + * @return {Query} this + * @api public + */ + +Query.prototype.read = Query.prototype.setReadPreference = function(pref) { + if (arguments.length > 1 && !Query.prototype.read.deprecationWarningIssued) { + console.error('Deprecation warning: \'tags\' argument is not supported anymore in Query.read() method. Please use mongodb.ReadPreference object instead.'); + Query.prototype.read.deprecationWarningIssued = true; + } + this.options.readPreference = utils.readPref(pref); + return this; +}; + +/** + * Sets the readConcern option for the query. + * + * #### Example: + * + * new Query().readConcern('local') + * new Query().readConcern('l') // same as local + * + * new Query().readConcern('available') + * new Query().readConcern('a') // same as available + * + * new Query().readConcern('majority') + * new Query().readConcern('m') // same as majority + * + * new Query().readConcern('linearizable') + * new Query().readConcern('lz') // same as linearizable + * + * new Query().readConcern('snapshot') + * new Query().readConcern('s') // same as snapshot + * + * new Query().r('s') // r is alias of readConcern + * + * + * #### Read Concern Level: + * + * local MongoDB 3.2+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). + * available MongoDB 3.6+ The query returns from the instance with no guarantee guarantee that the data has been written to a majority of the replica set members (i.e. may be rolled back). + * majority MongoDB 3.2+ The query returns the data that has been acknowledged by a majority of the replica set members. The documents returned by the read operation are durable, even in the event of failure. + * linearizable MongoDB 3.4+ The query returns data that reflects all successful majority-acknowledged writes that completed prior to the start of the read operation. The query may wait for concurrently executing writes to propagate to a majority of replica set members before returning results. + * snapshot MongoDB 4.0+ Only available for operations within multi-document transactions. Upon transaction commit with write concern "majority", the transaction operations are guaranteed to have read from a snapshot of majority-committed data. + * + * Aliases + * + * l local + * a available + * m majority + * lz linearizable + * s snapshot + * + * Read more about how to use read concern [here](https://docs.mongodb.com/manual/reference/read-concern/). + * + * @param {String} level one of the listed read concern level or their aliases + * @see mongodb https://docs.mongodb.com/manual/reference/read-concern/ + * @return {Query} this + * @api public + */ + +Query.prototype.readConcern = Query.prototype.r = function(level) { + this.options.readConcern = utils.readConcern(level); + return this; +}; + +/** + * Sets tailable option. + * + * #### Example: + * + * query.tailable() <== true + * query.tailable(true) + * query.tailable(false) + * + * #### Note: + * + * Cannot be used with `distinct()` + * + * @param {Boolean} v defaults to true + * @see mongodb http://www.mongodb.org/display/DOCS/Tailable+Cursors + * @api public + */ + +Query.prototype.tailable = function() { + this._validate('tailable'); + + this.options.tailable = arguments.length + ? !!arguments[0] + : true; + + return this; +}; + +/** + * Sets the specified number of `mongod` servers, or tag set of `mongod` servers, + * that must acknowledge this write before this write is considered successful. + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to the `w` value if it is specified in writeConcern options + * + * #### Example: + * + * mquery().writeConcern(0) + * mquery().writeConcern(1) + * mquery().writeConcern({ w: 1, j: true, wtimeout: 2000 }) + * mquery().writeConcern('majority') + * mquery().writeConcern('m') // same as majority + * mquery().writeConcern('tagSetName') // if the tag set is 'm', use .writeConcern({ w: 'm' }) instead + * mquery().w(1) // w is alias of writeConcern + * + * @method writeConcern + * @memberOf Query + * @instance + * @param {String|number|object} concern 0 for fire-and-forget, 1 for acknowledged by one server, 'majority' for majority of the replica set, or [any of the more advanced options](https://docs.mongodb.com/manual/reference/write-concern/#w-option). + * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#w-option + * @return {Query} this + * @api public + */ + +Query.prototype.writeConcern = Query.prototype.w = function writeConcern(concern) { + if ('object' === typeof concern) { + if ('undefined' !== typeof concern.j) this.options.j = concern.j; + if ('undefined' !== typeof concern.w) this.options.w = concern.w; + if ('undefined' !== typeof concern.wtimeout) this.options.wtimeout = concern.wtimeout; + } else { + this.options.w = 'm' === concern ? 'majority' : concern; + } + return this; +}; + +/** + * Specifies a time limit, in milliseconds, for the write concern. + * If `ms > 1`, it is maximum amount of time to wait for this write + * to propagate through the replica set before this operation fails. + * The default is `0`, which means no timeout. + * + * This option is only valid for operations that write to the database: + * + * - `deleteOne()` + * - `deleteMany()` + * - `findOneAndDelete()` + * - `findOneAndUpdate()` + * - `updateOne()` + * - `updateMany()` + * + * Defaults to `wtimeout` value if it is specified in writeConcern + * + * #### Example: + * + * mquery().w(2).j(true).wtimeout(2000) + * + * @method wtimeout + * @memberOf Query + * @instance + * @param {number} ms number of milliseconds to wait + * @see mongodb https://docs.mongodb.com/manual/reference/write-concern/#wtimeout + * @return {Query} this + * @api public + */ + +Query.prototype.wtimeout = Query.prototype.wTimeout = function wtimeout(ms) { + this.options.wtimeout = ms; + return this; +}; + +/** + * Merges another Query or conditions object into this one. + * + * When a Query is passed, conditions, field selection and options are merged. + * + * @param {Query|Object} source + * @return {Query} this + */ + +Query.prototype.merge = function(source) { + if (!source) + return this; + + if (!Query.canMerge(source)) + throw new TypeError('Invalid argument. Expected instanceof mquery or plain object'); + + if (source instanceof Query) { + // if source has a feature, apply it to ourselves + + if (source._conditions) { + utils.merge(this._conditions, source._conditions); + } + + if (source._fields) { + this._fields || (this._fields = {}); + utils.merge(this._fields, source._fields); + } + + if (source.options) { + this.options || (this.options = {}); + utils.merge(this.options, source.options); + } + + if (source._updateDoc) { + this._updateDoc || (this._updateDoc = {}); + utils.mergeClone(this._updateDoc, source._updateDoc); + } + + if (source._distinctDoc) { + this._distinctDoc = source._distinctDoc; + } + + return this; + } + + // plain object + utils.merge(this._conditions, source); + + return this; +}; + +/** + * Finds documents. + * + * #### Example: + * + * query.find() + * await query.find() + * await query.find({ name: 'Burning Lights' }) + * + * @param {Object} [criteria] mongodb selector + * @return {Query} this + * @api public + */ + +Query.prototype.find = function(criteria) { + this.op = 'find'; + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + return this; +}; + +/** + * Executes a `find` Query + * @returns the result + */ +Query.prototype._find = async function _find() { + const conds = this._conditions; + const options = this._optionsForExec(); + + if (this.$useProjection) { + options.projection = this._fieldsForExec(); + } else { + options.fields = this._fieldsForExec(); + } + + debug('find', this._collection.collectionName, conds, options); + + return this._collection.find(conds, options); +}; + +/** + * Returns the query cursor + * + * #### Examples: + * + * query.find().cursor(); + * query.cursor({ name: 'Burning Lights' }); + * + * @param {Object} [criteria] mongodb selector + * @return {Object} cursor + * @api public + */ + +Query.prototype.cursor = function cursor(criteria) { + if (this.op) { + if (this.op !== 'find') { + throw new TypeError('.cursor only support .find method'); + } + } else { + this.find(criteria); + } + + const conds = this._conditions; + const options = this._optionsForExec(); + + if (this.$useProjection) { + options.projection = this._fieldsForExec(); + } else { + options.fields = this._fieldsForExec(); + } + + debug('findCursor', this._collection.collectionName, conds, options); + + return this._collection.findCursor(conds, options); +}; + +/** + * Executes the query as a findOne() operation. + * + * #### Example: + * + * query.findOne().where('name', /^Burning/); + * + * query.findOne({ name: /^Burning/ }) + * + * await query.findOne({ name: /^Burning/ }); // executes + * + * @param {Object|Query} [criteria] mongodb selector + * @return {Query} this + * @api public + */ + +Query.prototype.findOne = function(criteria) { + this.op = 'findOne'; + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + return this; +}; + +/** + * Executes a `findOne` Query + * @returns the results + */ +Query.prototype._findOne = async function _findOne() { + const conds = this._conditions; + const options = this._optionsForExec(); + + if (this.$useProjection) { + options.projection = this._fieldsForExec(); + } else { + options.fields = this._fieldsForExec(); + } + + debug('findOne', this._collection.collectionName, conds, options); + + return this._collection.findOne(conds, options); +}; + +/** + * Executes the query as a countDocuments() operation. + * + * #### Example: + * + * query.countDocuments().where('color', 'black').exec(); + * + * query.countDocuments({ color: 'black' }) + * + * await query.countDocuments({ color: 'black' }); + * + * const count = await query.where('color', 'black').countDocuments(); + * console.log('there are %d kittens', count); + * + * @param {Object} [filter] mongodb selector + * @return {Query} this + * @api public + */ + +Query.prototype.countDocuments = function(filter) { + this.op = 'countDocuments'; + this._validate(); + + if (Query.canMerge(filter)) { + this.merge(filter); + } + + return this; +}; + +/** + * Executes a `countDocuments` Query + * @returns the results + */ +Query.prototype._countDocuments = async function _countDocuments() { + const conds = this._conditions; + const options = this._optionsForExec(); + + debug('countDocuments', this._collection.collectionName, conds, options); + + return this._collection.countDocuments(conds, options); +}; + +/** + * Executes the query as a estimatedDocumentCount() operation. + * + * #### Example: + * + * query.estimatedDocumentCount(); + * + * const count = await query.estimatedDocumentCount(); + * console.log('there are %d kittens', count); + * + * @return {Query} this + * @api public + */ + +Query.prototype.estimatedDocumentCount = function() { + this.op = 'estimatedDocumentCount'; + this._validate(); + + return this; +}; + +/** + * Executes a `count` Query + * @returns the results + */ +Query.prototype._estimatedDocumentCount = async function _estimatedDocumentCount() { + const conds = this._conditions; + const options = this._optionsForExec(); + debug('estimatedDocumentCount', this._collection.collectionName, conds, options); + return this._collection.estimatedDocumentCount(conds, options); +}; + +/** + * Declares or executes a distinct() operation. + * + * #### Example: + * + * await distinct(criteria, field) + * distinct(criteria, field) + * await distinct(field) + * distinct(field) + * await distinct() + * distinct() + * + * @param {Object|Query} [criteria] + * @param {String} [field] + * @return {Query} this + * @see mongodb http://www.mongodb.org/display/DOCS/Aggregation#Aggregation-Distinct + * @api public + */ + +Query.prototype.distinct = function(criteria, field) { + this.op = 'distinct'; + this._validate(); + + if (!field && typeof criteria === 'string') { + field = criteria; + criteria = undefined; + } + + if ('string' == typeof field) { + this._distinctDoc = field; + } + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + return this; +}; + +/** + * Executes a `distinct` Query + * @returns the results + */ +Query.prototype._distinct = async function _distinct() { + if (!this._distinctDoc) { + throw new Error('No value for `distinct` has been declared'); + } + + const conds = this._conditions, + options = this._optionsForExec(); + + return this._collection.distinct(this._distinctDoc, conds, options); +}; + +/** + * Declare and/or execute this query as an `updateMany()` operation. This function will update _all_ documents that match + * `criteria`, rather than just the first one. + * + * _All paths passed that are not $atomic operations will become $set ops._ + * + * #### Example: + * + * // Update every document whose `title` contains 'test' + * mquery().updateMany({ title: /test/ }, { year: 2017 }) + * + * @param {Object} [criteria] + * @param {Object} [doc] the update command + * @param {Object} [options] + * @return {Query} this + * @api public + */ + +Query.prototype.updateMany = function updateMany(criteria, doc, options) { + return _update(this, 'updateMany', criteria, doc, options); +}; + +/** + * Executes a `updateMany` Query + * @returns the results + */ +Query.prototype._updateMany = async function() { + return _updateExec(this, 'updateMany'); +}; + +/** + * Declare and/or execute this query as an `updateOne()` operation. This function will _always_ update just one document, + * regardless of the `multi` option. + * + * _All paths passed that are not $atomic operations will become $set ops._ + * + * #### Example: + * + * // Update the first document whose `title` contains 'test' + * mquery().updateMany({ title: /test/ }, { year: 2017 }) + * + * @param {Object} [criteria] + * @param {Object} [doc] the update command + * @param {Object} [options] + * @return {Query} this + * @api public + */ + +Query.prototype.updateOne = function updateOne(criteria, doc, options) { + return _update(this, 'updateOne', criteria, doc, options); +}; + +/** + * Executes a `updateOne` Query + * @returns the results + */ +Query.prototype._updateOne = async function() { + return _updateExec(this, 'updateOne'); +}; + +/** + * Declare and/or execute this query as an `replaceOne()` operation. Similar + * to `updateOne()`, except `replaceOne()` is not allowed to use atomic + * modifiers (`$set`, `$push`, etc.). Calling `replaceOne()` will always + * replace the existing doc. + * + * #### Example: + * + * // Replace the document with `_id` 1 with `{ _id: 1, year: 2017 }` + * mquery().replaceOne({ _id: 1 }, { year: 2017 }) + * + * @param {Object} [criteria] + * @param {Object} [doc] the update command + * @param {Object} [options] + * @return {Query} this + * @api public + */ + +Query.prototype.replaceOne = function replaceOne(criteria, doc, options) { + this.setOptions({ overwrite: true }); + return _update(this, 'replaceOne', criteria, doc, options); +}; + +/** + * Executes a `replaceOne` Query + * @returns the results + */ +Query.prototype._replaceOne = async function() { + return _updateExec(this, 'replaceOne'); +}; + +/*! + * Internal helper for updateMany, updateOne + */ + +function _update(query, op, criteria, doc, options) { + query.op = op; + + if (Query.canMerge(criteria)) { + query.merge(criteria); + } + + if (doc) { + query._mergeUpdate(doc); + } + + if (utils.isObject(options)) { + // { overwrite: true } + query.setOptions(options); + } + + return query; +} + +/** + * Helper for de-duplicating "update*" functions + * @param {Query} query The Query Object (replacement for "this") + * @param {String} op The Operation to be done + * @returns the results + */ +async function _updateExec(query, op) { + const options = query._optionsForExec(); + + const criteria = query._conditions; + const doc = query._updateForExec(); + + debug(op, query._collection.collectionName, criteria, doc, options); + return query._collection[op](criteria, doc, options); +} + +/** + * Declare and/or execute this query as a `deleteOne()` operation. + * + * #### Example: + * + * await mquery(collection).deleteOne({ artist: 'Anne Murray' }) + * + * @param {Object|Query} [criteria] mongodb selector + * @return {Query} this + * @api public + */ + +Query.prototype.deleteOne = function(criteria) { + this.op = 'deleteOne'; + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + return this; +}; + +/** + * Executes a `deleteOne` Query + * @returns the results + */ +Query.prototype._deleteOne = async function() { + const options = this._optionsForExec(); + delete options.justOne; + + const conds = this._conditions; + + debug('deleteOne', this._collection.collectionName, conds, options); + return this._collection.deleteOne(conds, options); +}; + +/** + * Declare and/or execute this query as a `deleteMany()` operation. Always deletes + * _every_ document that matches `criteria`. + * + * #### Example: + * + * await mquery(collection).deleteMany({ artist: 'Anne Murray' }) + * + * @param {Object|Query} [criteria] mongodb selector + * @return {Query} this + * @api public + */ + +Query.prototype.deleteMany = function(criteria) { + this.op = 'deleteMany'; + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + return this; +}; + +/** + * Executes a `deleteMany` Query + * @returns the results + */ +Query.prototype._deleteMany = async function() { + const options = this._optionsForExec(); + delete options.justOne; + + const conds = this._conditions; + + return this._collection.deleteMany(conds, options); +}; + +/** + * Issues a mongodb findOneAndUpdate command. + * + * Finds a matching document, updates it according to the `update` arg, passing any `options`, and returns the found document (if any). + * + * #### Available options + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * #### Examples: + * + * await query.findOneAndUpdate(conditions, update, options) // executes + * query.findOneAndUpdate(conditions, update, options) // returns Query + * await query.findOneAndUpdate(conditions, update) // executes + * query.findOneAndUpdate(conditions, update) // returns Query + * await query.findOneAndUpdate(update) // returns Query + * query.findOneAndUpdate(update) // returns Query + * await query.findOneAndUpdate() // executes + * query.findOneAndUpdate() // returns Query + * + * @param {Object|Query} [query] + * @param {Object} [doc] + * @param {Object} [options] + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndUpdate = function(criteria, doc, options) { + this.op = 'findOneAndUpdate'; + this._validate(); + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + // apply doc + if (doc) { + this._mergeUpdate(doc); + } + + options && this.setOptions(options); + + return this; +}; + +/** + * Executes a `findOneAndUpdate` Query + * @returns the results + */ +Query.prototype._findOneAndUpdate = async function() { + const conds = this._conditions; + const update = this._updateForExec(); + const options = this._optionsForExec(); + + return this._collection.findOneAndUpdate(conds, update, options); +}; + +/** + * Issues a mongodb findOneAndReplace command. + * + * Finds a matching document, replaces it according to the `replacement` arg, passing any `options`, and returns the found document (if any). + * + * #### Available options + * + * - `new`: bool - true to return the modified document rather than the original. defaults to true + * - `upsert`: bool - creates the object if it doesn't exist. defaults to false. + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * #### Examples: + * + * await query.findOneAndReplace(conditions, replacement, options) // executes + * query.findOneAndReplace(conditions, replacement, options) // returns Query + * await query.findOneAndReplace(conditions, replacement) // executes + * query.findOneAndReplace(conditions, replacement) // returns Query + * await query.findOneAndReplace(replacement) // returns Query + * query.findOneAndReplace(replacement) // returns Query + * await query.findOneAndReplace() // executes + * query.findOneAndReplace() // returns Query + * + * @param {Object|Query} [query] + * @param {Object} [replacement] + * @param {Object} [options] + * @see mongodb http://www.mongodb.org/display/DOCS/findAndModify+Command + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndReplace = function(criteria, replacement, options) { + this.op = 'findOneAndReplace'; + this._validate(); + + if (Query.canMerge(criteria)) { + this.merge(criteria); + } + + // apply replacement + if (replacement) { + this._updateDoc = replacement; + this.options = this.options || {}; + this.options.overwrite = true; + } + + options && this.setOptions(options); + + return this; +}; + +/** + * Executes a `findOneAndReplace` Query + * @returns the results + */ +Query.prototype._findOneAndReplace = async function() { + const conds = this._conditions; + const replacement = this._updateForExec(); + const options = this._optionsForExec(); + + debug('findOneAndReplace', this._collection.collectionName, conds, replacement, options); + return this._collection.findOneAndReplace(conds, replacement, options); +}; + +/** + * Issues a mongodb findOneAndDelete. + * + * Finds a matching document, removes it, returning the found document (if any). + * + * #### Available options + * + * - `sort`: if multiple docs are found by the conditions, sets the sort order to choose which doc to update + * + * #### Examples: + * + * await A.where().findOneAndDelete(conditions, options) // executes + * A.where().findOneAndDelete(conditions, options) // return Query + * await A.where().findOneAndDelete(conditions) // executes + * A.where().findOneAndDelete(conditions) // returns Query + * await A.where().findOneAndDelete() // executes + * A.where().findOneAndDelete() // returns Query + * + * @param {Object} [filter] + * @param {Object} [options] + * @return {Query} this + * @api public + */ + +Query.prototype.findOneAndDelete = function(filter, options) { + this.op = 'findOneAndDelete'; + this._validate(); + + if (Query.canMerge(filter)) { + this.merge(filter); + } + + // apply options + options && this.setOptions(options); + + return this; +}; + +/** + * Executes a `findOneAndRemove` Query + * @returns the results + */ +Query.prototype._findOneAndDelete = async function() { + const options = this._optionsForExec(); + const conds = this._conditions; + + debug('findOneAndDelete', this._collection.collectionName, conds, options); + return this._collection.findOneAndDelete(conds, options); +}; + +/** + * Add trace function that gets called when the query is executed. + * The function will be called with (method, queryInfo, query) and + * should return a callback function which will be called + * with (err, result, millis) when the query is complete. + * + * queryInfo is an object containing: { + * collectionName: , + * conditions: , + * options: , + * doc: [document to update, if applicable] + * } + * + * NOTE: Does not trace stream queries. + * + * @param {Function} traceFunction + * @return {Query} this + * @api public + */ +Query.prototype.setTraceFunction = function(traceFunction) { + this._traceFunction = traceFunction; + return this; +}; + +/** + * Executes the query + * + * #### Examples: + * + * query.exec(); + * await query.exec(); + * query.exec('update'); + * await query.exec('find'); + * + * @param {String|Function} [operation] + * @api public + */ + +Query.prototype.exec = async function exec(op) { + if (typeof op === 'string') { + this.op = op; + } + + assert.ok(this.op, 'Missing query type: (find, etc)'); + + const fnName = '_' + this.op; + + // better error, because default would list it as "this[fnName] is not a function" + if (typeof this[fnName] !== 'function') { + throw new TypeError(`this[${fnName}] is not a function`); + } + + return this[fnName](); +}; + +/** + * Executes the query returning a `Promise` which will be + * resolved with either the doc(s) or rejected with the error. + * + * @param {Function} [resolve] + * @param {Function} [reject] + * @return {Promise} + * @api public + */ + +Query.prototype.then = async function(res, rej) { + return this.exec().then(res, rej); +}; + +/** + * Returns a cursor for the given `find` query. + * + * @throws Error if operation is not a find + * @returns {Cursor} MongoDB driver cursor + */ + +Query.prototype.cursor = function() { + if ('find' != this.op) + throw new Error('cursor() is only available for find'); + + const conds = this._conditions; + + const options = this._optionsForExec(); + if (this.$useProjection) { + options.projection = this._fieldsForExec(); + } else { + options.fields = this._fieldsForExec(); + } + + return this._collection.findCursor(conds, options); +}; + +/** + * Determines if field selection has been made. + * + * @return {Boolean} + * @api public + */ + +Query.prototype.selected = function selected() { + return !!(this._fields && Object.keys(this._fields).length > 0); +}; + +/** + * Determines if inclusive field selection has been made. + * + * query.selectedInclusively() // false + * query.select('name') + * query.selectedInclusively() // true + * query.selectedExlusively() // false + * + * @returns {Boolean} + */ + +Query.prototype.selectedInclusively = function selectedInclusively() { + if (!this._fields) return false; + + const keys = Object.keys(this._fields); + if (0 === keys.length) return false; + + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + if (0 === this._fields[key]) return false; + if (this._fields[key] && + typeof this._fields[key] === 'object' && + this._fields[key].$meta) { + return false; + } + } + + return true; +}; + +/** + * Determines if exclusive field selection has been made. + * + * query.selectedExlusively() // false + * query.select('-name') + * query.selectedExlusively() // true + * query.selectedInclusively() // false + * + * @returns {Boolean} + */ + +Query.prototype.selectedExclusively = function selectedExclusively() { + if (!this._fields) return false; + + const keys = Object.keys(this._fields); + if (0 === keys.length) return false; + + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + if (0 === this._fields[key]) return true; + } + + return false; +}; + +/** + * Merges `doc` with the current update object. + * + * @param {Object} doc + */ + +Query.prototype._mergeUpdate = function(doc) { + if (!this._updateDoc) this._updateDoc = {}; + if (doc instanceof Query) { + if (doc._updateDoc) { + utils.mergeClone(this._updateDoc, doc._updateDoc); + } + } else { + utils.mergeClone(this._updateDoc, doc); + } +}; + +/** + * Returns default options. + * + * @return {Object} + * @api private + */ + +Query.prototype._optionsForExec = function() { + const options = utils.clone(this.options); + return options; +}; + +/** + * Returns fields selection for this query. + * + * @return {Object} + * @api private + */ + +Query.prototype._fieldsForExec = function() { + return utils.clone(this._fields); +}; + +/** + * Return an update document with corrected $set operations. + * + * @api private + */ + +Query.prototype._updateForExec = function() { + const update = this._updateDoc == null ? {} : utils.clone(this._updateDoc); + const ops = utils.keys(update); + const ret = {}; + + for (const op of ops) { + if (this.options.overwrite) { + ret[op] = update[op]; + continue; + } + + if ('$' !== op[0]) { + // fix up $set sugar + if (!ret.$set) { + if (update.$set) { + ret.$set = update.$set; + } else { + ret.$set = {}; + } + } + ret.$set[op] = update[op]; + if (!~ops.indexOf('$set')) ops.push('$set'); + } else if ('$set' === op) { + if (!ret.$set) { + ret[op] = update[op]; + } + } else { + ret[op] = update[op]; + } + } + + this._compiledUpdate = ret; + return ret; +}; + +/** + * Make sure _path is set. + * + * @parmam {String} method + */ + +Query.prototype._ensurePath = function(method) { + if (!this._path) { + const msg = method + '() must be used after where() ' + + 'when called with these arguments'; + throw new Error(msg); + } +}; + +/*! + * Permissions + */ + +Query.permissions = require('./permissions'); + +Query._isPermitted = function(a, b) { + const denied = Query.permissions[b]; + if (!denied) return true; + return true !== denied[a]; +}; + +Query.prototype._validate = function(action) { + let fail; + let validator; + + if (undefined === action) { + + validator = Query.permissions[this.op]; + if ('function' != typeof validator) return true; + + fail = validator(this); + + } else if (!Query._isPermitted(action, this.op)) { + fail = action; + } + + if (fail) { + throw new Error(fail + ' cannot be used with ' + this.op); + } +}; + +/** + * Determines if `conds` can be merged using `mquery().merge()` + * + * @param {Object} conds + * @return {Boolean} + */ + +Query.canMerge = function(conds) { + return conds instanceof Query || utils.isObject(conds); +}; + +/** + * Set a trace function that will get called whenever a + * query is executed. + * + * See `setTraceFunction()` for details. + * + * @param {Object} conds + * @return {Boolean} + */ +Query.setGlobalTraceFunction = function(traceFunction) { + Query.traceFunction = traceFunction; +}; + +/*! + * Exports. + */ + +Query.utils = utils; +Query.env = require('./env'); +Query.Collection = require('./collection'); +Query.BaseCollection = require('./collection/collection'); +module.exports = exports = Query; + +// TODO +// test utils diff --git a/backend/node_modules/mquery/lib/permissions.js b/backend/node_modules/mquery/lib/permissions.js new file mode 100644 index 0000000..b7ea0f3 --- /dev/null +++ b/backend/node_modules/mquery/lib/permissions.js @@ -0,0 +1,93 @@ +'use strict'; + +const denied = exports; + +denied.distinct = function(self) { + if (self._fields && Object.keys(self._fields).length > 0) { + return 'field selection and slice'; + } + + const keys = Object.keys(denied.distinct); + let err; + + keys.every(function(option) { + if (self.options[option]) { + err = option; + return false; + } + return true; + }); + + return err; +}; +denied.distinct.select = +denied.distinct.slice = +denied.distinct.sort = +denied.distinct.limit = +denied.distinct.skip = +denied.distinct.batchSize = +denied.distinct.hint = +denied.distinct.tailable = true; + + +// aggregation integration + + +denied.findOneAndUpdate = +denied.findOneAndDelete = function(self) { + const keys = Object.keys(denied.findOneAndUpdate); + let err; + + keys.every(function(option) { + if (self.options[option]) { + err = option; + return false; + } + return true; + }); + + return err; +}; +denied.findOneAndUpdate.limit = +denied.findOneAndUpdate.skip = +denied.findOneAndUpdate.batchSize = +denied.findOneAndUpdate.tailable = true; + +denied.findOneAndReplace = function(self) { + const keys = Object.keys(denied.findOneAndUpdate); + let err; + + keys.every(function(option) { + if (self.options[option]) { + err = option; + return false; + } + return true; + }); + + return err; +}; + + +denied.countDocuments = function(self) { + if (self._fields && Object.keys(self._fields).length > 0) { + return 'field selection and slice'; + } + + const keys = Object.keys(denied.countDocuments); + let err; + + keys.every(function(option) { + if (self.options[option]) { + err = option; + return false; + } + return true; + }); + + return err; +}; + +denied.countDocuments.slice = +denied.countDocuments.batchSize = +denied.countDocuments.tailable = true; diff --git a/backend/node_modules/mquery/lib/utils.js b/backend/node_modules/mquery/lib/utils.js new file mode 100644 index 0000000..dbaec25 --- /dev/null +++ b/backend/node_modules/mquery/lib/utils.js @@ -0,0 +1,297 @@ +'use strict'; + +/*! + * Module dependencies. + */ + +const specialProperties = ['__proto__', 'constructor', 'prototype']; + +/** + * Clones objects + * + * @param {Object} obj the object to clone + * @param {Object} options + * @return {Object} the cloned object + * @api private + */ + +const clone = exports.clone = function clone(obj, options) { + if (obj === undefined || obj === null) + return obj; + + if (Array.isArray(obj)) + return exports.cloneArray(obj, options); + + if (obj.constructor) { + if (/ObjectI[dD]$/.test(obj.constructor.name)) { + return 'function' == typeof obj.clone + ? obj.clone() + : new obj.constructor(obj.id); + } + + if (obj.constructor.name === 'ReadPreference') { + return new obj.constructor(obj.mode, clone(obj.tags, options)); + } + + if ('Binary' == obj._bsontype && obj.buffer && obj.value) { + return 'function' == typeof obj.clone + ? obj.clone() + : new obj.constructor(obj.value(true), obj.sub_type); + } + + if ('Date' === obj.constructor.name || 'Function' === obj.constructor.name) + return new obj.constructor(+obj); + + if ('RegExp' === obj.constructor.name) + return new RegExp(obj); + + if ('Buffer' === obj.constructor.name) + return Buffer.from(obj); + } + + if (isObject(obj)) + return exports.cloneObject(obj, options); + + if (obj.valueOf) + return obj.valueOf(); +}; + +/*! + * ignore + */ + +exports.cloneObject = function cloneObject(obj, options) { + const minimize = options && options.minimize, + ret = {}, + keys = Object.keys(obj), + len = keys.length; + let hasKeys = false, + val, + k = '', + i = 0; + + for (i = 0; i < len; ++i) { + k = keys[i]; + // Not technically prototype pollution because this wouldn't merge properties + // onto `Object.prototype`, but avoid properties like __proto__ as a precaution. + if (specialProperties.indexOf(k) !== -1) { + continue; + } + + val = clone(obj[k], options); + + if (!minimize || ('undefined' !== typeof val)) { + hasKeys || (hasKeys = true); + ret[k] = val; + } + } + + return minimize + ? hasKeys && ret + : ret; +}; + +exports.cloneArray = function cloneArray(arr, options) { + const ret = [], + l = arr.length; + let i = 0; + for (; i < l; i++) + ret.push(clone(arr[i], options)); + return ret; +}; + +/** + * Merges `from` into `to` without overwriting existing properties. + * + * @param {Object} to + * @param {Object} from + * @api private + */ + +exports.merge = function merge(to, from) { + const keys = Object.keys(from); + + for (const key of keys) { + if (specialProperties.indexOf(key) !== -1) { + continue; + } + if ('undefined' === typeof to[key]) { + to[key] = from[key]; + } else { + if (exports.isObject(from[key])) { + merge(to[key], from[key]); + } else { + to[key] = from[key]; + } + } + } +}; + +/** + * Same as merge but clones the assigned values. + * + * @param {Object} to + * @param {Object} from + * @api private + */ + +exports.mergeClone = function mergeClone(to, from) { + const keys = Object.keys(from); + + for (const key of keys) { + if (specialProperties.indexOf(key) !== -1) { + continue; + } + if ('undefined' === typeof to[key]) { + to[key] = clone(from[key]); + } else { + if (exports.isObject(from[key])) { + mergeClone(to[key], from[key]); + } else { + to[key] = clone(from[key]); + } + } + } +}; + +/** + * Read pref helper (mongo 2.2 drivers support this) + * + * Allows using aliases instead of full preference names: + * + * p primary + * pp primaryPreferred + * s secondary + * sp secondaryPreferred + * n nearest + * + * @param {String} pref + */ + +exports.readPref = function readPref(pref) { + switch (pref) { + case 'p': + pref = 'primary'; + break; + case 'pp': + pref = 'primaryPreferred'; + break; + case 's': + pref = 'secondary'; + break; + case 'sp': + pref = 'secondaryPreferred'; + break; + case 'n': + pref = 'nearest'; + break; + } + + return pref; +}; + + +/** + * Read Concern helper (mongo 3.2 drivers support this) + * + * Allows using string to specify read concern level: + * + * local 3.2+ + * available 3.6+ + * majority 3.2+ + * linearizable 3.4+ + * snapshot 4.0+ + * + * @param {String|Object} concern + */ + +exports.readConcern = function readConcern(concern) { + if ('string' === typeof concern) { + switch (concern) { + case 'l': + concern = 'local'; + break; + case 'a': + concern = 'available'; + break; + case 'm': + concern = 'majority'; + break; + case 'lz': + concern = 'linearizable'; + break; + case 's': + concern = 'snapshot'; + break; + } + concern = { level: concern }; + } + return concern; +}; + +/** + * Object.prototype.toString.call helper + */ + +const _toString = Object.prototype.toString; +exports.toString = function(arg) { + return _toString.call(arg); +}; + +/** + * Determines if `arg` is an object. + * + * @param {Object|Array|String|Function|RegExp|any} arg + * @return {Boolean} + */ + +const isObject = exports.isObject = function(arg) { + return '[object Object]' == exports.toString(arg); +}; + +/** + * Object.keys helper + */ + +exports.keys = Object.keys; + +/** + * Basic Object.create polyfill. + * Only one argument is supported. + * + * Based on https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/create + */ + +exports.create = 'function' == typeof Object.create + ? Object.create + : create; + +function create(proto) { + if (arguments.length > 1) { + throw new Error('Adding properties is not supported'); + } + + function F() { } + F.prototype = proto; + return new F; +} + +/** + * inheritance + */ + +exports.inherits = function(ctor, superCtor) { + ctor.prototype = exports.create(superCtor.prototype); + ctor.prototype.constructor = ctor; +}; + +/** + * Check if this object is an arguments object + * + * @param {Any} v + * @return {Boolean} + */ + +exports.isArgumentsObject = function(v) { + return Object.prototype.toString.call(v) === '[object Arguments]'; +}; diff --git a/backend/node_modules/mquery/package.json b/backend/node_modules/mquery/package.json new file mode 100644 index 0000000..f458217 --- /dev/null +++ b/backend/node_modules/mquery/package.json @@ -0,0 +1,35 @@ +{ + "name": "mquery", + "version": "6.0.0", + "description": "Expressive query building for MongoDB", + "main": "lib/mquery.js", + "scripts": { + "test": "mocha --exit test/index.js test/*.test.js", + "fix-lint": "eslint . --fix", + "lint": "eslint ." + }, + "repository": { + "type": "git", + "url": "git://github.com/aheckmann/mquery.git" + }, + "engines": { + "node": ">=20.19.0" + }, + "devDependencies": { + "eslint": "8.x", + "eslint-plugin-mocha-no-only": "1.1.1", + "mocha": "11.x", + "mongodb": "6.x" + }, + "bugs": { + "url": "https://github.com/aheckmann/mquery/issues/new" + }, + "author": "Aaron Heckmann ", + "license": "MIT", + "keywords": [ + "mongodb", + "query", + "builder" + ], + "homepage": "https://github.com/aheckmann/mquery/" +} diff --git a/backend/node_modules/sift/MIT-LICENSE.txt b/backend/node_modules/sift/MIT-LICENSE.txt new file mode 100644 index 0000000..c080d2e --- /dev/null +++ b/backend/node_modules/sift/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2015 Craig Condon + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/backend/node_modules/sift/README.md b/backend/node_modules/sift/README.md new file mode 100755 index 0000000..0501d1a --- /dev/null +++ b/backend/node_modules/sift/README.md @@ -0,0 +1,465 @@ +**Installation**: `npm install sift`, or `yarn add sift` + +## Sift is a tiny library for using MongoDB queries in Javascript + +[![Build Status](https://secure.travis-ci.org/crcn/sift.js.png)](https://secure.travis-ci.org/crcn/sift.js) + + + + +**For extended documentation, checkout http://docs.mongodb.org/manual/reference/operator/query/** + +## Features: + +- Supported operators: [\$in](#in), [\$nin](#nin), [\$exists](#exists), [\$gte](#gte), [\$gt](#gt), [\$lte](#lte), [\$lt](#lt), [\$eq](#eq), [\$ne](#ne), [\$mod](#mod), [\$all](#all), [\$and](#and), [\$or](#or), [\$nor](#nor), [\$not](#not), [\$size](#size), [\$type](#type), [\$regex](#regex), [\$where](#where), [\$elemMatch](#elemmatch) +- Regexp searches +- Supports node.js, and web +- Custom Operations +- Tree-shaking (omitting functionality from web app bundles) + +## Examples + +```javascript +import sift from "sift"; + +// intersecting arrays +const result1 = ["hello", "sifted", "array!"].filter( + sift({ $in: ["hello", "world"] }), +); // ['hello'] + +// regexp filter +const result2 = ["craig", "john", "jake"].filter(sift(/^j/)); //['john','jake'] + +// function filter +const testFilter = sift({ + //you can also filter against functions + name: function (value) { + return value.length == 5; + }, +}); + +const result3 = [ + { + name: "craig", + }, + { + name: "john", + }, + { + name: "jake", + }, +].filter(testFilter); // filtered: [{ name: 'craig' }] + +// you can test *single values* against your custom sifter +testFilter({ name: "sarah" }); //true +testFilter({ name: "tim" }); //false +``` + +## API + +### sift(query: MongoQuery, options?: Options): Function + +Creates a filter with all the built-in MongoDB query operations. + +- `query` - the filter to use against the target array +- `options` + - `operations` - [custom operations](#custom-operations) + - `compare` - compares difference between two values + +Example: + +```javascript +import sift from "sift"; + +const test = sift({ $gt: 5 }); + +console.log(test(6)); // true +console.log(test(4)); // false + +[3, 4, 5, 6, 7].filter(test); // [6, 7] +``` + +### createQueryTester(query: Query, options?: Options): Function + +Creates a filter function **without** built-in MongoDB query operations. This is useful +if you're looking to omit certain operations from application bundles. See [Omitting built-in operations](#omitting-built-in-operations) for more info. + +```javascript +import { createQueryTester, $eq, $in } from "sift"; +const filter = createQueryTester({ $eq: 5 }, { operations: { $eq, $in } }); +``` + +### createEqualsOperation(params: any, ownerQuery: Query, options: Options): Operation + +Used for [custom operations](#custom-operations). + +```javascript +import { createQueryTester, createEqualsOperation, $eq, $in } from "sift"; +const filter = createQueryTester( + { $mod: 5 }, + { + operations: { + $something(mod, ownerQuery, options) { + return createEqualsOperation( + (value) => value % mod === 0, + ownerQuery, + options, + ); + }, + }, + }, +); +filter(10); // true +filter(11); // false +``` + +## Supported Operators + +See MongoDB's [advanced queries](http://www.mongodb.org/display/DOCS/Advanced+Queries) for more info. + +### \$in + +array value must be _\$in_ the given query: + +Intersecting two arrays: + +```javascript +// filtered: ['Brazil'] +["Brazil", "Haiti", "Peru", "Chile"].filter( + sift({ $in: ["Costa Rica", "Brazil"] }), +); +``` + +Here's another example. This acts more like the \$or operator: + +```javascript +[{ name: "Craig", location: "Brazil" }].filter( + sift({ location: { $in: ["Costa Rica", "Brazil"] } }), +); +``` + +### \$nin + +Opposite of \$in: + +```javascript +// filtered: ['Haiti','Peru','Chile'] +["Brazil", "Haiti", "Peru", "Chile"].filter( + sift({ $nin: ["Costa Rica", "Brazil"] }), +); +``` + +### \$exists + +Checks if whether a value exists: + +```javascript +// filtered: ['Craig','Tim'] +sift({ $exists: true })(["Craig", null, "Tim"]); +``` + +You can also filter out values that don't exist + +```javascript +// filtered: [{ name: "Tim" }] +[{ name: "Craig", city: "Minneapolis" }, { name: "Tim" }].filter( + sift({ city: { $exists: false } }), +); +``` + +### \$gte + +Checks if a number is >= value: + +```javascript +// filtered: [2, 3] +[0, 1, 2, 3].filter(sift({ $gte: 2 })); +``` + +### \$gt + +Checks if a number is > value: + +```javascript +// filtered: [3] +[0, 1, 2, 3].filter(sift({ $gt: 2 })); +``` + +### \$lte + +Checks if a number is <= value. + +```javascript +// filtered: [0, 1, 2] +[0, 1, 2, 3].filter(sift({ $lte: 2 })); +``` + +### \$lt + +Checks if number is < value. + +```javascript +// filtered: [0, 1] +[0, 1, 2, 3].filter(sift({ $lt: 2 })); +``` + +### \$eq + +Checks if `query === value`. Note that **\$eq can be omitted**. For **\$eq**, and **\$ne** + +```javascript +// filtered: [{ state: 'MN' }] +[{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( + sift({ state: { $eq: "MN" } }), +); +``` + +Or: + +```javascript +// filtered: [{ state: 'MN' }] +[{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( + sift({ state: "MN" }), +); +``` + +### \$ne + +Checks if `query !== value`. + +```javascript +// filtered: [{ state: 'CA' }, { state: 'WI'}] +[{ state: "MN" }, { state: "CA" }, { state: "WI" }].filter( + sift({ state: { $ne: "MN" } }), +); +``` + +### \$mod + +Modulus: + +```javascript +// filtered: [300, 600] +[100, 200, 300, 400, 500, 600].filter(sift({ $mod: [3, 0] })); +``` + +### \$all + +values must match **everything** in array: + +```javascript +// filtered: [ { tags: ['books','programming','travel' ]} ] +[ + { tags: ["books", "programming", "travel"] }, + { tags: ["travel", "cooking"] }, +].filter(sift({ tags: { $all: ["books", "programming"] } })); +``` + +### \$and + +ability to use an array of expressions. All expressions must test true. + +```javascript +// filtered: [ { name: 'Craig', state: 'MN' }] + +[ + { name: "Craig", state: "MN" }, + { name: "Tim", state: "MN" }, + { name: "Joe", state: "CA" }, +].filter(sift({ $and: [{ name: "Craig" }, { state: "MN" }] })); +``` + +### \$or + +OR array of expressions. + +```javascript +// filtered: [ { name: 'Craig', state: 'MN' }, { name: 'Tim', state: 'MN' }] +[ + { name: "Craig", state: "MN" }, + { name: "Tim", state: "MN" }, + { name: "Joe", state: "CA" }, +].filter(sift({ $or: [{ name: "Craig" }, { state: "MN" }] })); +``` + +### \$nor + +opposite of or: + +```javascript +// filtered: [{ name: 'Joe', state: 'CA' }] +[ + { name: "Craig", state: "MN" }, + { name: "Tim", state: "MN" }, + { name: "Joe", state: "CA" }, +].filter(sift({ $nor: [{ name: "Craig" }, { state: "MN" }] })); +``` + +### \$size + +Matches an array - must match given size: + +```javascript +// filtered: ['food','cooking'] +[{ tags: ["food", "cooking"] }, { tags: ["traveling"] }].filter( + sift({ tags: { $size: 2 } }), +); +``` + +### \$type + +Matches a values based on the type + +```javascript +[new Date(), 4342, "hello world"].filter(sift({ $type: Date })); // returns single date +[new Date(), 4342, "hello world"].filter(sift({ $type: String })); // returns ['hello world'] +``` + +### \$regex + +Matches values based on the given regular expression + +```javascript +["frank", "fred", "sam", "frost"].filter( + sift({ $regex: /^f/i, $nin: ["frank"] }), +); // ["fred", "frost"] +["frank", "fred", "sam", "frost"].filter( + sift({ $regex: "^f", $options: "i", $nin: ["frank"] }), +); // ["fred", "frost"] +``` + +### \$where + +Matches based on some javascript comparison + +```javascript +[{ name: "frank" }, { name: "joe" }].filter( + sift({ $where: "this.name === 'frank'" }), +); // ["frank"] +[{ name: "frank" }, { name: "joe" }].filter( + sift({ + $where: function () { + return this.name === "frank"; + }, + }), +); // ["frank"] +``` + +### \$elemMatch + +Matches elements of array + +```javascript +var bills = [ + { + month: "july", + casts: [ + { + id: 1, + value: 200, + }, + { + id: 2, + value: 1000, + }, + ], + }, + { + month: "august", + casts: [ + { + id: 3, + value: 1000, + }, + { + id: 4, + value: 4000, + }, + ], + }, +]; + +var result = bills.filter( + sift({ + casts: { + $elemMatch: { + value: { $gt: 1000 }, + }, + }, + }), +); // {month:'august', casts:[{id:3, value: 1000},{id: 4, value: 4000}]} +``` + +### \$not + +Not expression: + +```javascript +["craig", "tim", "jake"].filter(sift({ $not: { $in: ["craig", "tim"] } })); // ['jake'] +["craig", "tim", "jake"].filter(sift({ $not: { $size: 5 } })); // ['tim','jake'] +``` + +### Date comparison + +Mongodb allows you to do date comparisons like so: + +```javascript +db.collection.find({ createdAt: { $gte: "2018-03-22T06:00:00Z" } }); +``` + +In Sift, you'll need to specify a Date object: + +```javascript +collection.find( + sift({ createdAt: { $gte: new Date("2018-03-22T06:00:00Z") } }), +); +``` + +## Custom behavior + +Sift works like MongoDB out of the box, but you're also able to modify the behavior to suite your needs. + +#### Custom operations + +You can register your own custom operations. Here's an example: + +```javascript +import sift, { createEqualsOperation } from "sift"; + +var filter = sift( + { + $customMod: 2, + }, + { + operations: { + $customMod(params, ownerQuery, options) { + return createEqualsOperation( + (value) => value % params !== 0, + ownerQuery, + options, + ); + }, + }, + }, +); + +[1, 2, 3, 4, 5].filter(filter); // [1, 3, 5] +``` + +#### Omitting built-in operations + +You can create a filter function that omits the built-in operations like so: + +```javascript +import { createQueryTester, $in, $all, $nin, $lt } from "sift"; +const test = createQueryTester( + { + $eq: 10, + }, + { operations: { $in, $all, $nin, $lt } }, +); + +[1, 2, 3, 4, 10].filter(test); +``` + +For bundlers like `Webpack` and `Rollup`, operations that aren't used are omitted from application bundles via tree-shaking. diff --git a/backend/node_modules/sift/es/index.js b/backend/node_modules/sift/es/index.js new file mode 100644 index 0000000..8e0e9f0 --- /dev/null +++ b/backend/node_modules/sift/es/index.js @@ -0,0 +1,632 @@ +const typeChecker = (type) => { + const typeString = "[object " + type + "]"; + return function (value) { + return getClassName(value) === typeString; + }; +}; +const getClassName = (value) => Object.prototype.toString.call(value); +const comparable = (value) => { + if (value instanceof Date) { + return value.getTime(); + } + else if (isArray(value)) { + return value.map(comparable); + } + else if (value && typeof value.toJSON === "function") { + return value.toJSON(); + } + return value; +}; +const coercePotentiallyNull = (value) => value == null ? null : value; +const isArray = typeChecker("Array"); +const isObject = typeChecker("Object"); +const isFunction = typeChecker("Function"); +const isProperty = (item, key) => { + return item.hasOwnProperty(key) && !isFunction(item[key]); +}; +const isVanillaObject = (value) => { + return (value && + (value.constructor === Object || + value.constructor === Array || + value.constructor.toString() === "function Object() { [native code] }" || + value.constructor.toString() === "function Array() { [native code] }") && + !value.toJSON); +}; +const equals = (a, b) => { + if (a == null && a == b) { + return true; + } + if (a === b) { + return true; + } + if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) { + return false; + } + if (isArray(a)) { + if (a.length !== b.length) { + return false; + } + for (let i = 0, { length } = a; i < length; i++) { + if (!equals(a[i], b[i])) + return false; + } + return true; + } + else if (isObject(a)) { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (const key in a) { + if (!equals(a[key], b[key])) + return false; + } + return true; + } + return false; +}; + +/** + * Walks through each value given the context - used for nested operations. E.g: + * { "person.address": { $eq: "blarg" }} + */ +const walkKeyPathValues = (item, keyPath, next, depth, key, owner) => { + const currentKey = keyPath[depth]; + // if array, then try matching. Might fall through for cases like: + // { $eq: [1, 2, 3] }, [ 1, 2, 3 ]. + if (isArray(item) && + isNaN(Number(currentKey)) && + !isProperty(item, currentKey)) { + for (let i = 0, { length } = item; i < length; i++) { + // if FALSE is returned, then terminate walker. For operations, this simply + // means that the search critera was met. + if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) { + return false; + } + } + } + if (depth === keyPath.length || item == null) { + return next(item, key, owner, depth === 0, depth === keyPath.length); + } + return walkKeyPathValues(item[currentKey], keyPath, next, depth + 1, currentKey, item); +}; +class BaseOperation { + constructor(params, owneryQuery, options, name) { + this.params = params; + this.owneryQuery = owneryQuery; + this.options = options; + this.name = name; + this.init(); + } + init() { } + reset() { + this.done = false; + this.keep = false; + } +} +class GroupOperation extends BaseOperation { + constructor(params, owneryQuery, options, children) { + super(params, owneryQuery, options); + this.children = children; + } + /** + */ + reset() { + this.keep = false; + this.done = false; + for (let i = 0, { length } = this.children; i < length; i++) { + this.children[i].reset(); + } + } + /** + */ + childrenNext(item, key, owner, root, leaf) { + let done = true; + let keep = true; + for (let i = 0, { length } = this.children; i < length; i++) { + const childOperation = this.children[i]; + if (!childOperation.done) { + childOperation.next(item, key, owner, root, leaf); + } + if (!childOperation.keep) { + keep = false; + } + if (childOperation.done) { + if (!childOperation.keep) { + break; + } + } + else { + done = false; + } + } + this.done = done; + this.keep = keep; + } +} +class NamedGroupOperation extends GroupOperation { + constructor(params, owneryQuery, options, children, name) { + super(params, owneryQuery, options, children); + this.name = name; + } +} +class QueryOperation extends GroupOperation { + constructor() { + super(...arguments); + this.propop = true; + } + /** + */ + next(item, key, parent, root) { + this.childrenNext(item, key, parent, root); + } +} +class NestedOperation extends GroupOperation { + constructor(keyPath, params, owneryQuery, options, children) { + super(params, owneryQuery, options, children); + this.keyPath = keyPath; + this.propop = true; + /** + */ + this._nextNestedValue = (value, key, owner, root, leaf) => { + this.childrenNext(value, key, owner, root, leaf); + return !this.done; + }; + } + /** + */ + next(item, key, parent) { + walkKeyPathValues(item, this.keyPath, this._nextNestedValue, 0, key, parent); + } +} +const createTester = (a, compare) => { + if (a instanceof Function) { + return a; + } + if (a instanceof RegExp) { + return (b) => { + const result = typeof b === "string" && a.test(b); + a.lastIndex = 0; + return result; + }; + } + const comparableA = comparable(a); + return (b) => compare(comparableA, comparable(b)); +}; +class EqualsOperation extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { + this._test = createTester(this.params, this.options.compare); + } + next(item, key, parent) { + if (!Array.isArray(parent) || parent.hasOwnProperty(key)) { + if (this._test(item, key, parent)) { + this.done = true; + this.keep = true; + } + } + } +} +const createEqualsOperation = (params, owneryQuery, options) => new EqualsOperation(params, owneryQuery, options); +const numericalOperationCreator = (createNumericalOperation) => (params, owneryQuery, options, name) => { + return createNumericalOperation(params, owneryQuery, options, name); +}; +const numericalOperation = (createTester) => numericalOperationCreator((params, owneryQuery, options, name) => { + const typeofParams = typeof comparable(params); + const test = createTester(params); + return new EqualsOperation((b) => { + const actualValue = coercePotentiallyNull(b); + return (typeof comparable(actualValue) === typeofParams && test(actualValue)); + }, owneryQuery, options, name); +}); +const createNamedOperation = (name, params, parentQuery, options) => { + const operationCreator = options.operations[name]; + if (!operationCreator) { + throwUnsupportedOperation(name); + } + return operationCreator(params, parentQuery, options, name); +}; +const throwUnsupportedOperation = (name) => { + throw new Error(`Unsupported operation: ${name}`); +}; +const containsOperation = (query, options) => { + for (const key in query) { + if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") + return true; + } + return false; +}; +const createNestedOperation = (keyPath, nestedQuery, parentKey, owneryQuery, options) => { + if (containsOperation(nestedQuery, options)) { + const [selfOperations, nestedOperations] = createQueryOperations(nestedQuery, parentKey, options); + if (nestedOperations.length) { + throw new Error(`Property queries must contain only operations, or exact objects.`); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, selfOperations); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [ + new EqualsOperation(nestedQuery, owneryQuery, options), + ]); +}; +const createQueryOperation = (query, owneryQuery = null, { compare, operations } = {}) => { + const options = { + compare: compare || equals, + operations: Object.assign({}, operations || {}), + }; + const [selfOperations, nestedOperations] = createQueryOperations(query, null, options); + const ops = []; + if (selfOperations.length) { + ops.push(new NestedOperation([], query, owneryQuery, options, selfOperations)); + } + ops.push(...nestedOperations); + if (ops.length === 1) { + return ops[0]; + } + return new QueryOperation(query, owneryQuery, options, ops); +}; +const createQueryOperations = (query, parentKey, options) => { + const selfOperations = []; + const nestedOperations = []; + if (!isVanillaObject(query)) { + selfOperations.push(new EqualsOperation(query, query, options)); + return [selfOperations, nestedOperations]; + } + for (const key in query) { + if (options.operations.hasOwnProperty(key)) { + const op = createNamedOperation(key, query[key], query, options); + if (op) { + if (!op.propop && parentKey && !options.operations[parentKey]) { + throw new Error(`Malformed query. ${key} cannot be matched against property.`); + } + } + // probably just a flag for another operation (like $options) + if (op != null) { + selfOperations.push(op); + } + } + else if (key.charAt(0) === "$") { + throwUnsupportedOperation(key); + } + else { + nestedOperations.push(createNestedOperation(key.split("."), query[key], key, query, options)); + } + } + return [selfOperations, nestedOperations]; +}; +const createOperationTester = (operation) => (item, key, owner) => { + operation.reset(); + operation.next(item, key, owner); + return operation.keep; +}; +const createQueryTester = (query, options = {}) => { + return createOperationTester(createQueryOperation(query, null, options)); +}; + +class $Ne extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { + this._test = createTester(this.params, this.options.compare); + } + reset() { + super.reset(); + this.keep = true; + } + next(item) { + if (this._test(item)) { + this.done = true; + this.keep = false; + } + } +} +// https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ +class $ElemMatch extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { + if (!this.params || typeof this.params !== "object") { + throw new Error(`Malformed query. $elemMatch must by an object.`); + } + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + } + reset() { + super.reset(); + this._queryOperation.reset(); + } + next(item) { + if (isArray(item)) { + for (let i = 0, { length } = item; i < length; i++) { + // reset query operation since item being tested needs to pass _all_ query + // operations for it to be a success + this._queryOperation.reset(); + const child = item[i]; + this._queryOperation.next(child, i, item, false); + this.keep = this.keep || this._queryOperation.keep; + } + this.done = true; + } + else { + this.done = false; + this.keep = false; + } + } +} +class $Not extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + } + reset() { + super.reset(); + this._queryOperation.reset(); + } + next(item, key, owner, root) { + this._queryOperation.next(item, key, owner, root); + this.done = this._queryOperation.done; + this.keep = !this._queryOperation.keep; + } +} +class $Size extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { } + next(item) { + if (isArray(item) && item.length === this.params) { + this.done = true; + this.keep = true; + } + // if (parent && parent.length === this.params) { + // this.done = true; + // this.keep = true; + // } + } +} +const assertGroupNotEmpty = (values) => { + if (values.length === 0) { + throw new Error(`$and/$or/$nor must be a nonempty array`); + } +}; +class $Or extends BaseOperation { + constructor() { + super(...arguments); + this.propop = false; + } + init() { + assertGroupNotEmpty(this.params); + this._ops = this.params.map((op) => createQueryOperation(op, null, this.options)); + } + reset() { + this.done = false; + this.keep = false; + for (let i = 0, { length } = this._ops; i < length; i++) { + this._ops[i].reset(); + } + } + next(item, key, owner) { + let done = false; + let success = false; + for (let i = 0, { length } = this._ops; i < length; i++) { + const op = this._ops[i]; + op.next(item, key, owner); + if (op.keep) { + done = true; + success = op.keep; + break; + } + } + this.keep = success; + this.done = done; + } +} +class $Nor extends $Or { + constructor() { + super(...arguments); + this.propop = false; + } + next(item, key, owner) { + super.next(item, key, owner); + this.keep = !this.keep; + } +} +class $In extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + init() { + const params = Array.isArray(this.params) ? this.params : [this.params]; + this._testers = params.map((value) => { + if (containsOperation(value, this.options)) { + throw new Error(`cannot nest $ under ${this.name.toLowerCase()}`); + } + return createTester(value, this.options.compare); + }); + } + next(item, key, owner) { + let done = false; + let success = false; + for (let i = 0, { length } = this._testers; i < length; i++) { + const test = this._testers[i]; + if (test(item)) { + done = true; + success = true; + break; + } + } + this.keep = success; + this.done = done; + } +} +class $Nin extends BaseOperation { + constructor(params, ownerQuery, options, name) { + super(params, ownerQuery, options, name); + this.propop = true; + this._in = new $In(params, ownerQuery, options, name); + } + next(item, key, owner, root) { + this._in.next(item, key, owner); + if (isArray(owner) && !root) { + if (this._in.keep) { + this.keep = false; + this.done = true; + } + else if (key == owner.length - 1) { + this.keep = true; + this.done = true; + } + } + else { + this.keep = !this._in.keep; + this.done = true; + } + } + reset() { + super.reset(); + this._in.reset(); + } +} +class $Exists extends BaseOperation { + constructor() { + super(...arguments); + this.propop = true; + } + next(item, key, owner, root, leaf) { + if (!leaf) { + this.done = true; + this.keep = !this.params; + } + else if (owner.hasOwnProperty(key) === this.params) { + this.done = true; + this.keep = true; + } + } +} +class $And extends NamedGroupOperation { + constructor(params, owneryQuery, options, name) { + super(params, owneryQuery, options, params.map((query) => createQueryOperation(query, owneryQuery, options)), name); + this.propop = false; + assertGroupNotEmpty(params); + } + next(item, key, owner, root) { + this.childrenNext(item, key, owner, root); + } +} +class $All extends NamedGroupOperation { + constructor(params, owneryQuery, options, name) { + super(params, owneryQuery, options, params.map((query) => createQueryOperation(query, owneryQuery, options)), name); + this.propop = true; + } + next(item, key, owner, root) { + this.childrenNext(item, key, owner, root); + } +} +const $eq = (params, owneryQuery, options) => new EqualsOperation(params, owneryQuery, options); +const $ne = (params, owneryQuery, options, name) => new $Ne(params, owneryQuery, options, name); +const $or = (params, owneryQuery, options, name) => new $Or(params, owneryQuery, options, name); +const $nor = (params, owneryQuery, options, name) => new $Nor(params, owneryQuery, options, name); +const $elemMatch = (params, owneryQuery, options, name) => new $ElemMatch(params, owneryQuery, options, name); +const $nin = (params, owneryQuery, options, name) => new $Nin(params, owneryQuery, options, name); +const $in = (params, owneryQuery, options, name) => { + return new $In(params, owneryQuery, options, name); +}; +const $lt = numericalOperation((params) => (b) => { + return b != null && b < params; +}); +const $lte = numericalOperation((params) => (b) => { + return b === params || b <= params; +}); +const $gt = numericalOperation((params) => (b) => { + return b != null && b > params; +}); +const $gte = numericalOperation((params) => (b) => { + return b === params || b >= params; +}); +const $mod = ([mod, equalsValue], owneryQuery, options) => new EqualsOperation((b) => comparable(b) % mod === equalsValue, owneryQuery, options); +const $exists = (params, owneryQuery, options, name) => new $Exists(params, owneryQuery, options, name); +const $regex = (pattern, owneryQuery, options) => new EqualsOperation(new RegExp(pattern, owneryQuery.$options), owneryQuery, options); +const $not = (params, owneryQuery, options, name) => new $Not(params, owneryQuery, options, name); +const typeAliases = { + number: (v) => typeof v === "number", + string: (v) => typeof v === "string", + bool: (v) => typeof v === "boolean", + array: (v) => Array.isArray(v), + null: (v) => v === null, + timestamp: (v) => v instanceof Date, +}; +const $type = (clazz, owneryQuery, options) => new EqualsOperation((b) => { + if (typeof clazz === "string") { + if (!typeAliases[clazz]) { + throw new Error(`Type alias does not exist`); + } + return typeAliases[clazz](b); + } + return b != null ? b instanceof clazz || b.constructor === clazz : false; +}, owneryQuery, options); +const $and = (params, ownerQuery, options, name) => new $And(params, ownerQuery, options, name); +const $all = (params, ownerQuery, options, name) => new $All(params, ownerQuery, options, name); +const $size = (params, ownerQuery, options) => new $Size(params, ownerQuery, options, "$size"); +const $options = () => null; +const $where = (params, ownerQuery, options) => { + let test; + if (isFunction(params)) { + test = params; + } + else if (!process.env.CSP_ENABLED) { + test = new Function("obj", "return " + params); + } + else { + throw new Error(`In CSP mode, sift does not support strings in "$where" condition`); + } + return new EqualsOperation((b) => test.bind(b)(b), ownerQuery, options); +}; + +var defaultOperations = /*#__PURE__*/Object.freeze({ + __proto__: null, + $Size: $Size, + $all: $all, + $and: $and, + $elemMatch: $elemMatch, + $eq: $eq, + $exists: $exists, + $gt: $gt, + $gte: $gte, + $in: $in, + $lt: $lt, + $lte: $lte, + $mod: $mod, + $ne: $ne, + $nin: $nin, + $nor: $nor, + $not: $not, + $options: $options, + $or: $or, + $regex: $regex, + $size: $size, + $type: $type, + $where: $where +}); + +const createDefaultQueryOperation = (query, ownerQuery, { compare, operations } = {}) => { + return createQueryOperation(query, ownerQuery, { + compare, + operations: Object.assign({}, defaultOperations, operations || {}), + }); +}; +const createDefaultQueryTester = (query, options = {}) => { + const op = createDefaultQueryOperation(query, null, options); + return createOperationTester(op); +}; + +export { $Size, $all, $and, $elemMatch, $eq, $exists, $gt, $gte, $in, $lt, $lte, $mod, $ne, $nin, $nor, $not, $options, $or, $regex, $size, $type, $where, EqualsOperation, createDefaultQueryOperation, createEqualsOperation, createOperationTester, createQueryOperation, createQueryTester, createDefaultQueryTester as default }; +//# sourceMappingURL=index.js.map diff --git a/backend/node_modules/sift/es/index.js.map b/backend/node_modules/sift/es/index.js.map new file mode 100644 index 0000000..8a9ff1d --- /dev/null +++ b/backend/node_modules/sift/es/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../src/utils.ts","../src/core.ts","../src/operations.ts","../src/index.ts"],"sourcesContent":[null,null,null,null],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/node_modules/sift/es5m/index.js b/backend/node_modules/sift/es5m/index.js new file mode 100644 index 0000000..afb7464 --- /dev/null +++ b/backend/node_modules/sift/es5m/index.js @@ -0,0 +1,743 @@ +/****************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +/* global Reflect, Promise, SuppressedError, Symbol */ + +var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); +}; + +function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +} + +typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; +}; + +var typeChecker = function (type) { + var typeString = "[object " + type + "]"; + return function (value) { + return getClassName(value) === typeString; + }; +}; +var getClassName = function (value) { return Object.prototype.toString.call(value); }; +var comparable = function (value) { + if (value instanceof Date) { + return value.getTime(); + } + else if (isArray(value)) { + return value.map(comparable); + } + else if (value && typeof value.toJSON === "function") { + return value.toJSON(); + } + return value; +}; +var coercePotentiallyNull = function (value) { + return value == null ? null : value; +}; +var isArray = typeChecker("Array"); +var isObject = typeChecker("Object"); +var isFunction = typeChecker("Function"); +var isProperty = function (item, key) { + return item.hasOwnProperty(key) && !isFunction(item[key]); +}; +var isVanillaObject = function (value) { + return (value && + (value.constructor === Object || + value.constructor === Array || + value.constructor.toString() === "function Object() { [native code] }" || + value.constructor.toString() === "function Array() { [native code] }") && + !value.toJSON); +}; +var equals = function (a, b) { + if (a == null && a == b) { + return true; + } + if (a === b) { + return true; + } + if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) { + return false; + } + if (isArray(a)) { + if (a.length !== b.length) { + return false; + } + for (var i = 0, length_1 = a.length; i < length_1; i++) { + if (!equals(a[i], b[i])) + return false; + } + return true; + } + else if (isObject(a)) { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (var key in a) { + if (!equals(a[key], b[key])) + return false; + } + return true; + } + return false; +}; + +/** + * Walks through each value given the context - used for nested operations. E.g: + * { "person.address": { $eq: "blarg" }} + */ +var walkKeyPathValues = function (item, keyPath, next, depth, key, owner) { + var currentKey = keyPath[depth]; + // if array, then try matching. Might fall through for cases like: + // { $eq: [1, 2, 3] }, [ 1, 2, 3 ]. + if (isArray(item) && + isNaN(Number(currentKey)) && + !isProperty(item, currentKey)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // if FALSE is returned, then terminate walker. For operations, this simply + // means that the search critera was met. + if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) { + return false; + } + } + } + if (depth === keyPath.length || item == null) { + return next(item, key, owner, depth === 0, depth === keyPath.length); + } + return walkKeyPathValues(item[currentKey], keyPath, next, depth + 1, currentKey, item); +}; +var BaseOperation = /** @class */ (function () { + function BaseOperation(params, owneryQuery, options, name) { + this.params = params; + this.owneryQuery = owneryQuery; + this.options = options; + this.name = name; + this.init(); + } + BaseOperation.prototype.init = function () { }; + BaseOperation.prototype.reset = function () { + this.done = false; + this.keep = false; + }; + return BaseOperation; +}()); +var GroupOperation = /** @class */ (function (_super) { + __extends(GroupOperation, _super); + function GroupOperation(params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options) || this; + _this.children = children; + return _this; + } + /** + */ + GroupOperation.prototype.reset = function () { + this.keep = false; + this.done = false; + for (var i = 0, length_2 = this.children.length; i < length_2; i++) { + this.children[i].reset(); + } + }; + /** + */ + GroupOperation.prototype.childrenNext = function (item, key, owner, root, leaf) { + var done = true; + var keep = true; + for (var i = 0, length_3 = this.children.length; i < length_3; i++) { + var childOperation = this.children[i]; + if (!childOperation.done) { + childOperation.next(item, key, owner, root, leaf); + } + if (!childOperation.keep) { + keep = false; + } + if (childOperation.done) { + if (!childOperation.keep) { + break; + } + } + else { + done = false; + } + } + this.done = done; + this.keep = keep; + }; + return GroupOperation; +}(BaseOperation)); +var NamedGroupOperation = /** @class */ (function (_super) { + __extends(NamedGroupOperation, _super); + function NamedGroupOperation(params, owneryQuery, options, children, name) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.name = name; + return _this; + } + return NamedGroupOperation; +}(GroupOperation)); +var QueryOperation = /** @class */ (function (_super) { + __extends(QueryOperation, _super); + function QueryOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + /** + */ + QueryOperation.prototype.next = function (item, key, parent, root) { + this.childrenNext(item, key, parent, root); + }; + return QueryOperation; +}(GroupOperation)); +var NestedOperation = /** @class */ (function (_super) { + __extends(NestedOperation, _super); + function NestedOperation(keyPath, params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.keyPath = keyPath; + _this.propop = true; + /** + */ + _this._nextNestedValue = function (value, key, owner, root, leaf) { + _this.childrenNext(value, key, owner, root, leaf); + return !_this.done; + }; + return _this; + } + /** + */ + NestedOperation.prototype.next = function (item, key, parent) { + walkKeyPathValues(item, this.keyPath, this._nextNestedValue, 0, key, parent); + }; + return NestedOperation; +}(GroupOperation)); +var createTester = function (a, compare) { + if (a instanceof Function) { + return a; + } + if (a instanceof RegExp) { + return function (b) { + var result = typeof b === "string" && a.test(b); + a.lastIndex = 0; + return result; + }; + } + var comparableA = comparable(a); + return function (b) { return compare(comparableA, comparable(b)); }; +}; +var EqualsOperation = /** @class */ (function (_super) { + __extends(EqualsOperation, _super); + function EqualsOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + EqualsOperation.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + EqualsOperation.prototype.next = function (item, key, parent) { + if (!Array.isArray(parent) || parent.hasOwnProperty(key)) { + if (this._test(item, key, parent)) { + this.done = true; + this.keep = true; + } + } + }; + return EqualsOperation; +}(BaseOperation)); +var createEqualsOperation = function (params, owneryQuery, options) { return new EqualsOperation(params, owneryQuery, options); }; +var numericalOperationCreator = function (createNumericalOperation) { + return function (params, owneryQuery, options, name) { + return createNumericalOperation(params, owneryQuery, options, name); + }; +}; +var numericalOperation = function (createTester) { + return numericalOperationCreator(function (params, owneryQuery, options, name) { + var typeofParams = typeof comparable(params); + var test = createTester(params); + return new EqualsOperation(function (b) { + var actualValue = coercePotentiallyNull(b); + return (typeof comparable(actualValue) === typeofParams && test(actualValue)); + }, owneryQuery, options, name); + }); +}; +var createNamedOperation = function (name, params, parentQuery, options) { + var operationCreator = options.operations[name]; + if (!operationCreator) { + throwUnsupportedOperation(name); + } + return operationCreator(params, parentQuery, options, name); +}; +var throwUnsupportedOperation = function (name) { + throw new Error("Unsupported operation: ".concat(name)); +}; +var containsOperation = function (query, options) { + for (var key in query) { + if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") + return true; + } + return false; +}; +var createNestedOperation = function (keyPath, nestedQuery, parentKey, owneryQuery, options) { + if (containsOperation(nestedQuery, options)) { + var _a = createQueryOperations(nestedQuery, parentKey, options), selfOperations = _a[0], nestedOperations = _a[1]; + if (nestedOperations.length) { + throw new Error("Property queries must contain only operations, or exact objects."); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, selfOperations); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [ + new EqualsOperation(nestedQuery, owneryQuery, options), + ]); +}; +var createQueryOperation = function (query, owneryQuery, _a) { + if (owneryQuery === void 0) { owneryQuery = null; } + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + var options = { + compare: compare || equals, + operations: Object.assign({}, operations || {}), + }; + var _c = createQueryOperations(query, null, options), selfOperations = _c[0], nestedOperations = _c[1]; + var ops = []; + if (selfOperations.length) { + ops.push(new NestedOperation([], query, owneryQuery, options, selfOperations)); + } + ops.push.apply(ops, nestedOperations); + if (ops.length === 1) { + return ops[0]; + } + return new QueryOperation(query, owneryQuery, options, ops); +}; +var createQueryOperations = function (query, parentKey, options) { + var selfOperations = []; + var nestedOperations = []; + if (!isVanillaObject(query)) { + selfOperations.push(new EqualsOperation(query, query, options)); + return [selfOperations, nestedOperations]; + } + for (var key in query) { + if (options.operations.hasOwnProperty(key)) { + var op = createNamedOperation(key, query[key], query, options); + if (op) { + if (!op.propop && parentKey && !options.operations[parentKey]) { + throw new Error("Malformed query. ".concat(key, " cannot be matched against property.")); + } + } + // probably just a flag for another operation (like $options) + if (op != null) { + selfOperations.push(op); + } + } + else if (key.charAt(0) === "$") { + throwUnsupportedOperation(key); + } + else { + nestedOperations.push(createNestedOperation(key.split("."), query[key], key, query, options)); + } + } + return [selfOperations, nestedOperations]; +}; +var createOperationTester = function (operation) { + return function (item, key, owner) { + operation.reset(); + operation.next(item, key, owner); + return operation.keep; + }; +}; +var createQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + return createOperationTester(createQueryOperation(query, null, options)); +}; + +var $Ne = /** @class */ (function (_super) { + __extends($Ne, _super); + function $Ne() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Ne.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + $Ne.prototype.reset = function () { + _super.prototype.reset.call(this); + this.keep = true; + }; + $Ne.prototype.next = function (item) { + if (this._test(item)) { + this.done = true; + this.keep = false; + } + }; + return $Ne; +}(BaseOperation)); +// https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ +var $ElemMatch = /** @class */ (function (_super) { + __extends($ElemMatch, _super); + function $ElemMatch() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $ElemMatch.prototype.init = function () { + if (!this.params || typeof this.params !== "object") { + throw new Error("Malformed query. $elemMatch must by an object."); + } + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $ElemMatch.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $ElemMatch.prototype.next = function (item) { + if (isArray(item)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // reset query operation since item being tested needs to pass _all_ query + // operations for it to be a success + this._queryOperation.reset(); + var child = item[i]; + this._queryOperation.next(child, i, item, false); + this.keep = this.keep || this._queryOperation.keep; + } + this.done = true; + } + else { + this.done = false; + this.keep = false; + } + }; + return $ElemMatch; +}(BaseOperation)); +var $Not = /** @class */ (function (_super) { + __extends($Not, _super); + function $Not() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Not.prototype.init = function () { + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $Not.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $Not.prototype.next = function (item, key, owner, root) { + this._queryOperation.next(item, key, owner, root); + this.done = this._queryOperation.done; + this.keep = !this._queryOperation.keep; + }; + return $Not; +}(BaseOperation)); +var $Size = /** @class */ (function (_super) { + __extends($Size, _super); + function $Size() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Size.prototype.init = function () { }; + $Size.prototype.next = function (item) { + if (isArray(item) && item.length === this.params) { + this.done = true; + this.keep = true; + } + // if (parent && parent.length === this.params) { + // this.done = true; + // this.keep = true; + // } + }; + return $Size; +}(BaseOperation)); +var assertGroupNotEmpty = function (values) { + if (values.length === 0) { + throw new Error("$and/$or/$nor must be a nonempty array"); + } +}; +var $Or = /** @class */ (function (_super) { + __extends($Or, _super); + function $Or() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Or.prototype.init = function () { + var _this = this; + assertGroupNotEmpty(this.params); + this._ops = this.params.map(function (op) { + return createQueryOperation(op, null, _this.options); + }); + }; + $Or.prototype.reset = function () { + this.done = false; + this.keep = false; + for (var i = 0, length_2 = this._ops.length; i < length_2; i++) { + this._ops[i].reset(); + } + }; + $Or.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_3 = this._ops.length; i < length_3; i++) { + var op = this._ops[i]; + op.next(item, key, owner); + if (op.keep) { + done = true; + success = op.keep; + break; + } + } + this.keep = success; + this.done = done; + }; + return $Or; +}(BaseOperation)); +var $Nor = /** @class */ (function (_super) { + __extends($Nor, _super); + function $Nor() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Nor.prototype.next = function (item, key, owner) { + _super.prototype.next.call(this, item, key, owner); + this.keep = !this.keep; + }; + return $Nor; +}($Or)); +var $In = /** @class */ (function (_super) { + __extends($In, _super); + function $In() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $In.prototype.init = function () { + var _this = this; + var params = Array.isArray(this.params) ? this.params : [this.params]; + this._testers = params.map(function (value) { + if (containsOperation(value, _this.options)) { + throw new Error("cannot nest $ under ".concat(_this.name.toLowerCase())); + } + return createTester(value, _this.options.compare); + }); + }; + $In.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_4 = this._testers.length; i < length_4; i++) { + var test = this._testers[i]; + if (test(item)) { + done = true; + success = true; + break; + } + } + this.keep = success; + this.done = done; + }; + return $In; +}(BaseOperation)); +var $Nin = /** @class */ (function (_super) { + __extends($Nin, _super); + function $Nin(params, ownerQuery, options, name) { + var _this = _super.call(this, params, ownerQuery, options, name) || this; + _this.propop = true; + _this._in = new $In(params, ownerQuery, options, name); + return _this; + } + $Nin.prototype.next = function (item, key, owner, root) { + this._in.next(item, key, owner); + if (isArray(owner) && !root) { + if (this._in.keep) { + this.keep = false; + this.done = true; + } + else if (key == owner.length - 1) { + this.keep = true; + this.done = true; + } + } + else { + this.keep = !this._in.keep; + this.done = true; + } + }; + $Nin.prototype.reset = function () { + _super.prototype.reset.call(this); + this._in.reset(); + }; + return $Nin; +}(BaseOperation)); +var $Exists = /** @class */ (function (_super) { + __extends($Exists, _super); + function $Exists() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Exists.prototype.next = function (item, key, owner, root, leaf) { + if (!leaf) { + this.done = true; + this.keep = !this.params; + } + else if (owner.hasOwnProperty(key) === this.params) { + this.done = true; + this.keep = true; + } + }; + return $Exists; +}(BaseOperation)); +var $And = /** @class */ (function (_super) { + __extends($And, _super); + function $And(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = false; + assertGroupNotEmpty(params); + return _this; + } + $And.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $And; +}(NamedGroupOperation)); +var $All = /** @class */ (function (_super) { + __extends($All, _super); + function $All(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = true; + return _this; + } + $All.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $All; +}(NamedGroupOperation)); +var $eq = function (params, owneryQuery, options) { + return new EqualsOperation(params, owneryQuery, options); +}; +var $ne = function (params, owneryQuery, options, name) { return new $Ne(params, owneryQuery, options, name); }; +var $or = function (params, owneryQuery, options, name) { return new $Or(params, owneryQuery, options, name); }; +var $nor = function (params, owneryQuery, options, name) { return new $Nor(params, owneryQuery, options, name); }; +var $elemMatch = function (params, owneryQuery, options, name) { return new $ElemMatch(params, owneryQuery, options, name); }; +var $nin = function (params, owneryQuery, options, name) { return new $Nin(params, owneryQuery, options, name); }; +var $in = function (params, owneryQuery, options, name) { + return new $In(params, owneryQuery, options, name); +}; +var $lt = numericalOperation(function (params) { return function (b) { + return b != null && b < params; +}; }); +var $lte = numericalOperation(function (params) { return function (b) { + return b === params || b <= params; +}; }); +var $gt = numericalOperation(function (params) { return function (b) { + return b != null && b > params; +}; }); +var $gte = numericalOperation(function (params) { return function (b) { + return b === params || b >= params; +}; }); +var $mod = function (_a, owneryQuery, options) { + var mod = _a[0], equalsValue = _a[1]; + return new EqualsOperation(function (b) { return comparable(b) % mod === equalsValue; }, owneryQuery, options); +}; +var $exists = function (params, owneryQuery, options, name) { return new $Exists(params, owneryQuery, options, name); }; +var $regex = function (pattern, owneryQuery, options) { + return new EqualsOperation(new RegExp(pattern, owneryQuery.$options), owneryQuery, options); +}; +var $not = function (params, owneryQuery, options, name) { return new $Not(params, owneryQuery, options, name); }; +var typeAliases = { + number: function (v) { return typeof v === "number"; }, + string: function (v) { return typeof v === "string"; }, + bool: function (v) { return typeof v === "boolean"; }, + array: function (v) { return Array.isArray(v); }, + null: function (v) { return v === null; }, + timestamp: function (v) { return v instanceof Date; }, +}; +var $type = function (clazz, owneryQuery, options) { + return new EqualsOperation(function (b) { + if (typeof clazz === "string") { + if (!typeAliases[clazz]) { + throw new Error("Type alias does not exist"); + } + return typeAliases[clazz](b); + } + return b != null ? b instanceof clazz || b.constructor === clazz : false; + }, owneryQuery, options); +}; +var $and = function (params, ownerQuery, options, name) { return new $And(params, ownerQuery, options, name); }; +var $all = function (params, ownerQuery, options, name) { return new $All(params, ownerQuery, options, name); }; +var $size = function (params, ownerQuery, options) { return new $Size(params, ownerQuery, options, "$size"); }; +var $options = function () { return null; }; +var $where = function (params, ownerQuery, options) { + var test; + if (isFunction(params)) { + test = params; + } + else if (!process.env.CSP_ENABLED) { + test = new Function("obj", "return " + params); + } + else { + throw new Error("In CSP mode, sift does not support strings in \"$where\" condition"); + } + return new EqualsOperation(function (b) { return test.bind(b)(b); }, ownerQuery, options); +}; + +var defaultOperations = /*#__PURE__*/Object.freeze({ + __proto__: null, + $Size: $Size, + $all: $all, + $and: $and, + $elemMatch: $elemMatch, + $eq: $eq, + $exists: $exists, + $gt: $gt, + $gte: $gte, + $in: $in, + $lt: $lt, + $lte: $lte, + $mod: $mod, + $ne: $ne, + $nin: $nin, + $nor: $nor, + $not: $not, + $options: $options, + $or: $or, + $regex: $regex, + $size: $size, + $type: $type, + $where: $where +}); + +var createDefaultQueryOperation = function (query, ownerQuery, _a) { + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + return createQueryOperation(query, ownerQuery, { + compare: compare, + operations: Object.assign({}, defaultOperations, operations || {}), + }); +}; +var createDefaultQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + var op = createDefaultQueryOperation(query, null, options); + return createOperationTester(op); +}; + +export { $Size, $all, $and, $elemMatch, $eq, $exists, $gt, $gte, $in, $lt, $lte, $mod, $ne, $nin, $nor, $not, $options, $or, $regex, $size, $type, $where, EqualsOperation, createDefaultQueryOperation, createEqualsOperation, createOperationTester, createQueryOperation, createQueryTester, createDefaultQueryTester as default }; +//# sourceMappingURL=index.js.map diff --git a/backend/node_modules/sift/es5m/index.js.map b/backend/node_modules/sift/es5m/index.js.map new file mode 100644 index 0000000..8523755 --- /dev/null +++ b/backend/node_modules/sift/es5m/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../node_modules/tslib/tslib.es6.js","../src/utils.ts","../src/core.ts","../src/operations.ts","../src/index.ts"],"sourcesContent":["/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise, SuppressedError, Symbol */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\r\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\r\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\r\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\r\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\r\n var _, done = false;\r\n for (var i = decorators.length - 1; i >= 0; i--) {\r\n var context = {};\r\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\r\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\r\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\r\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\r\n if (kind === \"accessor\") {\r\n if (result === void 0) continue;\r\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\r\n if (_ = accept(result.get)) descriptor.get = _;\r\n if (_ = accept(result.set)) descriptor.set = _;\r\n if (_ = accept(result.init)) initializers.unshift(_);\r\n }\r\n else if (_ = accept(result)) {\r\n if (kind === \"field\") initializers.unshift(_);\r\n else descriptor[key] = _;\r\n }\r\n }\r\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\r\n done = true;\r\n};\r\n\r\nexport function __runInitializers(thisArg, initializers, value) {\r\n var useValue = arguments.length > 2;\r\n for (var i = 0; i < initializers.length; i++) {\r\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\r\n }\r\n return useValue ? value : void 0;\r\n};\r\n\r\nexport function __propKey(x) {\r\n return typeof x === \"symbol\" ? x : \"\".concat(x);\r\n};\r\n\r\nexport function __setFunctionName(f, name, prefix) {\r\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\r\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\r\n};\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n var desc = Object.getOwnPropertyDescriptor(m, k);\r\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\r\n desc = { enumerable: true, get: function() { return m[k]; } };\r\n }\r\n Object.defineProperty(o, k2, desc);\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\nexport function __classPrivateFieldIn(state, receiver) {\r\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\r\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\r\n}\r\n\r\nexport function __addDisposableResource(env, value, async) {\r\n if (value !== null && value !== void 0) {\r\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\r\n var dispose;\r\n if (async) {\r\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\r\n dispose = value[Symbol.asyncDispose];\r\n }\r\n if (dispose === void 0) {\r\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\r\n dispose = value[Symbol.dispose];\r\n }\r\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\r\n env.stack.push({ value: value, dispose: dispose, async: async });\r\n }\r\n else if (async) {\r\n env.stack.push({ async: true });\r\n }\r\n return value;\r\n}\r\n\r\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\r\n\r\nexport function __disposeResources(env) {\r\n function fail(e) {\r\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\r\n env.hasError = true;\r\n }\r\n function next() {\r\n while (env.stack.length) {\r\n var rec = env.stack.pop();\r\n try {\r\n var result = rec.dispose && rec.dispose.call(rec.value);\r\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\r\n }\r\n catch (e) {\r\n fail(e);\r\n }\r\n }\r\n if (env.hasError) throw env.error;\r\n }\r\n return next();\r\n}\r\n\r\nexport default {\r\n __extends: __extends,\r\n __assign: __assign,\r\n __rest: __rest,\r\n __decorate: __decorate,\r\n __param: __param,\r\n __metadata: __metadata,\r\n __awaiter: __awaiter,\r\n __generator: __generator,\r\n __createBinding: __createBinding,\r\n __exportStar: __exportStar,\r\n __values: __values,\r\n __read: __read,\r\n __spread: __spread,\r\n __spreadArrays: __spreadArrays,\r\n __spreadArray: __spreadArray,\r\n __await: __await,\r\n __asyncGenerator: __asyncGenerator,\r\n __asyncDelegator: __asyncDelegator,\r\n __asyncValues: __asyncValues,\r\n __makeTemplateObject: __makeTemplateObject,\r\n __importStar: __importStar,\r\n __importDefault: __importDefault,\r\n __classPrivateFieldGet: __classPrivateFieldGet,\r\n __classPrivateFieldSet: __classPrivateFieldSet,\r\n __classPrivateFieldIn: __classPrivateFieldIn,\r\n __addDisposableResource: __addDisposableResource,\r\n __disposeResources: __disposeResources,\r\n};\r\n",null,null,null,null],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAI,aAAa,GAAG,SAAS,CAAC,EAAE,CAAC,EAAE;AACnC,IAAI,aAAa,GAAG,MAAM,CAAC,cAAc;AACzC,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,YAAY,KAAK,IAAI,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,EAAE,CAAC;AACpF,QAAQ,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1G,IAAI,OAAO,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC,CAAC;AACF;AACO,SAAS,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE;AAChC,IAAI,IAAI,OAAO,CAAC,KAAK,UAAU,IAAI,CAAC,KAAK,IAAI;AAC7C,QAAQ,MAAM,IAAI,SAAS,CAAC,sBAAsB,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,+BAA+B,CAAC,CAAC;AAClG,IAAI,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxB,IAAI,SAAS,EAAE,GAAG,EAAE,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,EAAE;AAC3C,IAAI,CAAC,CAAC,SAAS,GAAG,CAAC,KAAK,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;AACzF,CAAC;AA6RD;AACuB,OAAO,eAAe,KAAK,UAAU,GAAG,eAAe,GAAG,UAAU,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE;AACvH,IAAI,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC;AAC/B,IAAI,OAAO,CAAC,CAAC,IAAI,GAAG,iBAAiB,EAAE,CAAC,CAAC,KAAK,GAAG,KAAK,EAAE,CAAC,CAAC,UAAU,GAAG,UAAU,EAAE,CAAC,CAAC;AACrF;;AC5TO,IAAM,WAAW,GAAG,UAAQ,IAAI,EAAA;AACrC,IAAA,IAAM,UAAU,GAAG,UAAU,GAAG,IAAI,GAAG,GAAG,CAAC;AAC3C,IAAA,OAAO,UAAU,KAAK,EAAA;AACpB,QAAA,OAAO,YAAY,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC;AAC5C,KAAC,CAAC;AACJ,CAAC,CAAC;AAEF,IAAM,YAAY,GAAG,UAAC,KAAK,EAAK,EAAA,OAAA,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA,EAAA,CAAC;AAE/D,IAAM,UAAU,GAAG,UAAC,KAAU,EAAA;AACnC,IAAA,IAAI,KAAK,YAAY,IAAI,EAAE;AACzB,QAAA,OAAO,KAAK,CAAC,OAAO,EAAE,CAAC;KACxB;AAAM,SAAA,IAAI,OAAO,CAAC,KAAK,CAAC,EAAE;AACzB,QAAA,OAAO,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;KAC9B;SAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,UAAU,EAAE;AACtD,QAAA,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC;KACvB;AAED,IAAA,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEK,IAAM,qBAAqB,GAAG,UAAC,KAAU,EAAA;IAC9C,OAAA,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KAAK,CAAA;AAA5B,CAA4B,CAAC;AAExB,IAAM,OAAO,GAAG,WAAW,CAAa,OAAO,CAAC,CAAC;AACjD,IAAM,QAAQ,GAAG,WAAW,CAAS,QAAQ,CAAC,CAAC;AAC/C,IAAM,UAAU,GAAG,WAAW,CAAW,UAAU,CAAC,CAAC;AACrD,IAAM,UAAU,GAAG,UAAC,IAAS,EAAE,GAAQ,EAAA;AAC5C,IAAA,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5D,CAAC,CAAC;AACK,IAAM,eAAe,GAAG,UAAC,KAAK,EAAA;AACnC,IAAA,QACE,KAAK;AACL,SAAC,KAAK,CAAC,WAAW,KAAK,MAAM;YAC3B,KAAK,CAAC,WAAW,KAAK,KAAK;AAC3B,YAAA,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,qCAAqC;AACtE,YAAA,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,KAAK,oCAAoC,CAAC;AACxE,QAAA,CAAC,KAAK,CAAC,MAAM,EACb;AACJ,CAAC,CAAC;AAEK,IAAM,MAAM,GAAG,UAAC,CAAC,EAAE,CAAC,EAAA;IACzB,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE;AACvB,QAAA,OAAO,IAAI,CAAC;KACb;AACD,IAAA,IAAI,CAAC,KAAK,CAAC,EAAE;AACX,QAAA,OAAO,IAAI,CAAC;KACb;IAED,IAAI,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;AAC3E,QAAA,OAAO,KAAK,CAAC;KACd;AAED,IAAA,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE;QACd,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,EAAE;AACzB,YAAA,OAAO,KAAK,CAAC;SACd;AACD,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,CAAC,CAAN,MAAA,EAAQ,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;AAC/C,YAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AAAE,gBAAA,OAAO,KAAK,CAAC;SACvC;AACD,QAAA,OAAO,IAAI,CAAC;KACb;AAAM,SAAA,IAAI,QAAQ,CAAC,CAAC,CAAC,EAAE;AACtB,QAAA,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE;AACnD,YAAA,OAAO,KAAK,CAAC;SACd;AACD,QAAA,KAAK,IAAM,GAAG,IAAI,CAAC,EAAE;AACnB,YAAA,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;AAAE,gBAAA,OAAO,KAAK,CAAC;SAC3C;AACD,QAAA,OAAO,IAAI,CAAC;KACb;AACD,IAAA,OAAO,KAAK,CAAC;AACf,CAAC;;ACYD;;;AAGG;AAEH,IAAM,iBAAiB,GAAG,UACxB,IAAS,EACT,OAAc,EACd,IAAY,EACZ,KAAa,EACb,GAAQ,EACR,KAAU,EAAA;AAEV,IAAA,IAAM,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;;;IAIlC,IACE,OAAO,CAAC,IAAI,CAAC;AACb,QAAA,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AACzB,QAAA,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC,EAC7B;AACA,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAT,MAAA,EAAW,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;;;AAGlD,YAAA,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,CAAC,EAAE;AAC9D,gBAAA,OAAO,KAAK,CAAC;aACd;SACF;KACF;IAED,IAAI,KAAK,KAAK,OAAO,CAAC,MAAM,IAAI,IAAI,IAAI,IAAI,EAAE;AAC5C,QAAA,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,KAAK,CAAC,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;KACtE;AAED,IAAA,OAAO,iBAAiB,CACtB,IAAI,CAAC,UAAU,CAAC,EAChB,OAAO,EACP,IAAI,EACJ,KAAK,GAAG,CAAC,EACT,UAAU,EACV,IAAI,CACL,CAAC;AACJ,CAAC,CAAC;AAEF,IAAA,aAAA,kBAAA,YAAA;AAME,IAAA,SAAA,aAAA,CACW,MAAe,EACf,WAAgB,EAChB,OAAgB,EAChB,IAAa,EAAA;QAHb,IAAM,CAAA,MAAA,GAAN,MAAM,CAAS;QACf,IAAW,CAAA,WAAA,GAAX,WAAW,CAAK;QAChB,IAAO,CAAA,OAAA,GAAP,OAAO,CAAS;QAChB,IAAI,CAAA,IAAA,GAAJ,IAAI,CAAS;QAEtB,IAAI,CAAC,IAAI,EAAE,CAAC;KACb;IACS,aAAI,CAAA,SAAA,CAAA,IAAA,GAAd,eAAmB,CAAA;AACnB,IAAA,aAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;AACE,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;KACnB,CAAA;IAQH,OAAC,aAAA,CAAA;AAAD,CAAC,EAAA,CAAA,CAAA;AAED,IAAA,cAAA,kBAAA,UAAA,MAAA,EAAA;IAAsC,SAAkB,CAAA,cAAA,EAAA,MAAA,CAAA,CAAA;AAItD,IAAA,SAAA,cAAA,CACE,MAAW,EACX,WAAgB,EAChB,OAAgB,EACA,QAA0B,EAAA;QAE1C,IAAA,KAAA,GAAA,MAAK,YAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,IAAC,IAAA,CAAA;QAFpB,KAAQ,CAAA,QAAA,GAAR,QAAQ,CAAkB;;KAG3C;AAED;AACG;AAEH,IAAA,cAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;AACE,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAC,QAAQ,CAAlB,MAAA,EAAoB,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;YAC3D,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;SAC1B;KACF,CAAA;AAID;AACG;IAEO,cAAY,CAAA,SAAA,CAAA,YAAA,GAAtB,UACE,IAAS,EACT,GAAQ,EACR,KAAU,EACV,IAAa,EACb,IAAc,EAAA;QAEd,IAAI,IAAI,GAAG,IAAI,CAAC;QAChB,IAAI,IAAI,GAAG,IAAI,CAAC;AAChB,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAC,QAAQ,CAAlB,MAAA,EAAoB,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;YAC3D,IAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACxC,YAAA,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE;AACxB,gBAAA,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;aACnD;AACD,YAAA,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE;gBACxB,IAAI,GAAG,KAAK,CAAC;aACd;AACD,YAAA,IAAI,cAAc,CAAC,IAAI,EAAE;AACvB,gBAAA,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE;oBACxB,MAAM;iBACP;aACF;iBAAM;gBACL,IAAI,GAAG,KAAK,CAAC;aACd;SACF;AACD,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KAClB,CAAA;IACH,OAAC,cAAA,CAAA;AAAD,CAzDA,CAAsC,aAAa,CAyDlD,CAAA,CAAA;AAED,IAAA,mBAAA,kBAAA,UAAA,MAAA,EAAA;IACU,SAAc,CAAA,mBAAA,EAAA,MAAA,CAAA,CAAA;IAItB,SACE,mBAAA,CAAA,MAAW,EACX,WAAgB,EAChB,OAAgB,EAChB,QAA0B,EACjB,IAAY,EAAA;QAErB,IAAA,KAAA,GAAA,MAAK,CAAC,IAAA,CAAA,IAAA,EAAA,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAC,IAAA,CAAA;QAFrC,KAAI,CAAA,IAAA,GAAJ,IAAI,CAAQ;;KAGtB;IACH,OAAC,mBAAA,CAAA;AAAD,CAdA,CACU,cAAc,CAavB,CAAA,CAAA;AAED,IAAA,cAAA,kBAAA,UAAA,MAAA,EAAA;IAA2C,SAAc,CAAA,cAAA,EAAA,MAAA,CAAA,CAAA;AAAzD,IAAA,SAAA,cAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAOxB;AANC;AACG;IAEH,cAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAW,EAAE,GAAQ,EAAE,MAAW,EAAE,IAAa,EAAA;QACpD,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;KAC5C,CAAA;IACH,OAAC,cAAA,CAAA;AAAD,CARA,CAA2C,cAAc,CAQxD,CAAA,CAAA;AAED,IAAA,eAAA,kBAAA,UAAA,MAAA,EAAA;IAAqC,SAAc,CAAA,eAAA,EAAA,MAAA,CAAA,CAAA;IAEjD,SACW,eAAA,CAAA,OAAc,EACvB,MAAW,EACX,WAAgB,EAChB,OAAgB,EAChB,QAA0B,EAAA;QAE1B,IAAA,KAAA,GAAA,MAAK,CAAC,IAAA,CAAA,IAAA,EAAA,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAC,IAAA,CAAA;QANrC,KAAO,CAAA,OAAA,GAAP,OAAO,CAAO;QAFhB,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;AAwBvB;AACG;QAEK,KAAgB,CAAA,gBAAA,GAAG,UACzB,KAAU,EACV,GAAQ,EACR,KAAU,EACV,IAAa,EACb,IAAa,EAAA;AAEb,YAAA,KAAI,CAAC,YAAY,CAAC,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACjD,YAAA,OAAO,CAAC,KAAI,CAAC,IAAI,CAAC;AACpB,SAAC,CAAC;;KA3BD;AACD;AACG;AAEH,IAAA,eAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,MAAW,EAAA;AACnC,QAAA,iBAAiB,CACf,IAAI,EACJ,IAAI,CAAC,OAAO,EACZ,IAAI,CAAC,gBAAgB,EACrB,CAAC,EACD,GAAG,EACH,MAAM,CACP,CAAC;KACH,CAAA;IAeH,OAAC,eAAA,CAAA;AAAD,CAtCA,CAAqC,cAAc,CAsClD,CAAA,CAAA;AAEM,IAAM,YAAY,GAAG,UAAC,CAAC,EAAE,OAAmB,EAAA;AACjD,IAAA,IAAI,CAAC,YAAY,QAAQ,EAAE;AACzB,QAAA,OAAO,CAAC,CAAC;KACV;AACD,IAAA,IAAI,CAAC,YAAY,MAAM,EAAE;AACvB,QAAA,OAAO,UAAC,CAAC,EAAA;AACP,YAAA,IAAM,MAAM,GAAG,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClD,YAAA,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;AAChB,YAAA,OAAO,MAAM,CAAC;AAChB,SAAC,CAAC;KACH;AACD,IAAA,IAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;AAClC,IAAA,OAAO,UAAC,CAAC,EAAK,EAAA,OAAA,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAA,EAAA,CAAC;AACpD,CAAC,CAAC;AAEF,IAAA,eAAA,kBAAA,UAAA,MAAA,EAAA;IAA6C,SAAqB,CAAA,eAAA,EAAA,MAAA,CAAA,CAAA;AAAlE,IAAA,SAAA,eAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAaxB;AAXC,IAAA,eAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;AACE,QAAA,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KAC9D,CAAA;AACD,IAAA,eAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,UAAK,IAAI,EAAE,GAAQ,EAAE,MAAW,EAAA;AAC9B,QAAA,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YACxD,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE;AACjC,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aAClB;SACF;KACF,CAAA;IACH,OAAC,eAAA,CAAA;AAAD,CAdA,CAA6C,aAAa,CAczD,EAAA;IAEY,qBAAqB,GAAG,UACnC,MAAW,EACX,WAAgB,EAChB,OAAgB,EAAA,EACb,OAAA,IAAI,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAjD,GAAkD;AAEhD,IAAM,yBAAyB,GACpC,UAAC,wBAA+C,EAAA;AAChD,IAAA,OAAA,UAAC,MAAW,EAAE,WAAgB,EAAE,OAAgB,EAAE,IAAY,EAAA;QAC5D,OAAO,wBAAwB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;KACrE,CAAA;AAFD,CAEC,CAAC;AAEG,IAAM,kBAAkB,GAAG,UAAC,YAAoC,EAAA;IACrE,OAAA,yBAAyB,CACvB,UAAC,MAAW,EAAE,WAAuB,EAAE,OAAgB,EAAE,IAAY,EAAA;AACnE,QAAA,IAAM,YAAY,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAC/C,QAAA,IAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAClC,QAAA,OAAO,IAAI,eAAe,CACxB,UAAC,CAAC,EAAA;AACA,YAAA,IAAM,WAAW,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC;AAC7C,YAAA,QACE,OAAO,UAAU,CAAC,WAAW,CAAC,KAAK,YAAY,IAAI,IAAI,CAAC,WAAW,CAAC,EACpE;AACJ,SAAC,EACD,WAAW,EACX,OAAO,EACP,IAAI,CACL,CAAC;AACJ,KAAC,CACF,CAAA;AAhBD,CAgBC,CAAC;AASJ,IAAM,oBAAoB,GAAG,UAC3B,IAAY,EACZ,MAAW,EACX,WAAgB,EAChB,OAAgB,EAAA;IAEhB,IAAM,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC,gBAAgB,EAAE;QACrB,yBAAyB,CAAC,IAAI,CAAC,CAAC;KACjC;IACD,OAAO,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AAC9D,CAAC,CAAC;AAEF,IAAM,yBAAyB,GAAG,UAAC,IAAY,EAAA;AAC7C,IAAA,MAAM,IAAI,KAAK,CAAC,iCAA0B,IAAI,CAAE,CAAC,CAAC;AACpD,CAAC,CAAC;AAEK,IAAM,iBAAiB,GAAG,UAAC,KAAU,EAAE,OAAgB,EAAA;AAC5D,IAAA,KAAK,IAAM,GAAG,IAAI,KAAK,EAAE;AACvB,QAAA,IAAI,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG;AACjE,YAAA,OAAO,IAAI,CAAC;KACf;AACD,IAAA,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AACF,IAAM,qBAAqB,GAAG,UAC5B,OAAc,EACd,WAAgB,EAChB,SAAiB,EACjB,WAAgB,EAChB,OAAgB,EAAA;AAEhB,IAAA,IAAI,iBAAiB,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE;AACrC,QAAA,IAAA,EAAqC,GAAA,qBAAqB,CAC9D,WAAW,EACX,SAAS,EACT,OAAO,CACR,EAJM,cAAc,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,gBAAgB,QAItC,CAAC;AACF,QAAA,IAAI,gBAAgB,CAAC,MAAM,EAAE;AAC3B,YAAA,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;SACH;AACD,QAAA,OAAO,IAAI,eAAe,CACxB,OAAO,EACP,WAAW,EACX,WAAW,EACX,OAAO,EACP,cAAc,CACf,CAAC;KACH;IACD,OAAO,IAAI,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE;AACrE,QAAA,IAAI,eAAe,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC;AACvD,KAAA,CAAC,CAAC;AACL,CAAC,CAAC;IAEW,oBAAoB,GAAG,UAClC,KAAqB,EACrB,WAAuB,EACvB,EAA8C,EAAA;AAD9C,IAAA,IAAA,WAAA,KAAA,KAAA,CAAA,EAAA,EAAA,WAAuB,GAAA,IAAA,CAAA,EAAA;AACvB,IAAA,IAAA,EAAA,GAAA,EAAA,KAAA,KAAA,CAAA,GAA4C,EAAE,GAAA,EAAA,EAA5C,OAAO,GAAA,EAAA,CAAA,OAAA,EAAE,UAAU,GAAA,EAAA,CAAA,UAAA,CAAA;AAErB,IAAA,IAAM,OAAO,GAAG;QACd,OAAO,EAAE,OAAO,IAAI,MAAM;QAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,UAAU,IAAI,EAAE,CAAC;KAChD,CAAC;AAEI,IAAA,IAAA,EAAqC,GAAA,qBAAqB,CAC9D,KAAK,EACL,IAAI,EACJ,OAAO,CACR,EAJM,cAAc,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,gBAAgB,QAItC,CAAC;IAEF,IAAM,GAAG,GAAG,EAAE,CAAC;AAEf,IAAA,IAAI,cAAc,CAAC,MAAM,EAAE;AACzB,QAAA,GAAG,CAAC,IAAI,CACN,IAAI,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,CACrE,CAAC;KACH;AAED,IAAA,GAAG,CAAC,IAAI,CAAA,KAAA,CAAR,GAAG,EAAS,gBAAgB,CAAE,CAAA;AAE9B,IAAA,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE;AACpB,QAAA,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;KACf;IACD,OAAO,IAAI,cAAc,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;AAC9D,EAAE;AAEF,IAAM,qBAAqB,GAAG,UAC5B,KAAU,EACV,SAAiB,EACjB,OAAgB,EAAA;IAEhB,IAAM,cAAc,GAAG,EAAE,CAAC;IAC1B,IAAM,gBAAgB,GAAG,EAAE,CAAC;AAC5B,IAAA,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,EAAE;AAC3B,QAAA,cAAc,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAChE,QAAA,OAAO,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;KAC3C;AACD,IAAA,KAAK,IAAM,GAAG,IAAI,KAAK,EAAE;QACvB,IAAI,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;AAC1C,YAAA,IAAM,EAAE,GAAG,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YAEjE,IAAI,EAAE,EAAE;AACN,gBAAA,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,SAAS,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;AAC7D,oBAAA,MAAM,IAAI,KAAK,CACb,2BAAoB,GAAG,EAAA,sCAAA,CAAsC,CAC9D,CAAC;iBACH;aACF;;AAGD,YAAA,IAAI,EAAE,IAAI,IAAI,EAAE;AACd,gBAAA,cAAc,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aACzB;SACF;aAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE;YAChC,yBAAyB,CAAC,GAAG,CAAC,CAAC;SAChC;aAAM;YACL,gBAAgB,CAAC,IAAI,CACnB,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,CAAC,CACvE,CAAC;SACH;KACF;AAED,IAAA,OAAO,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;AAC5C,CAAC,CAAC;AAEK,IAAM,qBAAqB,GAChC,UAAQ,SAA2B,EAAA;AACnC,IAAA,OAAA,UAAC,IAAW,EAAE,GAAS,EAAE,KAAW,EAAA;QAClC,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACjC,OAAO,SAAS,CAAC,IAAI,CAAC;KACvB,CAAA;AAJD,EAIE;AAES,IAAA,iBAAiB,GAAG,UAC/B,KAAqB,EACrB,OAA8B,EAAA;AAA9B,IAAA,IAAA,OAAA,KAAA,KAAA,CAAA,EAAA,EAAA,OAA8B,GAAA,EAAA,CAAA,EAAA;IAE9B,OAAO,qBAAqB,CAC1B,oBAAoB,CAAiB,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAC3D,CAAC;AACJ;;AC7dA,IAAA,GAAA,kBAAA,UAAA,MAAA,EAAA;IAAkB,SAAkB,CAAA,GAAA,EAAA,MAAA,CAAA,CAAA;AAApC,IAAA,SAAA,GAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAexB;AAbC,IAAA,GAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;AACE,QAAA,IAAI,CAAC,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KAC9D,CAAA;AACD,IAAA,GAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;QACE,MAAK,CAAA,SAAA,CAAC,KAAK,CAAA,IAAA,CAAA,IAAA,CAAE,CAAC;AACd,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KAClB,CAAA;IACD,GAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAA;AACZ,QAAA,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE;AACpB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,YAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;SACnB;KACF,CAAA;IACH,OAAC,GAAA,CAAA;AAAD,CAhBA,CAAkB,aAAa,CAgB9B,CAAA,CAAA;AACD;AACA,IAAA,UAAA,kBAAA,UAAA,MAAA,EAAA;IAAyB,SAAyB,CAAA,UAAA,EAAA,MAAA,CAAA,CAAA;AAAlD,IAAA,SAAA,UAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAiCxB;AA/BC,IAAA,UAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;AACE,QAAA,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,EAAE;AACnD,YAAA,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;SACnE;AACD,QAAA,IAAI,CAAC,eAAe,GAAG,oBAAoB,CACzC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,OAAO,CACb,CAAC;KACH,CAAA;AACD,IAAA,UAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;QACE,MAAK,CAAA,SAAA,CAAC,KAAK,CAAA,IAAA,CAAA,IAAA,CAAE,CAAC;AACd,QAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;KAC9B,CAAA;IACD,UAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAA;AACZ,QAAA,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE;AACjB,YAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAT,MAAA,EAAW,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;;;AAGlD,gBAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;AAE7B,gBAAA,IAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AACtB,gBAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;AACjD,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;aACpD;AACD,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SAClB;aAAM;AACL,YAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,YAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;SACnB;KACF,CAAA;IACH,OAAC,UAAA,CAAA;AAAD,CAlCA,CAAyB,aAAa,CAkCrC,CAAA,CAAA;AAED,IAAA,IAAA,kBAAA,UAAA,MAAA,EAAA;IAAmB,SAAyB,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA;AAA5C,IAAA,SAAA,IAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAkBxB;AAhBC,IAAA,IAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;AACE,QAAA,IAAI,CAAC,eAAe,GAAG,oBAAoB,CACzC,IAAI,CAAC,MAAM,EACX,IAAI,CAAC,WAAW,EAChB,IAAI,CAAC,OAAO,CACb,CAAC;KACH,CAAA;AACD,IAAA,IAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;QACE,MAAK,CAAA,SAAA,CAAC,KAAK,CAAA,IAAA,CAAA,IAAA,CAAE,CAAC;AACd,QAAA,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;KAC9B,CAAA;IACD,IAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,IAAa,EAAA;AACjD,QAAA,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;QACtC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC;KACxC,CAAA;IACH,OAAC,IAAA,CAAA;AAAD,CAnBA,CAAmB,aAAa,CAmB/B,CAAA,CAAA;AAED,IAAA,KAAA,kBAAA,UAAA,MAAA,EAAA;IAA2B,SAAkB,CAAA,KAAA,EAAA,MAAA,CAAA,CAAA;AAA7C,IAAA,SAAA,KAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAYxB;IAXC,KAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,eAAS,CAAA;IACT,KAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAI,EAAA;AACP,QAAA,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,EAAE;AAChD,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SAClB;;;;;KAKF,CAAA;IACH,OAAC,KAAA,CAAA;AAAD,CAbA,CAA2B,aAAa,CAavC,EAAA;AAED,IAAM,mBAAmB,GAAG,UAAC,MAAa,EAAA;AACxC,IAAA,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE;AACvB,QAAA,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;KAC3D;AACH,CAAC,CAAC;AAEF,IAAA,GAAA,kBAAA,UAAA,MAAA,EAAA;IAAkB,SAAkB,CAAA,GAAA,EAAA,MAAA,CAAA,CAAA;AAApC,IAAA,SAAA,GAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,KAAK,CAAC;;KA+BzB;AA7BC,IAAA,GAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;QAAA,IAKC,KAAA,GAAA,IAAA,CAAA;AAJC,QAAA,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAC,EAAE,EAAA;YAC7B,OAAA,oBAAoB,CAAC,EAAE,EAAE,IAAI,EAAE,KAAI,CAAC,OAAO,CAAC,CAAA;AAA5C,SAA4C,CAC7C,CAAC;KACH,CAAA;AACD,IAAA,GAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;AACE,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,QAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAC,IAAI,CAAd,MAAA,EAAgB,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;YACvD,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;SACtB;KACF,CAAA;AACD,IAAA,GAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAA;QAClC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,OAAO,GAAG,KAAK,CAAC;AACpB,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAC,IAAI,CAAd,MAAA,EAAgB,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;YACvD,IAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACxB,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAC1B,YAAA,IAAI,EAAE,CAAC,IAAI,EAAE;gBACX,IAAI,GAAG,IAAI,CAAC;AACZ,gBAAA,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC;gBAClB,MAAM;aACP;SACF;AAED,QAAA,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;AACpB,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KAClB,CAAA;IACH,OAAC,GAAA,CAAA;AAAD,CAhCA,CAAkB,aAAa,CAgC9B,CAAA,CAAA;AAED,IAAA,IAAA,kBAAA,UAAA,MAAA,EAAA;IAAmB,SAAG,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA;AAAtB,IAAA,SAAA,IAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,KAAK,CAAC;;KAKzB;AAJC,IAAA,IAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAA;QAClC,MAAK,CAAA,SAAA,CAAC,IAAI,CAAC,IAAA,CAAA,IAAA,EAAA,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;AAC7B,QAAA,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;KACxB,CAAA;IACH,OAAC,IAAA,CAAA;AAAD,CANA,CAAmB,GAAG,CAMrB,CAAA,CAAA;AAED,IAAA,GAAA,kBAAA,UAAA,MAAA,EAAA;IAAkB,SAAkB,CAAA,GAAA,EAAA,MAAA,CAAA,CAAA;AAApC,IAAA,SAAA,GAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KA0BxB;AAxBC,IAAA,GAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,YAAA;QAAA,IAQC,KAAA,GAAA,IAAA,CAAA;QAPC,IAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxE,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,UAAC,KAAK,EAAA;YAC/B,IAAI,iBAAiB,CAAC,KAAK,EAAE,KAAI,CAAC,OAAO,CAAC,EAAE;AAC1C,gBAAA,MAAM,IAAI,KAAK,CAAC,sBAAA,CAAA,MAAA,CAAuB,KAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAE,CAAC,CAAC;aACnE;YACD,OAAO,YAAY,CAAC,KAAK,EAAE,KAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACnD,SAAC,CAAC,CAAC;KACJ,CAAA;AACD,IAAA,GAAA,CAAA,SAAA,CAAA,IAAI,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAA;QAClC,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,IAAI,OAAO,GAAG,KAAK,CAAC;AACpB,QAAA,KAAS,IAAA,CAAC,GAAG,CAAC,EAAI,QAAM,GAAK,IAAI,CAAC,QAAQ,CAAlB,MAAA,EAAoB,CAAC,GAAG,QAAM,EAAE,CAAC,EAAE,EAAE;YAC3D,IAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC9B,YAAA,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE;gBACd,IAAI,GAAG,IAAI,CAAC;gBACZ,OAAO,GAAG,IAAI,CAAC;gBACf,MAAM;aACP;SACF;AAED,QAAA,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC;AACpB,QAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;KAClB,CAAA;IACH,OAAC,GAAA,CAAA;AAAD,CA3BA,CAAkB,aAAa,CA2B9B,CAAA,CAAA;AAED,IAAA,IAAA,kBAAA,UAAA,MAAA,EAAA;IAAmB,SAAkB,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA;AAGnC,IAAA,SAAA,IAAA,CAAY,MAAW,EAAE,UAAe,EAAE,OAAgB,EAAE,IAAY,EAAA;QACtE,IAAA,KAAA,GAAA,MAAK,CAAC,IAAA,CAAA,IAAA,EAAA,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,IAAC,IAAA,CAAA;QAHlC,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;AAIrB,QAAA,KAAI,CAAC,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;;KACvD;IACD,IAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,IAAa,EAAA;QACjD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QAEhC,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE;AAC3B,YAAA,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;AACjB,gBAAA,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC;AAClB,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aAClB;iBAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;AAClC,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,gBAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;aAClB;SACF;aAAM;YACL,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AAC3B,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SAClB;KACF,CAAA;AACD,IAAA,IAAA,CAAA,SAAA,CAAA,KAAK,GAAL,YAAA;QACE,MAAK,CAAA,SAAA,CAAC,KAAK,CAAA,IAAA,CAAA,IAAA,CAAE,CAAC;AACd,QAAA,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;KAClB,CAAA;IACH,OAAC,IAAA,CAAA;AAAD,CA3BA,CAAmB,aAAa,CA2B/B,CAAA,CAAA;AAED,IAAA,OAAA,kBAAA,UAAA,MAAA,EAAA;IAAsB,SAAsB,CAAA,OAAA,EAAA,MAAA,CAAA,CAAA;AAA5C,IAAA,SAAA,OAAA,GAAA;;QACW,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KAUxB;IATC,OAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,IAAa,EAAE,IAAc,EAAA;QACjE,IAAI,CAAC,IAAI,EAAE;AACT,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,YAAA,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;SAC1B;aAAM,IAAI,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE;AACpD,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;AACjB,YAAA,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;SAClB;KACF,CAAA;IACH,OAAC,OAAA,CAAA;AAAD,CAXA,CAAsB,aAAa,CAWlC,CAAA,CAAA;AAED,IAAA,IAAA,kBAAA,UAAA,MAAA,EAAA;IAAmB,SAAmB,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA;AAEpC,IAAA,SAAA,IAAA,CACE,MAAoB,EACpB,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA;AAEZ,QAAA,IAAA,KAAA,GAAA,MAAK,CAAA,IAAA,CAAA,IAAA,EACH,MAAM,EACN,WAAW,EACX,OAAO,EACP,MAAM,CAAC,GAAG,CAAC,UAAC,KAAK,EAAA,EAAK,OAAA,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA,EAAA,CAAC,EACxE,IAAI,CACL,IAAC,IAAA,CAAA;QAbK,KAAM,CAAA,MAAA,GAAG,KAAK,CAAC;QAetB,mBAAmB,CAAC,MAAM,CAAC,CAAC;;KAC7B;IACD,IAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,IAAa,EAAA;QACjD,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;KAC3C,CAAA;IACH,OAAC,IAAA,CAAA;AAAD,CArBA,CAAmB,mBAAmB,CAqBrC,CAAA,CAAA;AAED,IAAA,IAAA,kBAAA,UAAA,MAAA,EAAA;IAAmB,SAAmB,CAAA,IAAA,EAAA,MAAA,CAAA,CAAA;AAEpC,IAAA,SAAA,IAAA,CACE,MAAoB,EACpB,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA;AAEZ,QAAA,IAAA,KAAA,GAAA,MAAK,CAAA,IAAA,CAAA,IAAA,EACH,MAAM,EACN,WAAW,EACX,OAAO,EACP,MAAM,CAAC,GAAG,CAAC,UAAC,KAAK,EAAA,EAAK,OAAA,oBAAoB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA,EAAA,CAAC,EACxE,IAAI,CACL,IAAC,IAAA,CAAA;QAbK,KAAM,CAAA,MAAA,GAAG,IAAI,CAAC;;KActB;IACD,IAAI,CAAA,SAAA,CAAA,IAAA,GAAJ,UAAK,IAAS,EAAE,GAAQ,EAAE,KAAU,EAAE,IAAa,EAAA;QACjD,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;KAC3C,CAAA;IACH,OAAC,IAAA,CAAA;AAAD,CAnBA,CAAmB,mBAAmB,CAmBrC,CAAA,CAAA;IAEY,GAAG,GAAG,UAAC,MAAW,EAAE,WAAuB,EAAE,OAAgB,EAAA;IACxE,OAAA,IAAI,eAAe,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAA;AAAjD,EAAkD;AACvC,IAAA,GAAG,GAAG,UACjB,MAAW,EACX,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA3C,GAA4C;AACpC,IAAA,GAAG,GAAG,UACjB,MAAoB,EACpB,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA3C,GAA4C;AACpC,IAAA,IAAI,GAAG,UAClB,MAAoB,EACpB,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA5C,GAA6C;AACrC,IAAA,UAAU,GAAG,UACxB,MAAW,EACX,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAlD,GAAmD;AAC3C,IAAA,IAAI,GAAG,UAClB,MAAW,EACX,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA5C,GAA6C;AACrC,IAAA,GAAG,GAAG,UACjB,MAAW,EACX,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA;IAEZ,OAAO,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;AACrD,EAAE;AAEW,IAAA,GAAG,GAAG,kBAAkB,CAAC,UAAC,MAAM,EAAA,EAAK,OAAA,UAAC,CAAC,EAAA;AAClD,IAAA,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,MAAM,CAAC;AACjC,CAAC,CAAA,EAAA,EAAE;AACU,IAAA,IAAI,GAAG,kBAAkB,CAAC,UAAC,MAAM,EAAA,EAAK,OAAA,UAAC,CAAC,EAAA;AACnD,IAAA,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC;AACrC,CAAC,CAAA,EAAA,EAAE;AACU,IAAA,GAAG,GAAG,kBAAkB,CAAC,UAAC,MAAM,EAAA,EAAK,OAAA,UAAC,CAAC,EAAA;AAClD,IAAA,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,MAAM,CAAC;AACjC,CAAC,CAAA,EAAA,EAAE;AACU,IAAA,IAAI,GAAG,kBAAkB,CAAC,UAAC,MAAM,EAAA,EAAK,OAAA,UAAC,CAAC,EAAA;AACnD,IAAA,OAAO,CAAC,KAAK,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC;AACrC,CAAC,CAAA,EAAA,EAAE;IACU,IAAI,GAAG,UAClB,EAA4B,EAC5B,WAAuB,EACvB,OAAgB,EAAA;QAFf,GAAG,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,WAAW,GAAA,EAAA,CAAA,CAAA,CAAA,CAAA;IAIjB,OAAA,IAAI,eAAe,CACjB,UAAC,CAAC,EAAK,EAAA,OAAA,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,WAAW,CAAA,EAAA,EAC1C,WAAW,EACX,OAAO,CACR,CAAA;AAJD,EAIE;AACS,IAAA,OAAO,GAAG,UACrB,MAAe,EACf,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA/C,GAAgD;IACxC,MAAM,GAAG,UACpB,OAAe,EACf,WAAuB,EACvB,OAAgB,EAAA;AAEhB,IAAA,OAAA,IAAI,eAAe,CACjB,IAAI,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,QAAQ,CAAC,EACzC,WAAW,EACX,OAAO,CACR,CAAA;AAJD,EAIE;AACS,IAAA,IAAI,GAAG,UAClB,MAAW,EACX,WAAuB,EACvB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,IAAI,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,CAA5C,GAA6C;AAElD,IAAM,WAAW,GAAG;IAClB,MAAM,EAAE,UAAC,CAAC,EAAK,EAAA,OAAA,OAAO,CAAC,KAAK,QAAQ,CAAA,EAAA;IACpC,MAAM,EAAE,UAAC,CAAC,EAAK,EAAA,OAAA,OAAO,CAAC,KAAK,QAAQ,CAAA,EAAA;IACpC,IAAI,EAAE,UAAC,CAAC,EAAK,EAAA,OAAA,OAAO,CAAC,KAAK,SAAS,CAAA,EAAA;AACnC,IAAA,KAAK,EAAE,UAAC,CAAC,EAAA,EAAK,OAAA,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA,EAAA;IAC9B,IAAI,EAAE,UAAC,CAAC,EAAA,EAAK,OAAA,CAAC,KAAK,IAAI,CAAA,EAAA;IACvB,SAAS,EAAE,UAAC,CAAC,EAAA,EAAK,OAAA,CAAC,YAAY,IAAI,CAAA,EAAA;CACpC,CAAC;IAEW,KAAK,GAAG,UACnB,KAAwB,EACxB,WAAuB,EACvB,OAAgB,EAAA;AAEhB,IAAA,OAAA,IAAI,eAAe,CACjB,UAAC,CAAC,EAAA;AACA,QAAA,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE;AAC7B,YAAA,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE;AACvB,gBAAA,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;aAC9C;AAED,YAAA,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;SAC9B;AAED,QAAA,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC,YAAY,KAAK,IAAI,CAAC,CAAC,WAAW,KAAK,KAAK,GAAG,KAAK,CAAC;AAC3E,KAAC,EACD,WAAW,EACX,OAAO,CACR,CAAA;AAdD,EAcE;AACS,IAAA,IAAI,GAAG,UAClB,MAAoB,EACpB,UAAsB,EACtB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAA3C,GAA4C;AAEpC,IAAA,IAAI,GAAG,UAClB,MAAoB,EACpB,UAAsB,EACtB,OAAgB,EAChB,IAAY,EAAA,EACT,OAAA,IAAI,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,IAAI,CAAC,CAA3C,GAA4C;AACpC,IAAA,KAAK,GAAG,UACnB,MAAc,EACd,UAAsB,EACtB,OAAgB,EACb,EAAA,OAAA,IAAI,KAAK,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,CAAC,CAA/C,GAAgD;IACxC,QAAQ,GAAG,cAAM,OAAA,IAAI,CAAJ,GAAK;IACtB,MAAM,GAAG,UACpB,MAAyB,EACzB,UAAsB,EACtB,OAAgB,EAAA;AAEhB,IAAA,IAAI,IAAI,CAAC;AAET,IAAA,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE;QACtB,IAAI,GAAG,MAAM,CAAC;KACf;AAAM,SAAA,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE;QACnC,IAAI,GAAG,IAAI,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC,CAAC;KAChD;SAAM;AACL,QAAA,MAAM,IAAI,KAAK,CACb,oEAAkE,CACnE,CAAC;KACH;IAED,OAAO,IAAI,eAAe,CAAC,UAAC,CAAC,EAAK,EAAA,OAAA,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAf,EAAe,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAC1E;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACpZA,IAAM,2BAA2B,GAAG,UAClC,KAAqB,EACrB,UAAe,EACf,EAA8C,EAAA;AAA9C,IAAA,IAAA,EAAA,GAAA,EAAA,KAAA,KAAA,CAAA,GAA4C,EAAE,GAAA,EAAA,EAA5C,OAAO,GAAA,EAAA,CAAA,OAAA,EAAE,UAAU,GAAA,EAAA,CAAA,UAAA,CAAA;AAErB,IAAA,OAAO,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE;AAC7C,QAAA,OAAO,EAAA,OAAA;AACP,QAAA,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,iBAAiB,EAAE,UAAU,IAAI,EAAE,CAAC;AACnE,KAAA,CAAC,CAAC;AACL,EAAE;AAEF,IAAM,wBAAwB,GAAG,UAC/B,KAAqB,EACrB,OAA8B,EAAA;AAA9B,IAAA,IAAA,OAAA,KAAA,KAAA,CAAA,EAAA,EAAA,OAA8B,GAAA,EAAA,CAAA,EAAA;IAE9B,IAAM,EAAE,GAAG,2BAA2B,CAAC,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AAC7D,IAAA,OAAO,qBAAqB,CAAC,EAAE,CAAC,CAAC;AACnC;;;;","x_google_ignoreList":[0]} \ No newline at end of file diff --git a/backend/node_modules/sift/index.d.ts b/backend/node_modules/sift/index.d.ts new file mode 100644 index 0000000..2b82571 --- /dev/null +++ b/backend/node_modules/sift/index.d.ts @@ -0,0 +1,4 @@ +import sift from "./lib"; + +export default sift; +export * from "./lib"; diff --git a/backend/node_modules/sift/index.js b/backend/node_modules/sift/index.js new file mode 100644 index 0000000..449294a --- /dev/null +++ b/backend/node_modules/sift/index.js @@ -0,0 +1,4 @@ +const lib = require("./lib"); + +module.exports = lib.default; +Object.assign(module.exports, lib); diff --git a/backend/node_modules/sift/lib/core.d.ts b/backend/node_modules/sift/lib/core.d.ts new file mode 100644 index 0000000..1e801ae --- /dev/null +++ b/backend/node_modules/sift/lib/core.d.ts @@ -0,0 +1,116 @@ +import { Key, Comparator } from "./utils"; +export interface Operation { + readonly keep: boolean; + readonly done: boolean; + propop: boolean; + reset(): any; + next(item: TItem, key?: Key, owner?: any, root?: boolean, leaf?: boolean): any; +} +export type Tester = (item: any, key?: Key, owner?: any, root?: boolean, leaf?: boolean) => boolean; +export interface NamedOperation { + name: string; +} +export type OperationCreator = (params: any, parentQuery: any, options: Options, name: string) => Operation; +export type BasicValueQuery = { + $eq?: TValue; + $ne?: TValue; + $lt?: TValue; + $gt?: TValue; + $lte?: TValue; + $gte?: TValue; + $in?: TValue[]; + $nin?: TValue[]; + $all?: TValue[]; + $mod?: [number, number]; + $exists?: boolean; + $regex?: string | RegExp; + $size?: number; + $where?: ((this: TValue, obj: TValue) => boolean) | string; + $options?: "i" | "g" | "m" | "u"; + $type?: Function; + $not?: NestedQuery; + $or?: NestedQuery[]; + $nor?: NestedQuery[]; + $and?: NestedQuery[]; +}; +export type ArrayValueQuery = { + $elemMatch?: Query; +} & BasicValueQuery; +type Unpacked = T extends (infer U)[] ? U : T; +export type ValueQuery = TValue extends Array ? ArrayValueQuery> : BasicValueQuery; +type NotObject = string | number | Date | boolean | Array; +export type ShapeQuery = TItemSchema extends NotObject ? {} : { + [k in keyof TItemSchema]?: TItemSchema[k] | ValueQuery; +}; +export type NestedQuery = ValueQuery & ShapeQuery; +export type Query = TItemSchema | RegExp | NestedQuery; +export type QueryOperators = keyof ValueQuery; +export declare abstract class BaseOperation implements Operation { + readonly params: TParams; + readonly owneryQuery: any; + readonly options: Options; + readonly name?: string; + keep: boolean; + done: boolean; + abstract propop: boolean; + constructor(params: TParams, owneryQuery: any, options: Options, name?: string); + protected init(): void; + reset(): void; + abstract next(item: any, key: Key, parent: any, root: boolean, leaf?: boolean): any; +} +declare abstract class GroupOperation extends BaseOperation { + readonly children: Operation[]; + keep: boolean; + done: boolean; + constructor(params: any, owneryQuery: any, options: Options, children: Operation[]); + /** + */ + reset(): void; + abstract next(item: any, key: Key, owner: any, root: boolean): any; + /** + */ + protected childrenNext(item: any, key: Key, owner: any, root: boolean, leaf?: boolean): void; +} +export declare abstract class NamedGroupOperation extends GroupOperation implements NamedOperation { + readonly name: string; + abstract propop: boolean; + constructor(params: any, owneryQuery: any, options: Options, children: Operation[], name: string); +} +export declare class QueryOperation extends GroupOperation { + readonly propop = true; + /** + */ + next(item: TItem, key: Key, parent: any, root: boolean): void; +} +export declare class NestedOperation extends GroupOperation { + readonly keyPath: Key[]; + readonly propop = true; + constructor(keyPath: Key[], params: any, owneryQuery: any, options: Options, children: Operation[]); + /** + */ + next(item: any, key: Key, parent: any): void; + /** + */ + private _nextNestedValue; +} +export declare const createTester: (a: any, compare: Comparator) => any; +export declare class EqualsOperation extends BaseOperation { + readonly propop = true; + private _test; + init(): void; + next(item: any, key: Key, parent: any): void; +} +export declare const createEqualsOperation: (params: any, owneryQuery: any, options: Options) => EqualsOperation; +export declare const numericalOperationCreator: (createNumericalOperation: OperationCreator) => (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export declare const numericalOperation: (createTester: (value: any) => Tester) => (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export type Options = { + operations: { + [identifier: string]: OperationCreator; + }; + compare: (a: any, b: any) => boolean; +}; +export declare const containsOperation: (query: any, options: Options) => boolean; +export declare const createQueryOperation: (query: Query, owneryQuery?: any, { compare, operations }?: Partial) => QueryOperation; +export declare const createOperationTester: (operation: Operation) => (item: TItem, key?: Key, owner?: any) => boolean; +export declare const createQueryTester: (query: Query, options?: Partial) => (item: TItem, key?: Key, owner?: any) => boolean; +export {}; diff --git a/backend/node_modules/sift/lib/index.d.ts b/backend/node_modules/sift/lib/index.d.ts new file mode 100644 index 0000000..6218e8b --- /dev/null +++ b/backend/node_modules/sift/lib/index.d.ts @@ -0,0 +1,6 @@ +import { Query, QueryOperators, BasicValueQuery, ArrayValueQuery, ValueQuery, NestedQuery, ShapeQuery, Options, createQueryTester, EqualsOperation, createQueryOperation, createEqualsOperation, createOperationTester } from "./core"; +declare const createDefaultQueryOperation: (query: Query, ownerQuery: any, { compare, operations }?: Partial) => import("./core").QueryOperation; +declare const createDefaultQueryTester: (query: Query, options?: Partial) => (item: unknown, key?: import("./utils").Key, owner?: any) => boolean; +export { Query, QueryOperators, BasicValueQuery, ArrayValueQuery, ValueQuery, NestedQuery, ShapeQuery, EqualsOperation, createQueryTester, createOperationTester, createDefaultQueryOperation, createEqualsOperation, createQueryOperation, }; +export * from "./operations"; +export default createDefaultQueryTester; diff --git a/backend/node_modules/sift/lib/index.js b/backend/node_modules/sift/lib/index.js new file mode 100644 index 0000000..74b5289 --- /dev/null +++ b/backend/node_modules/sift/lib/index.js @@ -0,0 +1,781 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sift = {})); +})(this, (function (exports) { 'use strict'; + + /****************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise, SuppressedError, Symbol */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; + }; + + var typeChecker = function (type) { + var typeString = "[object " + type + "]"; + return function (value) { + return getClassName(value) === typeString; + }; + }; + var getClassName = function (value) { return Object.prototype.toString.call(value); }; + var comparable = function (value) { + if (value instanceof Date) { + return value.getTime(); + } + else if (isArray(value)) { + return value.map(comparable); + } + else if (value && typeof value.toJSON === "function") { + return value.toJSON(); + } + return value; + }; + var coercePotentiallyNull = function (value) { + return value == null ? null : value; + }; + var isArray = typeChecker("Array"); + var isObject = typeChecker("Object"); + var isFunction = typeChecker("Function"); + var isProperty = function (item, key) { + return item.hasOwnProperty(key) && !isFunction(item[key]); + }; + var isVanillaObject = function (value) { + return (value && + (value.constructor === Object || + value.constructor === Array || + value.constructor.toString() === "function Object() { [native code] }" || + value.constructor.toString() === "function Array() { [native code] }") && + !value.toJSON); + }; + var equals = function (a, b) { + if (a == null && a == b) { + return true; + } + if (a === b) { + return true; + } + if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) { + return false; + } + if (isArray(a)) { + if (a.length !== b.length) { + return false; + } + for (var i = 0, length_1 = a.length; i < length_1; i++) { + if (!equals(a[i], b[i])) + return false; + } + return true; + } + else if (isObject(a)) { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (var key in a) { + if (!equals(a[key], b[key])) + return false; + } + return true; + } + return false; + }; + + /** + * Walks through each value given the context - used for nested operations. E.g: + * { "person.address": { $eq: "blarg" }} + */ + var walkKeyPathValues = function (item, keyPath, next, depth, key, owner) { + var currentKey = keyPath[depth]; + // if array, then try matching. Might fall through for cases like: + // { $eq: [1, 2, 3] }, [ 1, 2, 3 ]. + if (isArray(item) && + isNaN(Number(currentKey)) && + !isProperty(item, currentKey)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // if FALSE is returned, then terminate walker. For operations, this simply + // means that the search critera was met. + if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) { + return false; + } + } + } + if (depth === keyPath.length || item == null) { + return next(item, key, owner, depth === 0, depth === keyPath.length); + } + return walkKeyPathValues(item[currentKey], keyPath, next, depth + 1, currentKey, item); + }; + var BaseOperation = /** @class */ (function () { + function BaseOperation(params, owneryQuery, options, name) { + this.params = params; + this.owneryQuery = owneryQuery; + this.options = options; + this.name = name; + this.init(); + } + BaseOperation.prototype.init = function () { }; + BaseOperation.prototype.reset = function () { + this.done = false; + this.keep = false; + }; + return BaseOperation; + }()); + var GroupOperation = /** @class */ (function (_super) { + __extends(GroupOperation, _super); + function GroupOperation(params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options) || this; + _this.children = children; + return _this; + } + /** + */ + GroupOperation.prototype.reset = function () { + this.keep = false; + this.done = false; + for (var i = 0, length_2 = this.children.length; i < length_2; i++) { + this.children[i].reset(); + } + }; + /** + */ + GroupOperation.prototype.childrenNext = function (item, key, owner, root, leaf) { + var done = true; + var keep = true; + for (var i = 0, length_3 = this.children.length; i < length_3; i++) { + var childOperation = this.children[i]; + if (!childOperation.done) { + childOperation.next(item, key, owner, root, leaf); + } + if (!childOperation.keep) { + keep = false; + } + if (childOperation.done) { + if (!childOperation.keep) { + break; + } + } + else { + done = false; + } + } + this.done = done; + this.keep = keep; + }; + return GroupOperation; + }(BaseOperation)); + var NamedGroupOperation = /** @class */ (function (_super) { + __extends(NamedGroupOperation, _super); + function NamedGroupOperation(params, owneryQuery, options, children, name) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.name = name; + return _this; + } + return NamedGroupOperation; + }(GroupOperation)); + var QueryOperation = /** @class */ (function (_super) { + __extends(QueryOperation, _super); + function QueryOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + /** + */ + QueryOperation.prototype.next = function (item, key, parent, root) { + this.childrenNext(item, key, parent, root); + }; + return QueryOperation; + }(GroupOperation)); + var NestedOperation = /** @class */ (function (_super) { + __extends(NestedOperation, _super); + function NestedOperation(keyPath, params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.keyPath = keyPath; + _this.propop = true; + /** + */ + _this._nextNestedValue = function (value, key, owner, root, leaf) { + _this.childrenNext(value, key, owner, root, leaf); + return !_this.done; + }; + return _this; + } + /** + */ + NestedOperation.prototype.next = function (item, key, parent) { + walkKeyPathValues(item, this.keyPath, this._nextNestedValue, 0, key, parent); + }; + return NestedOperation; + }(GroupOperation)); + var createTester = function (a, compare) { + if (a instanceof Function) { + return a; + } + if (a instanceof RegExp) { + return function (b) { + var result = typeof b === "string" && a.test(b); + a.lastIndex = 0; + return result; + }; + } + var comparableA = comparable(a); + return function (b) { return compare(comparableA, comparable(b)); }; + }; + var EqualsOperation = /** @class */ (function (_super) { + __extends(EqualsOperation, _super); + function EqualsOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + EqualsOperation.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + EqualsOperation.prototype.next = function (item, key, parent) { + if (!Array.isArray(parent) || parent.hasOwnProperty(key)) { + if (this._test(item, key, parent)) { + this.done = true; + this.keep = true; + } + } + }; + return EqualsOperation; + }(BaseOperation)); + var createEqualsOperation = function (params, owneryQuery, options) { return new EqualsOperation(params, owneryQuery, options); }; + var numericalOperationCreator = function (createNumericalOperation) { + return function (params, owneryQuery, options, name) { + return createNumericalOperation(params, owneryQuery, options, name); + }; + }; + var numericalOperation = function (createTester) { + return numericalOperationCreator(function (params, owneryQuery, options, name) { + var typeofParams = typeof comparable(params); + var test = createTester(params); + return new EqualsOperation(function (b) { + var actualValue = coercePotentiallyNull(b); + return (typeof comparable(actualValue) === typeofParams && test(actualValue)); + }, owneryQuery, options, name); + }); + }; + var createNamedOperation = function (name, params, parentQuery, options) { + var operationCreator = options.operations[name]; + if (!operationCreator) { + throwUnsupportedOperation(name); + } + return operationCreator(params, parentQuery, options, name); + }; + var throwUnsupportedOperation = function (name) { + throw new Error("Unsupported operation: ".concat(name)); + }; + var containsOperation = function (query, options) { + for (var key in query) { + if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") + return true; + } + return false; + }; + var createNestedOperation = function (keyPath, nestedQuery, parentKey, owneryQuery, options) { + if (containsOperation(nestedQuery, options)) { + var _a = createQueryOperations(nestedQuery, parentKey, options), selfOperations = _a[0], nestedOperations = _a[1]; + if (nestedOperations.length) { + throw new Error("Property queries must contain only operations, or exact objects."); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, selfOperations); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [ + new EqualsOperation(nestedQuery, owneryQuery, options), + ]); + }; + var createQueryOperation = function (query, owneryQuery, _a) { + if (owneryQuery === void 0) { owneryQuery = null; } + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + var options = { + compare: compare || equals, + operations: Object.assign({}, operations || {}), + }; + var _c = createQueryOperations(query, null, options), selfOperations = _c[0], nestedOperations = _c[1]; + var ops = []; + if (selfOperations.length) { + ops.push(new NestedOperation([], query, owneryQuery, options, selfOperations)); + } + ops.push.apply(ops, nestedOperations); + if (ops.length === 1) { + return ops[0]; + } + return new QueryOperation(query, owneryQuery, options, ops); + }; + var createQueryOperations = function (query, parentKey, options) { + var selfOperations = []; + var nestedOperations = []; + if (!isVanillaObject(query)) { + selfOperations.push(new EqualsOperation(query, query, options)); + return [selfOperations, nestedOperations]; + } + for (var key in query) { + if (options.operations.hasOwnProperty(key)) { + var op = createNamedOperation(key, query[key], query, options); + if (op) { + if (!op.propop && parentKey && !options.operations[parentKey]) { + throw new Error("Malformed query. ".concat(key, " cannot be matched against property.")); + } + } + // probably just a flag for another operation (like $options) + if (op != null) { + selfOperations.push(op); + } + } + else if (key.charAt(0) === "$") { + throwUnsupportedOperation(key); + } + else { + nestedOperations.push(createNestedOperation(key.split("."), query[key], key, query, options)); + } + } + return [selfOperations, nestedOperations]; + }; + var createOperationTester = function (operation) { + return function (item, key, owner) { + operation.reset(); + operation.next(item, key, owner); + return operation.keep; + }; + }; + var createQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + return createOperationTester(createQueryOperation(query, null, options)); + }; + + var $Ne = /** @class */ (function (_super) { + __extends($Ne, _super); + function $Ne() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Ne.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + $Ne.prototype.reset = function () { + _super.prototype.reset.call(this); + this.keep = true; + }; + $Ne.prototype.next = function (item) { + if (this._test(item)) { + this.done = true; + this.keep = false; + } + }; + return $Ne; + }(BaseOperation)); + // https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ + var $ElemMatch = /** @class */ (function (_super) { + __extends($ElemMatch, _super); + function $ElemMatch() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $ElemMatch.prototype.init = function () { + if (!this.params || typeof this.params !== "object") { + throw new Error("Malformed query. $elemMatch must by an object."); + } + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $ElemMatch.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $ElemMatch.prototype.next = function (item) { + if (isArray(item)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // reset query operation since item being tested needs to pass _all_ query + // operations for it to be a success + this._queryOperation.reset(); + var child = item[i]; + this._queryOperation.next(child, i, item, false); + this.keep = this.keep || this._queryOperation.keep; + } + this.done = true; + } + else { + this.done = false; + this.keep = false; + } + }; + return $ElemMatch; + }(BaseOperation)); + var $Not = /** @class */ (function (_super) { + __extends($Not, _super); + function $Not() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Not.prototype.init = function () { + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $Not.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $Not.prototype.next = function (item, key, owner, root) { + this._queryOperation.next(item, key, owner, root); + this.done = this._queryOperation.done; + this.keep = !this._queryOperation.keep; + }; + return $Not; + }(BaseOperation)); + var $Size = /** @class */ (function (_super) { + __extends($Size, _super); + function $Size() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Size.prototype.init = function () { }; + $Size.prototype.next = function (item) { + if (isArray(item) && item.length === this.params) { + this.done = true; + this.keep = true; + } + // if (parent && parent.length === this.params) { + // this.done = true; + // this.keep = true; + // } + }; + return $Size; + }(BaseOperation)); + var assertGroupNotEmpty = function (values) { + if (values.length === 0) { + throw new Error("$and/$or/$nor must be a nonempty array"); + } + }; + var $Or = /** @class */ (function (_super) { + __extends($Or, _super); + function $Or() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Or.prototype.init = function () { + var _this = this; + assertGroupNotEmpty(this.params); + this._ops = this.params.map(function (op) { + return createQueryOperation(op, null, _this.options); + }); + }; + $Or.prototype.reset = function () { + this.done = false; + this.keep = false; + for (var i = 0, length_2 = this._ops.length; i < length_2; i++) { + this._ops[i].reset(); + } + }; + $Or.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_3 = this._ops.length; i < length_3; i++) { + var op = this._ops[i]; + op.next(item, key, owner); + if (op.keep) { + done = true; + success = op.keep; + break; + } + } + this.keep = success; + this.done = done; + }; + return $Or; + }(BaseOperation)); + var $Nor = /** @class */ (function (_super) { + __extends($Nor, _super); + function $Nor() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Nor.prototype.next = function (item, key, owner) { + _super.prototype.next.call(this, item, key, owner); + this.keep = !this.keep; + }; + return $Nor; + }($Or)); + var $In = /** @class */ (function (_super) { + __extends($In, _super); + function $In() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $In.prototype.init = function () { + var _this = this; + var params = Array.isArray(this.params) ? this.params : [this.params]; + this._testers = params.map(function (value) { + if (containsOperation(value, _this.options)) { + throw new Error("cannot nest $ under ".concat(_this.name.toLowerCase())); + } + return createTester(value, _this.options.compare); + }); + }; + $In.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_4 = this._testers.length; i < length_4; i++) { + var test = this._testers[i]; + if (test(item)) { + done = true; + success = true; + break; + } + } + this.keep = success; + this.done = done; + }; + return $In; + }(BaseOperation)); + var $Nin = /** @class */ (function (_super) { + __extends($Nin, _super); + function $Nin(params, ownerQuery, options, name) { + var _this = _super.call(this, params, ownerQuery, options, name) || this; + _this.propop = true; + _this._in = new $In(params, ownerQuery, options, name); + return _this; + } + $Nin.prototype.next = function (item, key, owner, root) { + this._in.next(item, key, owner); + if (isArray(owner) && !root) { + if (this._in.keep) { + this.keep = false; + this.done = true; + } + else if (key == owner.length - 1) { + this.keep = true; + this.done = true; + } + } + else { + this.keep = !this._in.keep; + this.done = true; + } + }; + $Nin.prototype.reset = function () { + _super.prototype.reset.call(this); + this._in.reset(); + }; + return $Nin; + }(BaseOperation)); + var $Exists = /** @class */ (function (_super) { + __extends($Exists, _super); + function $Exists() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Exists.prototype.next = function (item, key, owner, root, leaf) { + if (!leaf) { + this.done = true; + this.keep = !this.params; + } + else if (owner.hasOwnProperty(key) === this.params) { + this.done = true; + this.keep = true; + } + }; + return $Exists; + }(BaseOperation)); + var $And = /** @class */ (function (_super) { + __extends($And, _super); + function $And(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = false; + assertGroupNotEmpty(params); + return _this; + } + $And.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $And; + }(NamedGroupOperation)); + var $All = /** @class */ (function (_super) { + __extends($All, _super); + function $All(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = true; + return _this; + } + $All.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $All; + }(NamedGroupOperation)); + var $eq = function (params, owneryQuery, options) { + return new EqualsOperation(params, owneryQuery, options); + }; + var $ne = function (params, owneryQuery, options, name) { return new $Ne(params, owneryQuery, options, name); }; + var $or = function (params, owneryQuery, options, name) { return new $Or(params, owneryQuery, options, name); }; + var $nor = function (params, owneryQuery, options, name) { return new $Nor(params, owneryQuery, options, name); }; + var $elemMatch = function (params, owneryQuery, options, name) { return new $ElemMatch(params, owneryQuery, options, name); }; + var $nin = function (params, owneryQuery, options, name) { return new $Nin(params, owneryQuery, options, name); }; + var $in = function (params, owneryQuery, options, name) { + return new $In(params, owneryQuery, options, name); + }; + var $lt = numericalOperation(function (params) { return function (b) { + return b != null && b < params; + }; }); + var $lte = numericalOperation(function (params) { return function (b) { + return b === params || b <= params; + }; }); + var $gt = numericalOperation(function (params) { return function (b) { + return b != null && b > params; + }; }); + var $gte = numericalOperation(function (params) { return function (b) { + return b === params || b >= params; + }; }); + var $mod = function (_a, owneryQuery, options) { + var mod = _a[0], equalsValue = _a[1]; + return new EqualsOperation(function (b) { return comparable(b) % mod === equalsValue; }, owneryQuery, options); + }; + var $exists = function (params, owneryQuery, options, name) { return new $Exists(params, owneryQuery, options, name); }; + var $regex = function (pattern, owneryQuery, options) { + return new EqualsOperation(new RegExp(pattern, owneryQuery.$options), owneryQuery, options); + }; + var $not = function (params, owneryQuery, options, name) { return new $Not(params, owneryQuery, options, name); }; + var typeAliases = { + number: function (v) { return typeof v === "number"; }, + string: function (v) { return typeof v === "string"; }, + bool: function (v) { return typeof v === "boolean"; }, + array: function (v) { return Array.isArray(v); }, + null: function (v) { return v === null; }, + timestamp: function (v) { return v instanceof Date; }, + }; + var $type = function (clazz, owneryQuery, options) { + return new EqualsOperation(function (b) { + if (typeof clazz === "string") { + if (!typeAliases[clazz]) { + throw new Error("Type alias does not exist"); + } + return typeAliases[clazz](b); + } + return b != null ? b instanceof clazz || b.constructor === clazz : false; + }, owneryQuery, options); + }; + var $and = function (params, ownerQuery, options, name) { return new $And(params, ownerQuery, options, name); }; + var $all = function (params, ownerQuery, options, name) { return new $All(params, ownerQuery, options, name); }; + var $size = function (params, ownerQuery, options) { return new $Size(params, ownerQuery, options, "$size"); }; + var $options = function () { return null; }; + var $where = function (params, ownerQuery, options) { + var test; + if (isFunction(params)) { + test = params; + } + else if (!process.env.CSP_ENABLED) { + test = new Function("obj", "return " + params); + } + else { + throw new Error("In CSP mode, sift does not support strings in \"$where\" condition"); + } + return new EqualsOperation(function (b) { return test.bind(b)(b); }, ownerQuery, options); + }; + + var defaultOperations = /*#__PURE__*/Object.freeze({ + __proto__: null, + $Size: $Size, + $all: $all, + $and: $and, + $elemMatch: $elemMatch, + $eq: $eq, + $exists: $exists, + $gt: $gt, + $gte: $gte, + $in: $in, + $lt: $lt, + $lte: $lte, + $mod: $mod, + $ne: $ne, + $nin: $nin, + $nor: $nor, + $not: $not, + $options: $options, + $or: $or, + $regex: $regex, + $size: $size, + $type: $type, + $where: $where + }); + + var createDefaultQueryOperation = function (query, ownerQuery, _a) { + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + return createQueryOperation(query, ownerQuery, { + compare: compare, + operations: Object.assign({}, defaultOperations, operations || {}), + }); + }; + var createDefaultQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + var op = createDefaultQueryOperation(query, null, options); + return createOperationTester(op); + }; + + exports.$Size = $Size; + exports.$all = $all; + exports.$and = $and; + exports.$elemMatch = $elemMatch; + exports.$eq = $eq; + exports.$exists = $exists; + exports.$gt = $gt; + exports.$gte = $gte; + exports.$in = $in; + exports.$lt = $lt; + exports.$lte = $lte; + exports.$mod = $mod; + exports.$ne = $ne; + exports.$nin = $nin; + exports.$nor = $nor; + exports.$not = $not; + exports.$options = $options; + exports.$or = $or; + exports.$regex = $regex; + exports.$size = $size; + exports.$type = $type; + exports.$where = $where; + exports.EqualsOperation = EqualsOperation; + exports.createDefaultQueryOperation = createDefaultQueryOperation; + exports.createEqualsOperation = createEqualsOperation; + exports.createOperationTester = createOperationTester; + exports.createQueryOperation = createQueryOperation; + exports.createQueryTester = createQueryTester; + exports.default = createDefaultQueryTester; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); +//# sourceMappingURL=index.js.map diff --git a/backend/node_modules/sift/lib/index.js.map b/backend/node_modules/sift/lib/index.js.map new file mode 100644 index 0000000..ca6a98b --- /dev/null +++ b/backend/node_modules/sift/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sources":["../node_modules/tslib/tslib.es6.js","../src/utils.ts","../src/core.ts","../src/operations.ts","../src/index.ts"],"sourcesContent":["/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise, SuppressedError, Symbol */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\r\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\r\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\r\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\r\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\r\n var _, done = false;\r\n for (var i = decorators.length - 1; i >= 0; i--) {\r\n var context = {};\r\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\r\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\r\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\r\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\r\n if (kind === \"accessor\") {\r\n if (result === void 0) continue;\r\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\r\n if (_ = accept(result.get)) descriptor.get = _;\r\n if (_ = accept(result.set)) descriptor.set = _;\r\n if (_ = accept(result.init)) initializers.unshift(_);\r\n }\r\n else if (_ = accept(result)) {\r\n if (kind === \"field\") initializers.unshift(_);\r\n else descriptor[key] = _;\r\n }\r\n }\r\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\r\n done = true;\r\n};\r\n\r\nexport function __runInitializers(thisArg, initializers, value) {\r\n var useValue = arguments.length > 2;\r\n for (var i = 0; i < initializers.length; i++) {\r\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\r\n }\r\n return useValue ? value : void 0;\r\n};\r\n\r\nexport function __propKey(x) {\r\n return typeof x === \"symbol\" ? x : \"\".concat(x);\r\n};\r\n\r\nexport function __setFunctionName(f, name, prefix) {\r\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\r\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\r\n};\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n var desc = Object.getOwnPropertyDescriptor(m, k);\r\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\r\n desc = { enumerable: true, get: function() { return m[k]; } };\r\n }\r\n Object.defineProperty(o, k2, desc);\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\nexport function __classPrivateFieldIn(state, receiver) {\r\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\r\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\r\n}\r\n\r\nexport function __addDisposableResource(env, value, async) {\r\n if (value !== null && value !== void 0) {\r\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\r\n var dispose;\r\n if (async) {\r\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\r\n dispose = value[Symbol.asyncDispose];\r\n }\r\n if (dispose === void 0) {\r\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\r\n dispose = value[Symbol.dispose];\r\n }\r\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\r\n env.stack.push({ value: value, dispose: dispose, async: async });\r\n }\r\n else if (async) {\r\n env.stack.push({ async: true });\r\n }\r\n return value;\r\n}\r\n\r\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\r\n\r\nexport function __disposeResources(env) {\r\n function fail(e) {\r\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\r\n env.hasError = true;\r\n }\r\n function next() {\r\n while (env.stack.length) {\r\n var rec = env.stack.pop();\r\n try {\r\n var result = rec.dispose && rec.dispose.call(rec.value);\r\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\r\n }\r\n catch (e) {\r\n fail(e);\r\n }\r\n }\r\n if (env.hasError) throw env.error;\r\n }\r\n return next();\r\n}\r\n\r\nexport default {\r\n __extends: __extends,\r\n __assign: __assign,\r\n __rest: __rest,\r\n __decorate: __decorate,\r\n __param: __param,\r\n __metadata: __metadata,\r\n __awaiter: __awaiter,\r\n __generator: __generator,\r\n __createBinding: __createBinding,\r\n __exportStar: __exportStar,\r\n __values: __values,\r\n __read: __read,\r\n __spread: __spread,\r\n __spreadArrays: __spreadArrays,\r\n __spreadArray: __spreadArray,\r\n __await: __await,\r\n __asyncGenerator: __asyncGenerator,\r\n __asyncDelegator: __asyncDelegator,\r\n __asyncValues: __asyncValues,\r\n __makeTemplateObject: __makeTemplateObject,\r\n __importStar: __importStar,\r\n __importDefault: __importDefault,\r\n __classPrivateFieldGet: __classPrivateFieldGet,\r\n __classPrivateFieldSet: __classPrivateFieldSet,\r\n __classPrivateFieldIn: __classPrivateFieldIn,\r\n __addDisposableResource: __addDisposableResource,\r\n __disposeResources: __disposeResources,\r\n};\r\n",null,null,null,null],"names":[],"mappings":"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","x_google_ignoreList":[0]} \ No newline at end of file diff --git a/backend/node_modules/sift/lib/operations.d.ts b/backend/node_modules/sift/lib/operations.d.ts new file mode 100644 index 0000000..7e49f74 --- /dev/null +++ b/backend/node_modules/sift/lib/operations.d.ts @@ -0,0 +1,88 @@ +import { BaseOperation, EqualsOperation, Options, Operation, Query, NamedGroupOperation } from "./core"; +import { Key } from "./utils"; +declare class $Ne extends BaseOperation { + readonly propop = true; + private _test; + init(): void; + reset(): void; + next(item: any): void; +} +declare class $ElemMatch extends BaseOperation> { + readonly propop = true; + private _queryOperation; + init(): void; + reset(): void; + next(item: any): void; +} +declare class $Not extends BaseOperation> { + readonly propop = true; + private _queryOperation; + init(): void; + reset(): void; + next(item: any, key: Key, owner: any, root: boolean): void; +} +export declare class $Size extends BaseOperation { + readonly propop = true; + init(): void; + next(item: any): void; +} +declare class $Or extends BaseOperation { + readonly propop = false; + private _ops; + init(): void; + reset(): void; + next(item: any, key: Key, owner: any): void; +} +declare class $Nor extends $Or { + readonly propop = false; + next(item: any, key: Key, owner: any): void; +} +declare class $In extends BaseOperation { + readonly propop = true; + private _testers; + init(): void; + next(item: any, key: Key, owner: any): void; +} +declare class $Nin extends BaseOperation { + readonly propop = true; + private _in; + constructor(params: any, ownerQuery: any, options: Options, name: string); + next(item: any, key: Key, owner: any, root: boolean): void; + reset(): void; +} +declare class $Exists extends BaseOperation { + readonly propop = true; + next(item: any, key: Key, owner: any, root: boolean, leaf?: boolean): void; +} +declare class $And extends NamedGroupOperation { + readonly propop = false; + constructor(params: Query[], owneryQuery: Query, options: Options, name: string); + next(item: any, key: Key, owner: any, root: boolean): void; +} +declare class $All extends NamedGroupOperation { + readonly propop = true; + constructor(params: Query[], owneryQuery: Query, options: Options, name: string); + next(item: any, key: Key, owner: any, root: boolean): void; +} +export declare const $eq: (params: any, owneryQuery: Query, options: Options) => EqualsOperation; +export declare const $ne: (params: any, owneryQuery: Query, options: Options, name: string) => $Ne; +export declare const $or: (params: Query[], owneryQuery: Query, options: Options, name: string) => $Or; +export declare const $nor: (params: Query[], owneryQuery: Query, options: Options, name: string) => $Nor; +export declare const $elemMatch: (params: any, owneryQuery: Query, options: Options, name: string) => $ElemMatch; +export declare const $nin: (params: any, owneryQuery: Query, options: Options, name: string) => $Nin; +export declare const $in: (params: any, owneryQuery: Query, options: Options, name: string) => $In; +export declare const $lt: (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export declare const $lte: (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export declare const $gt: (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export declare const $gte: (params: any, owneryQuery: any, options: Options, name: string) => Operation; +export declare const $mod: ([mod, equalsValue]: number[], owneryQuery: Query, options: Options) => EqualsOperation<(b: any) => boolean>; +export declare const $exists: (params: boolean, owneryQuery: Query, options: Options, name: string) => $Exists; +export declare const $regex: (pattern: string, owneryQuery: Query, options: Options) => EqualsOperation; +export declare const $not: (params: any, owneryQuery: Query, options: Options, name: string) => $Not; +export declare const $type: (clazz: Function | string, owneryQuery: Query, options: Options) => EqualsOperation<(b: any) => any>; +export declare const $and: (params: Query[], ownerQuery: Query, options: Options, name: string) => $And; +export declare const $all: (params: Query[], ownerQuery: Query, options: Options, name: string) => $All; +export declare const $size: (params: number, ownerQuery: Query, options: Options) => $Size; +export declare const $options: () => any; +export declare const $where: (params: string | Function, ownerQuery: Query, options: Options) => EqualsOperation<(b: any) => any>; +export {}; diff --git a/backend/node_modules/sift/lib/utils.d.ts b/backend/node_modules/sift/lib/utils.d.ts new file mode 100644 index 0000000..4de9e74 --- /dev/null +++ b/backend/node_modules/sift/lib/utils.d.ts @@ -0,0 +1,11 @@ +export type Key = string | number; +export type Comparator = (a: any, b: any) => boolean; +export declare const typeChecker: (type: any) => (value: any) => value is TType; +export declare const comparable: (value: any) => any; +export declare const coercePotentiallyNull: (value: any) => any; +export declare const isArray: (value: any) => value is any[]; +export declare const isObject: (value: any) => value is Object; +export declare const isFunction: (value: any) => value is Function; +export declare const isProperty: (item: any, key: any) => boolean; +export declare const isVanillaObject: (value: any) => boolean; +export declare const equals: (a: any, b: any) => boolean; diff --git a/backend/node_modules/sift/package.json b/backend/node_modules/sift/package.json new file mode 100644 index 0000000..0784287 --- /dev/null +++ b/backend/node_modules/sift/package.json @@ -0,0 +1,61 @@ +{ + "name": "sift", + "description": "MongoDB query filtering in JavaScript", + "version": "17.1.3", + "repository": "crcn/sift.js", + "sideEffects": false, + "author": { + "name": "Craig Condon", + "email": "craig.j.condon@gmail.com" + }, + "license": "MIT", + "engines": {}, + "typings": "./index.d.ts", + "husky": { + "hooks": { + "pre-commit": "pretty-quick --staged" + } + }, + "devDependencies": { + "@rollup/plugin-replace": "^2.3.2", + "@rollup/plugin-typescript": "8.2.1", + "@types/node": "^13.7.0", + "bson": "^6.6.0", + "eval": "^0.1.8", + "husky": "^9.0.11", + "mocha": "10.4.0", + "mongodb": "^3.6.6", + "prettier": "3.2.5", + "pretty-quick": "^4.0.0", + "rimraf": "^5.0.5", + "rollup": "^4.14.2", + "@rollup/plugin-terser": "^0.4.4", + "tslib": "2.6.2", + "typescript": "5.4.5" + }, + "main": "./index.js", + "module": "./es5m/index.js", + "es2015": "./es/index.js", + "scripts": { + "clean": "rimraf lib es5m es", + "prebuild": "npm run clean && npm run build:types", + "build": "rollup -c", + "build:types": "tsc -p tsconfig.json --emitDeclarationOnly --outDir lib", + "test": "npm run test:spec && npm run test:types", + "test:spec": "mocha ./test -R spec", + "test:types": "cd test && tsc types.ts --noEmit", + "prepublishOnly": "npm run build && npm run test" + }, + "files": [ + "es", + "es5m", + "lib", + "src", + "*.d.ts", + "*.js.map", + "index.js", + "sift.csp.min.js", + "sift.min.js", + "MIT-LICENSE.txt" + ] +} diff --git a/backend/node_modules/sift/sift.csp.min.js b/backend/node_modules/sift/sift.csp.min.js new file mode 100644 index 0000000..b80705d --- /dev/null +++ b/backend/node_modules/sift/sift.csp.min.js @@ -0,0 +1,778 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sift = {})); +})(this, (function (exports) { 'use strict'; + + /****************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise, SuppressedError, Symbol */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { + var e = new Error(message); + return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; + }; + + var typeChecker = function (type) { + var typeString = "[object " + type + "]"; + return function (value) { + return getClassName(value) === typeString; + }; + }; + var getClassName = function (value) { return Object.prototype.toString.call(value); }; + var comparable = function (value) { + if (value instanceof Date) { + return value.getTime(); + } + else if (isArray(value)) { + return value.map(comparable); + } + else if (value && typeof value.toJSON === "function") { + return value.toJSON(); + } + return value; + }; + var coercePotentiallyNull = function (value) { + return value == null ? null : value; + }; + var isArray = typeChecker("Array"); + var isObject = typeChecker("Object"); + var isFunction = typeChecker("Function"); + var isProperty = function (item, key) { + return item.hasOwnProperty(key) && !isFunction(item[key]); + }; + var isVanillaObject = function (value) { + return (value && + (value.constructor === Object || + value.constructor === Array || + value.constructor.toString() === "function Object() { [native code] }" || + value.constructor.toString() === "function Array() { [native code] }") && + !value.toJSON); + }; + var equals = function (a, b) { + if (a == null && a == b) { + return true; + } + if (a === b) { + return true; + } + if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) { + return false; + } + if (isArray(a)) { + if (a.length !== b.length) { + return false; + } + for (var i = 0, length_1 = a.length; i < length_1; i++) { + if (!equals(a[i], b[i])) + return false; + } + return true; + } + else if (isObject(a)) { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (var key in a) { + if (!equals(a[key], b[key])) + return false; + } + return true; + } + return false; + }; + + /** + * Walks through each value given the context - used for nested operations. E.g: + * { "person.address": { $eq: "blarg" }} + */ + var walkKeyPathValues = function (item, keyPath, next, depth, key, owner) { + var currentKey = keyPath[depth]; + // if array, then try matching. Might fall through for cases like: + // { $eq: [1, 2, 3] }, [ 1, 2, 3 ]. + if (isArray(item) && + isNaN(Number(currentKey)) && + !isProperty(item, currentKey)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // if FALSE is returned, then terminate walker. For operations, this simply + // means that the search critera was met. + if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) { + return false; + } + } + } + if (depth === keyPath.length || item == null) { + return next(item, key, owner, depth === 0, depth === keyPath.length); + } + return walkKeyPathValues(item[currentKey], keyPath, next, depth + 1, currentKey, item); + }; + var BaseOperation = /** @class */ (function () { + function BaseOperation(params, owneryQuery, options, name) { + this.params = params; + this.owneryQuery = owneryQuery; + this.options = options; + this.name = name; + this.init(); + } + BaseOperation.prototype.init = function () { }; + BaseOperation.prototype.reset = function () { + this.done = false; + this.keep = false; + }; + return BaseOperation; + }()); + var GroupOperation = /** @class */ (function (_super) { + __extends(GroupOperation, _super); + function GroupOperation(params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options) || this; + _this.children = children; + return _this; + } + /** + */ + GroupOperation.prototype.reset = function () { + this.keep = false; + this.done = false; + for (var i = 0, length_2 = this.children.length; i < length_2; i++) { + this.children[i].reset(); + } + }; + /** + */ + GroupOperation.prototype.childrenNext = function (item, key, owner, root, leaf) { + var done = true; + var keep = true; + for (var i = 0, length_3 = this.children.length; i < length_3; i++) { + var childOperation = this.children[i]; + if (!childOperation.done) { + childOperation.next(item, key, owner, root, leaf); + } + if (!childOperation.keep) { + keep = false; + } + if (childOperation.done) { + if (!childOperation.keep) { + break; + } + } + else { + done = false; + } + } + this.done = done; + this.keep = keep; + }; + return GroupOperation; + }(BaseOperation)); + var NamedGroupOperation = /** @class */ (function (_super) { + __extends(NamedGroupOperation, _super); + function NamedGroupOperation(params, owneryQuery, options, children, name) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.name = name; + return _this; + } + return NamedGroupOperation; + }(GroupOperation)); + var QueryOperation = /** @class */ (function (_super) { + __extends(QueryOperation, _super); + function QueryOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + /** + */ + QueryOperation.prototype.next = function (item, key, parent, root) { + this.childrenNext(item, key, parent, root); + }; + return QueryOperation; + }(GroupOperation)); + var NestedOperation = /** @class */ (function (_super) { + __extends(NestedOperation, _super); + function NestedOperation(keyPath, params, owneryQuery, options, children) { + var _this = _super.call(this, params, owneryQuery, options, children) || this; + _this.keyPath = keyPath; + _this.propop = true; + /** + */ + _this._nextNestedValue = function (value, key, owner, root, leaf) { + _this.childrenNext(value, key, owner, root, leaf); + return !_this.done; + }; + return _this; + } + /** + */ + NestedOperation.prototype.next = function (item, key, parent) { + walkKeyPathValues(item, this.keyPath, this._nextNestedValue, 0, key, parent); + }; + return NestedOperation; + }(GroupOperation)); + var createTester = function (a, compare) { + if (a instanceof Function) { + return a; + } + if (a instanceof RegExp) { + return function (b) { + var result = typeof b === "string" && a.test(b); + a.lastIndex = 0; + return result; + }; + } + var comparableA = comparable(a); + return function (b) { return compare(comparableA, comparable(b)); }; + }; + var EqualsOperation = /** @class */ (function (_super) { + __extends(EqualsOperation, _super); + function EqualsOperation() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + EqualsOperation.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + EqualsOperation.prototype.next = function (item, key, parent) { + if (!Array.isArray(parent) || parent.hasOwnProperty(key)) { + if (this._test(item, key, parent)) { + this.done = true; + this.keep = true; + } + } + }; + return EqualsOperation; + }(BaseOperation)); + var createEqualsOperation = function (params, owneryQuery, options) { return new EqualsOperation(params, owneryQuery, options); }; + var numericalOperationCreator = function (createNumericalOperation) { + return function (params, owneryQuery, options, name) { + return createNumericalOperation(params, owneryQuery, options, name); + }; + }; + var numericalOperation = function (createTester) { + return numericalOperationCreator(function (params, owneryQuery, options, name) { + var typeofParams = typeof comparable(params); + var test = createTester(params); + return new EqualsOperation(function (b) { + var actualValue = coercePotentiallyNull(b); + return (typeof comparable(actualValue) === typeofParams && test(actualValue)); + }, owneryQuery, options, name); + }); + }; + var createNamedOperation = function (name, params, parentQuery, options) { + var operationCreator = options.operations[name]; + if (!operationCreator) { + throwUnsupportedOperation(name); + } + return operationCreator(params, parentQuery, options, name); + }; + var throwUnsupportedOperation = function (name) { + throw new Error("Unsupported operation: ".concat(name)); + }; + var containsOperation = function (query, options) { + for (var key in query) { + if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") + return true; + } + return false; + }; + var createNestedOperation = function (keyPath, nestedQuery, parentKey, owneryQuery, options) { + if (containsOperation(nestedQuery, options)) { + var _a = createQueryOperations(nestedQuery, parentKey, options), selfOperations = _a[0], nestedOperations = _a[1]; + if (nestedOperations.length) { + throw new Error("Property queries must contain only operations, or exact objects."); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, selfOperations); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [ + new EqualsOperation(nestedQuery, owneryQuery, options), + ]); + }; + var createQueryOperation = function (query, owneryQuery, _a) { + if (owneryQuery === void 0) { owneryQuery = null; } + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + var options = { + compare: compare || equals, + operations: Object.assign({}, operations || {}), + }; + var _c = createQueryOperations(query, null, options), selfOperations = _c[0], nestedOperations = _c[1]; + var ops = []; + if (selfOperations.length) { + ops.push(new NestedOperation([], query, owneryQuery, options, selfOperations)); + } + ops.push.apply(ops, nestedOperations); + if (ops.length === 1) { + return ops[0]; + } + return new QueryOperation(query, owneryQuery, options, ops); + }; + var createQueryOperations = function (query, parentKey, options) { + var selfOperations = []; + var nestedOperations = []; + if (!isVanillaObject(query)) { + selfOperations.push(new EqualsOperation(query, query, options)); + return [selfOperations, nestedOperations]; + } + for (var key in query) { + if (options.operations.hasOwnProperty(key)) { + var op = createNamedOperation(key, query[key], query, options); + if (op) { + if (!op.propop && parentKey && !options.operations[parentKey]) { + throw new Error("Malformed query. ".concat(key, " cannot be matched against property.")); + } + } + // probably just a flag for another operation (like $options) + if (op != null) { + selfOperations.push(op); + } + } + else if (key.charAt(0) === "$") { + throwUnsupportedOperation(key); + } + else { + nestedOperations.push(createNestedOperation(key.split("."), query[key], key, query, options)); + } + } + return [selfOperations, nestedOperations]; + }; + var createOperationTester = function (operation) { + return function (item, key, owner) { + operation.reset(); + operation.next(item, key, owner); + return operation.keep; + }; + }; + var createQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + return createOperationTester(createQueryOperation(query, null, options)); + }; + + var $Ne = /** @class */ (function (_super) { + __extends($Ne, _super); + function $Ne() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Ne.prototype.init = function () { + this._test = createTester(this.params, this.options.compare); + }; + $Ne.prototype.reset = function () { + _super.prototype.reset.call(this); + this.keep = true; + }; + $Ne.prototype.next = function (item) { + if (this._test(item)) { + this.done = true; + this.keep = false; + } + }; + return $Ne; + }(BaseOperation)); + // https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ + var $ElemMatch = /** @class */ (function (_super) { + __extends($ElemMatch, _super); + function $ElemMatch() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $ElemMatch.prototype.init = function () { + if (!this.params || typeof this.params !== "object") { + throw new Error("Malformed query. $elemMatch must by an object."); + } + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $ElemMatch.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $ElemMatch.prototype.next = function (item) { + if (isArray(item)) { + for (var i = 0, length_1 = item.length; i < length_1; i++) { + // reset query operation since item being tested needs to pass _all_ query + // operations for it to be a success + this._queryOperation.reset(); + var child = item[i]; + this._queryOperation.next(child, i, item, false); + this.keep = this.keep || this._queryOperation.keep; + } + this.done = true; + } + else { + this.done = false; + this.keep = false; + } + }; + return $ElemMatch; + }(BaseOperation)); + var $Not = /** @class */ (function (_super) { + __extends($Not, _super); + function $Not() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Not.prototype.init = function () { + this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options); + }; + $Not.prototype.reset = function () { + _super.prototype.reset.call(this); + this._queryOperation.reset(); + }; + $Not.prototype.next = function (item, key, owner, root) { + this._queryOperation.next(item, key, owner, root); + this.done = this._queryOperation.done; + this.keep = !this._queryOperation.keep; + }; + return $Not; + }(BaseOperation)); + var $Size = /** @class */ (function (_super) { + __extends($Size, _super); + function $Size() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Size.prototype.init = function () { }; + $Size.prototype.next = function (item) { + if (isArray(item) && item.length === this.params) { + this.done = true; + this.keep = true; + } + // if (parent && parent.length === this.params) { + // this.done = true; + // this.keep = true; + // } + }; + return $Size; + }(BaseOperation)); + var assertGroupNotEmpty = function (values) { + if (values.length === 0) { + throw new Error("$and/$or/$nor must be a nonempty array"); + } + }; + var $Or = /** @class */ (function (_super) { + __extends($Or, _super); + function $Or() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Or.prototype.init = function () { + var _this = this; + assertGroupNotEmpty(this.params); + this._ops = this.params.map(function (op) { + return createQueryOperation(op, null, _this.options); + }); + }; + $Or.prototype.reset = function () { + this.done = false; + this.keep = false; + for (var i = 0, length_2 = this._ops.length; i < length_2; i++) { + this._ops[i].reset(); + } + }; + $Or.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_3 = this._ops.length; i < length_3; i++) { + var op = this._ops[i]; + op.next(item, key, owner); + if (op.keep) { + done = true; + success = op.keep; + break; + } + } + this.keep = success; + this.done = done; + }; + return $Or; + }(BaseOperation)); + var $Nor = /** @class */ (function (_super) { + __extends($Nor, _super); + function $Nor() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = false; + return _this; + } + $Nor.prototype.next = function (item, key, owner) { + _super.prototype.next.call(this, item, key, owner); + this.keep = !this.keep; + }; + return $Nor; + }($Or)); + var $In = /** @class */ (function (_super) { + __extends($In, _super); + function $In() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $In.prototype.init = function () { + var _this = this; + var params = Array.isArray(this.params) ? this.params : [this.params]; + this._testers = params.map(function (value) { + if (containsOperation(value, _this.options)) { + throw new Error("cannot nest $ under ".concat(_this.name.toLowerCase())); + } + return createTester(value, _this.options.compare); + }); + }; + $In.prototype.next = function (item, key, owner) { + var done = false; + var success = false; + for (var i = 0, length_4 = this._testers.length; i < length_4; i++) { + var test = this._testers[i]; + if (test(item)) { + done = true; + success = true; + break; + } + } + this.keep = success; + this.done = done; + }; + return $In; + }(BaseOperation)); + var $Nin = /** @class */ (function (_super) { + __extends($Nin, _super); + function $Nin(params, ownerQuery, options, name) { + var _this = _super.call(this, params, ownerQuery, options, name) || this; + _this.propop = true; + _this._in = new $In(params, ownerQuery, options, name); + return _this; + } + $Nin.prototype.next = function (item, key, owner, root) { + this._in.next(item, key, owner); + if (isArray(owner) && !root) { + if (this._in.keep) { + this.keep = false; + this.done = true; + } + else if (key == owner.length - 1) { + this.keep = true; + this.done = true; + } + } + else { + this.keep = !this._in.keep; + this.done = true; + } + }; + $Nin.prototype.reset = function () { + _super.prototype.reset.call(this); + this._in.reset(); + }; + return $Nin; + }(BaseOperation)); + var $Exists = /** @class */ (function (_super) { + __extends($Exists, _super); + function $Exists() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.propop = true; + return _this; + } + $Exists.prototype.next = function (item, key, owner, root, leaf) { + if (!leaf) { + this.done = true; + this.keep = !this.params; + } + else if (owner.hasOwnProperty(key) === this.params) { + this.done = true; + this.keep = true; + } + }; + return $Exists; + }(BaseOperation)); + var $And = /** @class */ (function (_super) { + __extends($And, _super); + function $And(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = false; + assertGroupNotEmpty(params); + return _this; + } + $And.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $And; + }(NamedGroupOperation)); + var $All = /** @class */ (function (_super) { + __extends($All, _super); + function $All(params, owneryQuery, options, name) { + var _this = _super.call(this, params, owneryQuery, options, params.map(function (query) { return createQueryOperation(query, owneryQuery, options); }), name) || this; + _this.propop = true; + return _this; + } + $All.prototype.next = function (item, key, owner, root) { + this.childrenNext(item, key, owner, root); + }; + return $All; + }(NamedGroupOperation)); + var $eq = function (params, owneryQuery, options) { + return new EqualsOperation(params, owneryQuery, options); + }; + var $ne = function (params, owneryQuery, options, name) { return new $Ne(params, owneryQuery, options, name); }; + var $or = function (params, owneryQuery, options, name) { return new $Or(params, owneryQuery, options, name); }; + var $nor = function (params, owneryQuery, options, name) { return new $Nor(params, owneryQuery, options, name); }; + var $elemMatch = function (params, owneryQuery, options, name) { return new $ElemMatch(params, owneryQuery, options, name); }; + var $nin = function (params, owneryQuery, options, name) { return new $Nin(params, owneryQuery, options, name); }; + var $in = function (params, owneryQuery, options, name) { + return new $In(params, owneryQuery, options, name); + }; + var $lt = numericalOperation(function (params) { return function (b) { + return b != null && b < params; + }; }); + var $lte = numericalOperation(function (params) { return function (b) { + return b === params || b <= params; + }; }); + var $gt = numericalOperation(function (params) { return function (b) { + return b != null && b > params; + }; }); + var $gte = numericalOperation(function (params) { return function (b) { + return b === params || b >= params; + }; }); + var $mod = function (_a, owneryQuery, options) { + var mod = _a[0], equalsValue = _a[1]; + return new EqualsOperation(function (b) { return comparable(b) % mod === equalsValue; }, owneryQuery, options); + }; + var $exists = function (params, owneryQuery, options, name) { return new $Exists(params, owneryQuery, options, name); }; + var $regex = function (pattern, owneryQuery, options) { + return new EqualsOperation(new RegExp(pattern, owneryQuery.$options), owneryQuery, options); + }; + var $not = function (params, owneryQuery, options, name) { return new $Not(params, owneryQuery, options, name); }; + var typeAliases = { + number: function (v) { return typeof v === "number"; }, + string: function (v) { return typeof v === "string"; }, + bool: function (v) { return typeof v === "boolean"; }, + array: function (v) { return Array.isArray(v); }, + null: function (v) { return v === null; }, + timestamp: function (v) { return v instanceof Date; }, + }; + var $type = function (clazz, owneryQuery, options) { + return new EqualsOperation(function (b) { + if (typeof clazz === "string") { + if (!typeAliases[clazz]) { + throw new Error("Type alias does not exist"); + } + return typeAliases[clazz](b); + } + return b != null ? b instanceof clazz || b.constructor === clazz : false; + }, owneryQuery, options); + }; + var $and = function (params, ownerQuery, options, name) { return new $And(params, ownerQuery, options, name); }; + var $all = function (params, ownerQuery, options, name) { return new $All(params, ownerQuery, options, name); }; + var $size = function (params, ownerQuery, options) { return new $Size(params, ownerQuery, options, "$size"); }; + var $options = function () { return null; }; + var $where = function (params, ownerQuery, options) { + var test; + if (isFunction(params)) { + test = params; + } + else { + throw new Error("In CSP mode, sift does not support strings in \"$where\" condition"); + } + return new EqualsOperation(function (b) { return test.bind(b)(b); }, ownerQuery, options); + }; + + var defaultOperations = /*#__PURE__*/Object.freeze({ + __proto__: null, + $Size: $Size, + $all: $all, + $and: $and, + $elemMatch: $elemMatch, + $eq: $eq, + $exists: $exists, + $gt: $gt, + $gte: $gte, + $in: $in, + $lt: $lt, + $lte: $lte, + $mod: $mod, + $ne: $ne, + $nin: $nin, + $nor: $nor, + $not: $not, + $options: $options, + $or: $or, + $regex: $regex, + $size: $size, + $type: $type, + $where: $where + }); + + var createDefaultQueryOperation = function (query, ownerQuery, _a) { + var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations; + return createQueryOperation(query, ownerQuery, { + compare: compare, + operations: Object.assign({}, defaultOperations, operations || {}), + }); + }; + var createDefaultQueryTester = function (query, options) { + if (options === void 0) { options = {}; } + var op = createDefaultQueryOperation(query, null, options); + return createOperationTester(op); + }; + + exports.$Size = $Size; + exports.$all = $all; + exports.$and = $and; + exports.$elemMatch = $elemMatch; + exports.$eq = $eq; + exports.$exists = $exists; + exports.$gt = $gt; + exports.$gte = $gte; + exports.$in = $in; + exports.$lt = $lt; + exports.$lte = $lte; + exports.$mod = $mod; + exports.$ne = $ne; + exports.$nin = $nin; + exports.$nor = $nor; + exports.$not = $not; + exports.$options = $options; + exports.$or = $or; + exports.$regex = $regex; + exports.$size = $size; + exports.$type = $type; + exports.$where = $where; + exports.EqualsOperation = EqualsOperation; + exports.createDefaultQueryOperation = createDefaultQueryOperation; + exports.createEqualsOperation = createEqualsOperation; + exports.createOperationTester = createOperationTester; + exports.createQueryOperation = createQueryOperation; + exports.createQueryTester = createQueryTester; + exports.default = createDefaultQueryTester; + + Object.defineProperty(exports, '__esModule', { value: true }); + +})); +//# sourceMappingURL=sift.csp.min.js.map diff --git a/backend/node_modules/sift/sift.csp.min.js.map b/backend/node_modules/sift/sift.csp.min.js.map new file mode 100644 index 0000000..681fb85 --- /dev/null +++ b/backend/node_modules/sift/sift.csp.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"sift.csp.min.js","sources":["node_modules/tslib/tslib.es6.js","src/utils.ts","src/core.ts","src/operations.ts","src/index.ts"],"sourcesContent":["/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise, SuppressedError, Symbol */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\r\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\r\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\r\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\r\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\r\n var _, done = false;\r\n for (var i = decorators.length - 1; i >= 0; i--) {\r\n var context = {};\r\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\r\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\r\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\r\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\r\n if (kind === \"accessor\") {\r\n if (result === void 0) continue;\r\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\r\n if (_ = accept(result.get)) descriptor.get = _;\r\n if (_ = accept(result.set)) descriptor.set = _;\r\n if (_ = accept(result.init)) initializers.unshift(_);\r\n }\r\n else if (_ = accept(result)) {\r\n if (kind === \"field\") initializers.unshift(_);\r\n else descriptor[key] = _;\r\n }\r\n }\r\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\r\n done = true;\r\n};\r\n\r\nexport function __runInitializers(thisArg, initializers, value) {\r\n var useValue = arguments.length > 2;\r\n for (var i = 0; i < initializers.length; i++) {\r\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\r\n }\r\n return useValue ? value : void 0;\r\n};\r\n\r\nexport function __propKey(x) {\r\n return typeof x === \"symbol\" ? x : \"\".concat(x);\r\n};\r\n\r\nexport function __setFunctionName(f, name, prefix) {\r\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\r\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\r\n};\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n var desc = Object.getOwnPropertyDescriptor(m, k);\r\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\r\n desc = { enumerable: true, get: function() { return m[k]; } };\r\n }\r\n Object.defineProperty(o, k2, desc);\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\nexport function __classPrivateFieldIn(state, receiver) {\r\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\r\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\r\n}\r\n\r\nexport function __addDisposableResource(env, value, async) {\r\n if (value !== null && value !== void 0) {\r\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\r\n var dispose;\r\n if (async) {\r\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\r\n dispose = value[Symbol.asyncDispose];\r\n }\r\n if (dispose === void 0) {\r\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\r\n dispose = value[Symbol.dispose];\r\n }\r\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\r\n env.stack.push({ value: value, dispose: dispose, async: async });\r\n }\r\n else if (async) {\r\n env.stack.push({ async: true });\r\n }\r\n return value;\r\n}\r\n\r\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\r\n\r\nexport function __disposeResources(env) {\r\n function fail(e) {\r\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\r\n env.hasError = true;\r\n }\r\n function next() {\r\n while (env.stack.length) {\r\n var rec = env.stack.pop();\r\n try {\r\n var result = rec.dispose && rec.dispose.call(rec.value);\r\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\r\n }\r\n catch (e) {\r\n fail(e);\r\n }\r\n }\r\n if (env.hasError) throw env.error;\r\n }\r\n return next();\r\n}\r\n\r\nexport default {\r\n __extends: __extends,\r\n __assign: __assign,\r\n __rest: __rest,\r\n __decorate: __decorate,\r\n __param: __param,\r\n __metadata: __metadata,\r\n __awaiter: __awaiter,\r\n __generator: __generator,\r\n __createBinding: __createBinding,\r\n __exportStar: __exportStar,\r\n __values: __values,\r\n __read: __read,\r\n __spread: __spread,\r\n __spreadArrays: __spreadArrays,\r\n __spreadArray: __spreadArray,\r\n __await: __await,\r\n __asyncGenerator: __asyncGenerator,\r\n __asyncDelegator: __asyncDelegator,\r\n __asyncValues: __asyncValues,\r\n __makeTemplateObject: __makeTemplateObject,\r\n __importStar: __importStar,\r\n __importDefault: __importDefault,\r\n __classPrivateFieldGet: __classPrivateFieldGet,\r\n __classPrivateFieldSet: __classPrivateFieldSet,\r\n __classPrivateFieldIn: __classPrivateFieldIn,\r\n __addDisposableResource: __addDisposableResource,\r\n __disposeResources: __disposeResources,\r\n};\r\n",null,null,null,null],"names":[],"mappings":"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","x_google_ignoreList":[0]} \ No newline at end of file diff --git a/backend/node_modules/sift/sift.min.js b/backend/node_modules/sift/sift.min.js new file mode 100644 index 0000000..035d448 --- /dev/null +++ b/backend/node_modules/sift/sift.min.js @@ -0,0 +1,2 @@ +!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).sift={})}(this,(function(n){"use strict";var t=function(n,r){return t=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(n,t){n.__proto__=t}||function(n,t){for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(n[r]=t[r])},t(n,r)};function r(n,r){if("function"!=typeof r&&null!==r)throw new TypeError("Class extends value "+String(r)+" is not a constructor or null");function i(){this.constructor=n}t(n,r),n.prototype=null===r?Object.create(r):(i.prototype=r.prototype,new i)}"function"==typeof SuppressedError&&SuppressedError;var i=function(n){var t="[object "+n+"]";return function(n){return u(n)===t}},u=function(n){return Object.prototype.toString.call(n)},e=function(n){return n instanceof Date?n.getTime():o(n)?n.map(e):n&&"function"==typeof n.toJSON?n.toJSON():n},o=i("Array"),f=i("Object"),s=i("Function"),c=function(n,t){if(null==n&&n==t)return!0;if(n===t)return!0;if(Object.prototype.toString.call(n)!==Object.prototype.toString.call(t))return!1;if(o(n)){if(n.length!==t.length)return!1;for(var r=0,i=n.length;rn}})),Q=d((function(n){return function(t){return t===n||t>=n}})),V=function(n,t,r){var i=n[0],u=n[1];return new y((function(n){return e(n)%i===u}),t,r)},W=function(n,t,r,i){return new N(n,t,r,i)},X=function(n,t,r){return new y(new RegExp(n,t.$options),t,r)},Y=function(n,t,r,i){return new M(n,t,r,i)},Z={number:function(n){return"number"==typeof n},string:function(n){return"string"==typeof n},bool:function(n){return"boolean"==typeof n},array:function(n){return Array.isArray(n)},null:function(n){return null===n},timestamp:function(n){return n instanceof Date}},nn=function(n,t,r){return new y((function(t){if("string"==typeof n){if(!Z[n])throw new Error("Type alias does not exist");return Z[n](t)}return null!=t&&(t instanceof n||t.constructor===n)}),t,r)},tn=function(n,t,r,i){return new C(n,t,r,i)},rn=function(n,t,r,i){return new D(n,t,r,i)},un=function(n,t,r){return new S(n,t,r,"$size")},en=function(){return null},on=function(n,t,r){var i;if(s(n))i=n;else{if(process.env.CSP_ENABLED)throw new Error('In CSP mode, sift does not support strings in "$where" condition');i=new Function("obj","return "+n)}return new y((function(n){return i.bind(n)(n)}),t,r)},fn=Object.freeze({__proto__:null,$Size:S,$all:rn,$and:tn,$elemMatch:B,$eq:P,$exists:W,$gt:L,$gte:Q,$in:H,$lt:J,$lte:K,$mod:V,$ne:R,$nin:G,$nor:U,$not:Y,$options:en,$or:I,$regex:X,$size:un,$type:nn,$where:on}),sn=function(n,t,r){var i=void 0===r?{}:r,u=i.compare,e=i.operations;return g(n,t,{compare:u,operations:Object.assign({},fn,e||{})})};n.$Size=S,n.$all=rn,n.$and=tn,n.$elemMatch=B,n.$eq=P,n.$exists=W,n.$gt=L,n.$gte=Q,n.$in=H,n.$lt=J,n.$lte=K,n.$mod=V,n.$ne=R,n.$nin=G,n.$nor=U,n.$not=Y,n.$options=en,n.$or=I,n.$regex=X,n.$size=un,n.$type=nn,n.$where=on,n.EqualsOperation=y,n.createDefaultQueryOperation=sn,n.createEqualsOperation=function(n,t,r){return new y(n,t,r)},n.createOperationTester=x,n.createQueryOperation=g,n.createQueryTester=function(n,t){return void 0===t&&(t={}),x(g(n,null,t))},n.default=function(n,t){void 0===t&&(t={});var r=sn(n,null,t);return x(r)},Object.defineProperty(n,"v",{value:!0})})); +//# sourceMappingURL=sift.min.js.map diff --git a/backend/node_modules/sift/sift.min.js.map b/backend/node_modules/sift/sift.min.js.map new file mode 100644 index 0000000..fd72537 --- /dev/null +++ b/backend/node_modules/sift/sift.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"sift.min.js","sources":["node_modules/tslib/tslib.es6.js","src/utils.ts","src/core.ts","src/operations.ts","src/index.ts"],"sourcesContent":["/******************************************************************************\r\nCopyright (c) Microsoft Corporation.\r\n\r\nPermission to use, copy, modify, and/or distribute this software for any\r\npurpose with or without fee is hereby granted.\r\n\r\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\r\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\r\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\r\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\r\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR\r\nOTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\r\nPERFORMANCE OF THIS SOFTWARE.\r\n***************************************************************************** */\r\n/* global Reflect, Promise, SuppressedError, Symbol */\r\n\r\nvar extendStatics = function(d, b) {\r\n extendStatics = Object.setPrototypeOf ||\r\n ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||\r\n function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };\r\n return extendStatics(d, b);\r\n};\r\n\r\nexport function __extends(d, b) {\r\n if (typeof b !== \"function\" && b !== null)\r\n throw new TypeError(\"Class extends value \" + String(b) + \" is not a constructor or null\");\r\n extendStatics(d, b);\r\n function __() { this.constructor = d; }\r\n d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());\r\n}\r\n\r\nexport var __assign = function() {\r\n __assign = Object.assign || function __assign(t) {\r\n for (var s, i = 1, n = arguments.length; i < n; i++) {\r\n s = arguments[i];\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];\r\n }\r\n return t;\r\n }\r\n return __assign.apply(this, arguments);\r\n}\r\n\r\nexport function __rest(s, e) {\r\n var t = {};\r\n for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)\r\n t[p] = s[p];\r\n if (s != null && typeof Object.getOwnPropertySymbols === \"function\")\r\n for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {\r\n if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))\r\n t[p[i]] = s[p[i]];\r\n }\r\n return t;\r\n}\r\n\r\nexport function __decorate(decorators, target, key, desc) {\r\n var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;\r\n if (typeof Reflect === \"object\" && typeof Reflect.decorate === \"function\") r = Reflect.decorate(decorators, target, key, desc);\r\n else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;\r\n return c > 3 && r && Object.defineProperty(target, key, r), r;\r\n}\r\n\r\nexport function __param(paramIndex, decorator) {\r\n return function (target, key) { decorator(target, key, paramIndex); }\r\n}\r\n\r\nexport function __esDecorate(ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {\r\n function accept(f) { if (f !== void 0 && typeof f !== \"function\") throw new TypeError(\"Function expected\"); return f; }\r\n var kind = contextIn.kind, key = kind === \"getter\" ? \"get\" : kind === \"setter\" ? \"set\" : \"value\";\r\n var target = !descriptorIn && ctor ? contextIn[\"static\"] ? ctor : ctor.prototype : null;\r\n var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});\r\n var _, done = false;\r\n for (var i = decorators.length - 1; i >= 0; i--) {\r\n var context = {};\r\n for (var p in contextIn) context[p] = p === \"access\" ? {} : contextIn[p];\r\n for (var p in contextIn.access) context.access[p] = contextIn.access[p];\r\n context.addInitializer = function (f) { if (done) throw new TypeError(\"Cannot add initializers after decoration has completed\"); extraInitializers.push(accept(f || null)); };\r\n var result = (0, decorators[i])(kind === \"accessor\" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);\r\n if (kind === \"accessor\") {\r\n if (result === void 0) continue;\r\n if (result === null || typeof result !== \"object\") throw new TypeError(\"Object expected\");\r\n if (_ = accept(result.get)) descriptor.get = _;\r\n if (_ = accept(result.set)) descriptor.set = _;\r\n if (_ = accept(result.init)) initializers.unshift(_);\r\n }\r\n else if (_ = accept(result)) {\r\n if (kind === \"field\") initializers.unshift(_);\r\n else descriptor[key] = _;\r\n }\r\n }\r\n if (target) Object.defineProperty(target, contextIn.name, descriptor);\r\n done = true;\r\n};\r\n\r\nexport function __runInitializers(thisArg, initializers, value) {\r\n var useValue = arguments.length > 2;\r\n for (var i = 0; i < initializers.length; i++) {\r\n value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);\r\n }\r\n return useValue ? value : void 0;\r\n};\r\n\r\nexport function __propKey(x) {\r\n return typeof x === \"symbol\" ? x : \"\".concat(x);\r\n};\r\n\r\nexport function __setFunctionName(f, name, prefix) {\r\n if (typeof name === \"symbol\") name = name.description ? \"[\".concat(name.description, \"]\") : \"\";\r\n return Object.defineProperty(f, \"name\", { configurable: true, value: prefix ? \"\".concat(prefix, \" \", name) : name });\r\n};\r\n\r\nexport function __metadata(metadataKey, metadataValue) {\r\n if (typeof Reflect === \"object\" && typeof Reflect.metadata === \"function\") return Reflect.metadata(metadataKey, metadataValue);\r\n}\r\n\r\nexport function __awaiter(thisArg, _arguments, P, generator) {\r\n function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }\r\n return new (P || (P = Promise))(function (resolve, reject) {\r\n function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }\r\n function rejected(value) { try { step(generator[\"throw\"](value)); } catch (e) { reject(e); } }\r\n function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }\r\n step((generator = generator.apply(thisArg, _arguments || [])).next());\r\n });\r\n}\r\n\r\nexport function __generator(thisArg, body) {\r\n var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;\r\n return g = { next: verb(0), \"throw\": verb(1), \"return\": verb(2) }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() { return this; }), g;\r\n function verb(n) { return function (v) { return step([n, v]); }; }\r\n function step(op) {\r\n if (f) throw new TypeError(\"Generator is already executing.\");\r\n while (g && (g = 0, op[0] && (_ = 0)), _) try {\r\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\r\n if (y = 0, t) op = [op[0] & 2, t.value];\r\n switch (op[0]) {\r\n case 0: case 1: t = op; break;\r\n case 4: _.label++; return { value: op[1], done: false };\r\n case 5: _.label++; y = op[1]; op = [0]; continue;\r\n case 7: op = _.ops.pop(); _.trys.pop(); continue;\r\n default:\r\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }\r\n if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }\r\n if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }\r\n if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }\r\n if (t[2]) _.ops.pop();\r\n _.trys.pop(); continue;\r\n }\r\n op = body.call(thisArg, _);\r\n } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }\r\n if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };\r\n }\r\n}\r\n\r\nexport var __createBinding = Object.create ? (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n var desc = Object.getOwnPropertyDescriptor(m, k);\r\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\r\n desc = { enumerable: true, get: function() { return m[k]; } };\r\n }\r\n Object.defineProperty(o, k2, desc);\r\n}) : (function(o, m, k, k2) {\r\n if (k2 === undefined) k2 = k;\r\n o[k2] = m[k];\r\n});\r\n\r\nexport function __exportStar(m, o) {\r\n for (var p in m) if (p !== \"default\" && !Object.prototype.hasOwnProperty.call(o, p)) __createBinding(o, m, p);\r\n}\r\n\r\nexport function __values(o) {\r\n var s = typeof Symbol === \"function\" && Symbol.iterator, m = s && o[s], i = 0;\r\n if (m) return m.call(o);\r\n if (o && typeof o.length === \"number\") return {\r\n next: function () {\r\n if (o && i >= o.length) o = void 0;\r\n return { value: o && o[i++], done: !o };\r\n }\r\n };\r\n throw new TypeError(s ? \"Object is not iterable.\" : \"Symbol.iterator is not defined.\");\r\n}\r\n\r\nexport function __read(o, n) {\r\n var m = typeof Symbol === \"function\" && o[Symbol.iterator];\r\n if (!m) return o;\r\n var i = m.call(o), r, ar = [], e;\r\n try {\r\n while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);\r\n }\r\n catch (error) { e = { error: error }; }\r\n finally {\r\n try {\r\n if (r && !r.done && (m = i[\"return\"])) m.call(i);\r\n }\r\n finally { if (e) throw e.error; }\r\n }\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spread() {\r\n for (var ar = [], i = 0; i < arguments.length; i++)\r\n ar = ar.concat(__read(arguments[i]));\r\n return ar;\r\n}\r\n\r\n/** @deprecated */\r\nexport function __spreadArrays() {\r\n for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;\r\n for (var r = Array(s), k = 0, i = 0; i < il; i++)\r\n for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)\r\n r[k] = a[j];\r\n return r;\r\n}\r\n\r\nexport function __spreadArray(to, from, pack) {\r\n if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {\r\n if (ar || !(i in from)) {\r\n if (!ar) ar = Array.prototype.slice.call(from, 0, i);\r\n ar[i] = from[i];\r\n }\r\n }\r\n return to.concat(ar || Array.prototype.slice.call(from));\r\n}\r\n\r\nexport function __await(v) {\r\n return this instanceof __await ? (this.v = v, this) : new __await(v);\r\n}\r\n\r\nexport function __asyncGenerator(thisArg, _arguments, generator) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var g = generator.apply(thisArg, _arguments || []), i, q = [];\r\n return i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i;\r\n function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }\r\n function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }\r\n function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }\r\n function fulfill(value) { resume(\"next\", value); }\r\n function reject(value) { resume(\"throw\", value); }\r\n function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }\r\n}\r\n\r\nexport function __asyncDelegator(o) {\r\n var i, p;\r\n return i = {}, verb(\"next\"), verb(\"throw\", function (e) { throw e; }), verb(\"return\"), i[Symbol.iterator] = function () { return this; }, i;\r\n function verb(n, f) { i[n] = o[n] ? function (v) { return (p = !p) ? { value: __await(o[n](v)), done: false } : f ? f(v) : v; } : f; }\r\n}\r\n\r\nexport function __asyncValues(o) {\r\n if (!Symbol.asyncIterator) throw new TypeError(\"Symbol.asyncIterator is not defined.\");\r\n var m = o[Symbol.asyncIterator], i;\r\n return m ? m.call(o) : (o = typeof __values === \"function\" ? __values(o) : o[Symbol.iterator](), i = {}, verb(\"next\"), verb(\"throw\"), verb(\"return\"), i[Symbol.asyncIterator] = function () { return this; }, i);\r\n function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }\r\n function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }\r\n}\r\n\r\nexport function __makeTemplateObject(cooked, raw) {\r\n if (Object.defineProperty) { Object.defineProperty(cooked, \"raw\", { value: raw }); } else { cooked.raw = raw; }\r\n return cooked;\r\n};\r\n\r\nvar __setModuleDefault = Object.create ? (function(o, v) {\r\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\r\n}) : function(o, v) {\r\n o[\"default\"] = v;\r\n};\r\n\r\nexport function __importStar(mod) {\r\n if (mod && mod.__esModule) return mod;\r\n var result = {};\r\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\r\n __setModuleDefault(result, mod);\r\n return result;\r\n}\r\n\r\nexport function __importDefault(mod) {\r\n return (mod && mod.__esModule) ? mod : { default: mod };\r\n}\r\n\r\nexport function __classPrivateFieldGet(receiver, state, kind, f) {\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a getter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot read private member from an object whose class did not declare it\");\r\n return kind === \"m\" ? f : kind === \"a\" ? f.call(receiver) : f ? f.value : state.get(receiver);\r\n}\r\n\r\nexport function __classPrivateFieldSet(receiver, state, value, kind, f) {\r\n if (kind === \"m\") throw new TypeError(\"Private method is not writable\");\r\n if (kind === \"a\" && !f) throw new TypeError(\"Private accessor was defined without a setter\");\r\n if (typeof state === \"function\" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError(\"Cannot write private member to an object whose class did not declare it\");\r\n return (kind === \"a\" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;\r\n}\r\n\r\nexport function __classPrivateFieldIn(state, receiver) {\r\n if (receiver === null || (typeof receiver !== \"object\" && typeof receiver !== \"function\")) throw new TypeError(\"Cannot use 'in' operator on non-object\");\r\n return typeof state === \"function\" ? receiver === state : state.has(receiver);\r\n}\r\n\r\nexport function __addDisposableResource(env, value, async) {\r\n if (value !== null && value !== void 0) {\r\n if (typeof value !== \"object\" && typeof value !== \"function\") throw new TypeError(\"Object expected.\");\r\n var dispose;\r\n if (async) {\r\n if (!Symbol.asyncDispose) throw new TypeError(\"Symbol.asyncDispose is not defined.\");\r\n dispose = value[Symbol.asyncDispose];\r\n }\r\n if (dispose === void 0) {\r\n if (!Symbol.dispose) throw new TypeError(\"Symbol.dispose is not defined.\");\r\n dispose = value[Symbol.dispose];\r\n }\r\n if (typeof dispose !== \"function\") throw new TypeError(\"Object not disposable.\");\r\n env.stack.push({ value: value, dispose: dispose, async: async });\r\n }\r\n else if (async) {\r\n env.stack.push({ async: true });\r\n }\r\n return value;\r\n}\r\n\r\nvar _SuppressedError = typeof SuppressedError === \"function\" ? SuppressedError : function (error, suppressed, message) {\r\n var e = new Error(message);\r\n return e.name = \"SuppressedError\", e.error = error, e.suppressed = suppressed, e;\r\n};\r\n\r\nexport function __disposeResources(env) {\r\n function fail(e) {\r\n env.error = env.hasError ? new _SuppressedError(e, env.error, \"An error was suppressed during disposal.\") : e;\r\n env.hasError = true;\r\n }\r\n function next() {\r\n while (env.stack.length) {\r\n var rec = env.stack.pop();\r\n try {\r\n var result = rec.dispose && rec.dispose.call(rec.value);\r\n if (rec.async) return Promise.resolve(result).then(next, function(e) { fail(e); return next(); });\r\n }\r\n catch (e) {\r\n fail(e);\r\n }\r\n }\r\n if (env.hasError) throw env.error;\r\n }\r\n return next();\r\n}\r\n\r\nexport default {\r\n __extends: __extends,\r\n __assign: __assign,\r\n __rest: __rest,\r\n __decorate: __decorate,\r\n __param: __param,\r\n __metadata: __metadata,\r\n __awaiter: __awaiter,\r\n __generator: __generator,\r\n __createBinding: __createBinding,\r\n __exportStar: __exportStar,\r\n __values: __values,\r\n __read: __read,\r\n __spread: __spread,\r\n __spreadArrays: __spreadArrays,\r\n __spreadArray: __spreadArray,\r\n __await: __await,\r\n __asyncGenerator: __asyncGenerator,\r\n __asyncDelegator: __asyncDelegator,\r\n __asyncValues: __asyncValues,\r\n __makeTemplateObject: __makeTemplateObject,\r\n __importStar: __importStar,\r\n __importDefault: __importDefault,\r\n __classPrivateFieldGet: __classPrivateFieldGet,\r\n __classPrivateFieldSet: __classPrivateFieldSet,\r\n __classPrivateFieldIn: __classPrivateFieldIn,\r\n __addDisposableResource: __addDisposableResource,\r\n __disposeResources: __disposeResources,\r\n};\r\n",null,null,null,null],"names":["extendStatics","d","b","Object","setPrototypeOf","__proto__","Array","p","prototype","hasOwnProperty","call","__extends","TypeError","String","__","this","constructor","create","SuppressedError","typeChecker","type","typeString","value","getClassName","toString","comparable","Date","getTime","isArray","map","toJSON","isObject","isFunction","equals","a","length","i","length_1","keys","key","walkKeyPathValues","item","keyPath","next","depth","owner","currentKey","isNaN","Number","isProperty","BaseOperation","params","owneryQuery","options","name","init","reset","done","keep","GroupOperation","_super","children","_this","length_2","childrenNext","root","leaf","length_3","childOperation","NamedGroupOperation","QueryOperation","propop","parent","NestedOperation","_nextNestedValue","createTester","compare","Function","RegExp","result","test","lastIndex","comparableA","EqualsOperation","_test","numericalOperation","createNumericalOperation","typeofParams","actualValue","createNamedOperation","parentQuery","operationCreator","operations","throwUnsupportedOperation","Error","containsOperation","query","charAt","createNestedOperation","nestedQuery","parentKey","_a","createQueryOperations","selfOperations","createQueryOperation","_b","assign","_c","nestedOperations","ops","push","apply","op","split","createOperationTester","operation","$Ne","$ElemMatch","_queryOperation","child","$Not","$Size","assertGroupNotEmpty","values","$Or","_ops","success","$Nor","$In","_testers","concat","toLowerCase","length_4","$Nin","ownerQuery","_in","$Exists","$And","$All","$eq","$ne","$or","$nor","$elemMatch","$nin","$in","$lt","$lte","$gt","$gte","$mod","mod","equalsValue","$exists","$regex","pattern","$options","$not","typeAliases","number","v","string","bool","array","null","timestamp","$type","clazz","$and","$all","$size","$where","process","env","CSP_ENABLED","bind","createDefaultQueryOperation","defaultOperations"],"mappings":"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","x_google_ignoreList":[0]} \ No newline at end of file diff --git a/backend/node_modules/sift/src/core.ts b/backend/node_modules/sift/src/core.ts new file mode 100644 index 0000000..68ecea9 --- /dev/null +++ b/backend/node_modules/sift/src/core.ts @@ -0,0 +1,494 @@ +import { + isArray, + Key, + Comparator, + isVanillaObject, + comparable, + equals, + coercePotentiallyNull, + isProperty, +} from "./utils"; + +export interface Operation { + readonly keep: boolean; + readonly done: boolean; + propop: boolean; + reset(); + next(item: TItem, key?: Key, owner?: any, root?: boolean, leaf?: boolean); +} + +export type Tester = ( + item: any, + key?: Key, + owner?: any, + root?: boolean, + leaf?: boolean, +) => boolean; + +export interface NamedOperation { + name: string; +} + +export type OperationCreator = ( + params: any, + parentQuery: any, + options: Options, + name: string, +) => Operation; + +export type BasicValueQuery = { + $eq?: TValue; + $ne?: TValue; + $lt?: TValue; + $gt?: TValue; + $lte?: TValue; + $gte?: TValue; + $in?: TValue[]; + $nin?: TValue[]; + $all?: TValue[]; + $mod?: [number, number]; + $exists?: boolean; + $regex?: string | RegExp; + $size?: number; + $where?: ((this: TValue, obj: TValue) => boolean) | string; + $options?: "i" | "g" | "m" | "u"; + $type?: Function; + $not?: NestedQuery; + $or?: NestedQuery[]; + $nor?: NestedQuery[]; + $and?: NestedQuery[]; +}; + +export type ArrayValueQuery = { + $elemMatch?: Query; +} & BasicValueQuery; +type Unpacked = T extends (infer U)[] ? U : T; + +export type ValueQuery = + TValue extends Array + ? ArrayValueQuery> + : BasicValueQuery; + +type NotObject = string | number | Date | boolean | Array; +export type ShapeQuery = TItemSchema extends NotObject + ? {} + : { [k in keyof TItemSchema]?: TItemSchema[k] | ValueQuery }; + +export type NestedQuery = ValueQuery & + ShapeQuery; +export type Query = + | TItemSchema + | RegExp + | NestedQuery; + +export type QueryOperators = keyof ValueQuery; + +/** + * Walks through each value given the context - used for nested operations. E.g: + * { "person.address": { $eq: "blarg" }} + */ + +const walkKeyPathValues = ( + item: any, + keyPath: Key[], + next: Tester, + depth: number, + key: Key, + owner: any, +) => { + const currentKey = keyPath[depth]; + + // if array, then try matching. Might fall through for cases like: + // { $eq: [1, 2, 3] }, [ 1, 2, 3 ]. + if ( + isArray(item) && + isNaN(Number(currentKey)) && + !isProperty(item, currentKey) + ) { + for (let i = 0, { length } = item; i < length; i++) { + // if FALSE is returned, then terminate walker. For operations, this simply + // means that the search critera was met. + if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) { + return false; + } + } + } + + if (depth === keyPath.length || item == null) { + return next(item, key, owner, depth === 0, depth === keyPath.length); + } + + return walkKeyPathValues( + item[currentKey], + keyPath, + next, + depth + 1, + currentKey, + item, + ); +}; + +export abstract class BaseOperation + implements Operation +{ + keep: boolean; + done: boolean; + abstract propop: boolean; + constructor( + readonly params: TParams, + readonly owneryQuery: any, + readonly options: Options, + readonly name?: string, + ) { + this.init(); + } + protected init() {} + reset() { + this.done = false; + this.keep = false; + } + abstract next( + item: any, + key: Key, + parent: any, + root: boolean, + leaf?: boolean, + ); +} + +abstract class GroupOperation extends BaseOperation { + keep: boolean; + done: boolean; + + constructor( + params: any, + owneryQuery: any, + options: Options, + public readonly children: Operation[], + ) { + super(params, owneryQuery, options); + } + + /** + */ + + reset() { + this.keep = false; + this.done = false; + for (let i = 0, { length } = this.children; i < length; i++) { + this.children[i].reset(); + } + } + + abstract next(item: any, key: Key, owner: any, root: boolean); + + /** + */ + + protected childrenNext( + item: any, + key: Key, + owner: any, + root: boolean, + leaf?: boolean, + ) { + let done = true; + let keep = true; + for (let i = 0, { length } = this.children; i < length; i++) { + const childOperation = this.children[i]; + if (!childOperation.done) { + childOperation.next(item, key, owner, root, leaf); + } + if (!childOperation.keep) { + keep = false; + } + if (childOperation.done) { + if (!childOperation.keep) { + break; + } + } else { + done = false; + } + } + this.done = done; + this.keep = keep; + } +} + +export abstract class NamedGroupOperation + extends GroupOperation + implements NamedOperation +{ + abstract propop: boolean; + constructor( + params: any, + owneryQuery: any, + options: Options, + children: Operation[], + readonly name: string, + ) { + super(params, owneryQuery, options, children); + } +} + +export class QueryOperation extends GroupOperation { + readonly propop = true; + /** + */ + + next(item: TItem, key: Key, parent: any, root: boolean) { + this.childrenNext(item, key, parent, root); + } +} + +export class NestedOperation extends GroupOperation { + readonly propop = true; + constructor( + readonly keyPath: Key[], + params: any, + owneryQuery: any, + options: Options, + children: Operation[], + ) { + super(params, owneryQuery, options, children); + } + /** + */ + + next(item: any, key: Key, parent: any) { + walkKeyPathValues( + item, + this.keyPath, + this._nextNestedValue, + 0, + key, + parent, + ); + } + + /** + */ + + private _nextNestedValue = ( + value: any, + key: Key, + owner: any, + root: boolean, + leaf: boolean, + ) => { + this.childrenNext(value, key, owner, root, leaf); + return !this.done; + }; +} + +export const createTester = (a, compare: Comparator) => { + if (a instanceof Function) { + return a; + } + if (a instanceof RegExp) { + return (b) => { + const result = typeof b === "string" && a.test(b); + a.lastIndex = 0; + return result; + }; + } + const comparableA = comparable(a); + return (b) => compare(comparableA, comparable(b)); +}; + +export class EqualsOperation extends BaseOperation { + readonly propop = true; + private _test: Tester; + init() { + this._test = createTester(this.params, this.options.compare); + } + next(item, key: Key, parent: any) { + if (!Array.isArray(parent) || parent.hasOwnProperty(key)) { + if (this._test(item, key, parent)) { + this.done = true; + this.keep = true; + } + } + } +} + +export const createEqualsOperation = ( + params: any, + owneryQuery: any, + options: Options, +) => new EqualsOperation(params, owneryQuery, options); + +export const numericalOperationCreator = + (createNumericalOperation: OperationCreator) => + (params: any, owneryQuery: any, options: Options, name: string) => { + return createNumericalOperation(params, owneryQuery, options, name); + }; + +export const numericalOperation = (createTester: (value: any) => Tester) => + numericalOperationCreator( + (params: any, owneryQuery: Query, options: Options, name: string) => { + const typeofParams = typeof comparable(params); + const test = createTester(params); + return new EqualsOperation( + (b) => { + const actualValue = coercePotentiallyNull(b); + return ( + typeof comparable(actualValue) === typeofParams && test(actualValue) + ); + }, + owneryQuery, + options, + name, + ); + }, + ); + +export type Options = { + operations: { + [identifier: string]: OperationCreator; + }; + compare: (a, b) => boolean; +}; + +const createNamedOperation = ( + name: string, + params: any, + parentQuery: any, + options: Options, +) => { + const operationCreator = options.operations[name]; + if (!operationCreator) { + throwUnsupportedOperation(name); + } + return operationCreator(params, parentQuery, options, name); +}; + +const throwUnsupportedOperation = (name: string) => { + throw new Error(`Unsupported operation: ${name}`); +}; + +export const containsOperation = (query: any, options: Options) => { + for (const key in query) { + if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") + return true; + } + return false; +}; +const createNestedOperation = ( + keyPath: Key[], + nestedQuery: any, + parentKey: string, + owneryQuery: any, + options: Options, +) => { + if (containsOperation(nestedQuery, options)) { + const [selfOperations, nestedOperations] = createQueryOperations( + nestedQuery, + parentKey, + options, + ); + if (nestedOperations.length) { + throw new Error( + `Property queries must contain only operations, or exact objects.`, + ); + } + return new NestedOperation( + keyPath, + nestedQuery, + owneryQuery, + options, + selfOperations, + ); + } + return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [ + new EqualsOperation(nestedQuery, owneryQuery, options), + ]); +}; + +export const createQueryOperation = ( + query: Query, + owneryQuery: any = null, + { compare, operations }: Partial = {}, +): QueryOperation => { + const options = { + compare: compare || equals, + operations: Object.assign({}, operations || {}), + }; + + const [selfOperations, nestedOperations] = createQueryOperations( + query, + null, + options, + ); + + const ops = []; + + if (selfOperations.length) { + ops.push( + new NestedOperation([], query, owneryQuery, options, selfOperations), + ); + } + + ops.push(...nestedOperations); + + if (ops.length === 1) { + return ops[0]; + } + return new QueryOperation(query, owneryQuery, options, ops); +}; + +const createQueryOperations = ( + query: any, + parentKey: string, + options: Options, +) => { + const selfOperations = []; + const nestedOperations = []; + if (!isVanillaObject(query)) { + selfOperations.push(new EqualsOperation(query, query, options)); + return [selfOperations, nestedOperations]; + } + for (const key in query) { + if (options.operations.hasOwnProperty(key)) { + const op = createNamedOperation(key, query[key], query, options); + + if (op) { + if (!op.propop && parentKey && !options.operations[parentKey]) { + throw new Error( + `Malformed query. ${key} cannot be matched against property.`, + ); + } + } + + // probably just a flag for another operation (like $options) + if (op != null) { + selfOperations.push(op); + } + } else if (key.charAt(0) === "$") { + throwUnsupportedOperation(key); + } else { + nestedOperations.push( + createNestedOperation(key.split("."), query[key], key, query, options), + ); + } + } + + return [selfOperations, nestedOperations]; +}; + +export const createOperationTester = + (operation: Operation) => + (item: TItem, key?: Key, owner?: any) => { + operation.reset(); + operation.next(item, key, owner); + return operation.keep; + }; + +export const createQueryTester = ( + query: Query, + options: Partial = {}, +) => { + return createOperationTester( + createQueryOperation(query, null, options), + ); +}; diff --git a/backend/node_modules/sift/src/index.ts b/backend/node_modules/sift/src/index.ts new file mode 100644 index 0000000..ea733b9 --- /dev/null +++ b/backend/node_modules/sift/src/index.ts @@ -0,0 +1,54 @@ +import * as defaultOperations from "./operations"; +import { + Query, + QueryOperators, + BasicValueQuery, + ArrayValueQuery, + ValueQuery, + NestedQuery, + ShapeQuery, + Options, + createQueryTester, + EqualsOperation, + createQueryOperation, + createEqualsOperation, + createOperationTester, +} from "./core"; + +const createDefaultQueryOperation = ( + query: Query, + ownerQuery: any, + { compare, operations }: Partial = {}, +) => { + return createQueryOperation(query, ownerQuery, { + compare, + operations: Object.assign({}, defaultOperations, operations || {}), + }); +}; + +const createDefaultQueryTester = ( + query: Query, + options: Partial = {}, +) => { + const op = createDefaultQueryOperation(query, null, options); + return createOperationTester(op); +}; + +export { + Query, + QueryOperators, + BasicValueQuery, + ArrayValueQuery, + ValueQuery, + NestedQuery, + ShapeQuery, + EqualsOperation, + createQueryTester, + createOperationTester, + createDefaultQueryOperation, + createEqualsOperation, + createQueryOperation, +}; +export * from "./operations"; + +export default createDefaultQueryTester; diff --git a/backend/node_modules/sift/src/operations.ts b/backend/node_modules/sift/src/operations.ts new file mode 100644 index 0000000..68177b1 --- /dev/null +++ b/backend/node_modules/sift/src/operations.ts @@ -0,0 +1,422 @@ +import { + BaseOperation, + EqualsOperation, + Options, + createTester, + Tester, + createQueryOperation, + QueryOperation, + Operation, + Query, + NamedGroupOperation, + numericalOperation, + containsOperation, +} from "./core"; +import { Key, comparable, isFunction, isArray } from "./utils"; + +class $Ne extends BaseOperation { + readonly propop = true; + private _test: Tester; + init() { + this._test = createTester(this.params, this.options.compare); + } + reset() { + super.reset(); + this.keep = true; + } + next(item: any) { + if (this._test(item)) { + this.done = true; + this.keep = false; + } + } +} +// https://docs.mongodb.com/manual/reference/operator/query/elemMatch/ +class $ElemMatch extends BaseOperation> { + readonly propop = true; + private _queryOperation: QueryOperation; + init() { + if (!this.params || typeof this.params !== "object") { + throw new Error(`Malformed query. $elemMatch must by an object.`); + } + this._queryOperation = createQueryOperation( + this.params, + this.owneryQuery, + this.options, + ); + } + reset() { + super.reset(); + this._queryOperation.reset(); + } + next(item: any) { + if (isArray(item)) { + for (let i = 0, { length } = item; i < length; i++) { + // reset query operation since item being tested needs to pass _all_ query + // operations for it to be a success + this._queryOperation.reset(); + + const child = item[i]; + this._queryOperation.next(child, i, item, false); + this.keep = this.keep || this._queryOperation.keep; + } + this.done = true; + } else { + this.done = false; + this.keep = false; + } + } +} + +class $Not extends BaseOperation> { + readonly propop = true; + private _queryOperation: QueryOperation; + init() { + this._queryOperation = createQueryOperation( + this.params, + this.owneryQuery, + this.options, + ); + } + reset() { + super.reset(); + this._queryOperation.reset(); + } + next(item: any, key: Key, owner: any, root: boolean) { + this._queryOperation.next(item, key, owner, root); + this.done = this._queryOperation.done; + this.keep = !this._queryOperation.keep; + } +} + +export class $Size extends BaseOperation { + readonly propop = true; + init() {} + next(item) { + if (isArray(item) && item.length === this.params) { + this.done = true; + this.keep = true; + } + // if (parent && parent.length === this.params) { + // this.done = true; + // this.keep = true; + // } + } +} + +const assertGroupNotEmpty = (values: any[]) => { + if (values.length === 0) { + throw new Error(`$and/$or/$nor must be a nonempty array`); + } +}; + +class $Or extends BaseOperation { + readonly propop = false; + private _ops: Operation[]; + init() { + assertGroupNotEmpty(this.params); + this._ops = this.params.map((op) => + createQueryOperation(op, null, this.options), + ); + } + reset() { + this.done = false; + this.keep = false; + for (let i = 0, { length } = this._ops; i < length; i++) { + this._ops[i].reset(); + } + } + next(item: any, key: Key, owner: any) { + let done = false; + let success = false; + for (let i = 0, { length } = this._ops; i < length; i++) { + const op = this._ops[i]; + op.next(item, key, owner); + if (op.keep) { + done = true; + success = op.keep; + break; + } + } + + this.keep = success; + this.done = done; + } +} + +class $Nor extends $Or { + readonly propop = false; + next(item: any, key: Key, owner: any) { + super.next(item, key, owner); + this.keep = !this.keep; + } +} + +class $In extends BaseOperation { + readonly propop = true; + private _testers: Tester[]; + init() { + const params = Array.isArray(this.params) ? this.params : [this.params]; + this._testers = params.map((value) => { + if (containsOperation(value, this.options)) { + throw new Error(`cannot nest $ under ${this.name.toLowerCase()}`); + } + return createTester(value, this.options.compare); + }); + } + next(item: any, key: Key, owner: any) { + let done = false; + let success = false; + for (let i = 0, { length } = this._testers; i < length; i++) { + const test = this._testers[i]; + if (test(item)) { + done = true; + success = true; + break; + } + } + + this.keep = success; + this.done = done; + } +} + +class $Nin extends BaseOperation { + readonly propop = true; + private _in: $In; + constructor(params: any, ownerQuery: any, options: Options, name: string) { + super(params, ownerQuery, options, name); + this._in = new $In(params, ownerQuery, options, name); + } + next(item: any, key: Key, owner: any, root: boolean) { + this._in.next(item, key, owner); + + if (isArray(owner) && !root) { + if (this._in.keep) { + this.keep = false; + this.done = true; + } else if (key == owner.length - 1) { + this.keep = true; + this.done = true; + } + } else { + this.keep = !this._in.keep; + this.done = true; + } + } + reset() { + super.reset(); + this._in.reset(); + } +} + +class $Exists extends BaseOperation { + readonly propop = true; + next(item: any, key: Key, owner: any, root: boolean, leaf?: boolean) { + if (!leaf) { + this.done = true; + this.keep = !this.params; + } else if (owner.hasOwnProperty(key) === this.params) { + this.done = true; + this.keep = true; + } + } +} + +class $And extends NamedGroupOperation { + readonly propop = false; + constructor( + params: Query[], + owneryQuery: Query, + options: Options, + name: string, + ) { + super( + params, + owneryQuery, + options, + params.map((query) => createQueryOperation(query, owneryQuery, options)), + name, + ); + + assertGroupNotEmpty(params); + } + next(item: any, key: Key, owner: any, root: boolean) { + this.childrenNext(item, key, owner, root); + } +} + +class $All extends NamedGroupOperation { + readonly propop = true; + constructor( + params: Query[], + owneryQuery: Query, + options: Options, + name: string, + ) { + super( + params, + owneryQuery, + options, + params.map((query) => createQueryOperation(query, owneryQuery, options)), + name, + ); + } + next(item: any, key: Key, owner: any, root: boolean) { + this.childrenNext(item, key, owner, root); + } +} + +export const $eq = (params: any, owneryQuery: Query, options: Options) => + new EqualsOperation(params, owneryQuery, options); +export const $ne = ( + params: any, + owneryQuery: Query, + options: Options, + name: string, +) => new $Ne(params, owneryQuery, options, name); +export const $or = ( + params: Query[], + owneryQuery: Query, + options: Options, + name: string, +) => new $Or(params, owneryQuery, options, name); +export const $nor = ( + params: Query[], + owneryQuery: Query, + options: Options, + name: string, +) => new $Nor(params, owneryQuery, options, name); +export const $elemMatch = ( + params: any, + owneryQuery: Query, + options: Options, + name: string, +) => new $ElemMatch(params, owneryQuery, options, name); +export const $nin = ( + params: any, + owneryQuery: Query, + options: Options, + name: string, +) => new $Nin(params, owneryQuery, options, name); +export const $in = ( + params: any, + owneryQuery: Query, + options: Options, + name: string, +) => { + return new $In(params, owneryQuery, options, name); +}; + +export const $lt = numericalOperation((params) => (b) => { + return b != null && b < params; +}); +export const $lte = numericalOperation((params) => (b) => { + return b === params || b <= params; +}); +export const $gt = numericalOperation((params) => (b) => { + return b != null && b > params; +}); +export const $gte = numericalOperation((params) => (b) => { + return b === params || b >= params; +}); +export const $mod = ( + [mod, equalsValue]: number[], + owneryQuery: Query, + options: Options, +) => + new EqualsOperation( + (b) => comparable(b) % mod === equalsValue, + owneryQuery, + options, + ); +export const $exists = ( + params: boolean, + owneryQuery: Query, + options: Options, + name: string, +) => new $Exists(params, owneryQuery, options, name); +export const $regex = ( + pattern: string, + owneryQuery: Query, + options: Options, +) => + new EqualsOperation( + new RegExp(pattern, owneryQuery.$options), + owneryQuery, + options, + ); +export const $not = ( + params: any, + owneryQuery: Query, + options: Options, + name: string, +) => new $Not(params, owneryQuery, options, name); + +const typeAliases = { + number: (v) => typeof v === "number", + string: (v) => typeof v === "string", + bool: (v) => typeof v === "boolean", + array: (v) => Array.isArray(v), + null: (v) => v === null, + timestamp: (v) => v instanceof Date, +}; + +export const $type = ( + clazz: Function | string, + owneryQuery: Query, + options: Options, +) => + new EqualsOperation( + (b) => { + if (typeof clazz === "string") { + if (!typeAliases[clazz]) { + throw new Error(`Type alias does not exist`); + } + + return typeAliases[clazz](b); + } + + return b != null ? b instanceof clazz || b.constructor === clazz : false; + }, + owneryQuery, + options, + ); +export const $and = ( + params: Query[], + ownerQuery: Query, + options: Options, + name: string, +) => new $And(params, ownerQuery, options, name); + +export const $all = ( + params: Query[], + ownerQuery: Query, + options: Options, + name: string, +) => new $All(params, ownerQuery, options, name); +export const $size = ( + params: number, + ownerQuery: Query, + options: Options, +) => new $Size(params, ownerQuery, options, "$size"); +export const $options = () => null; +export const $where = ( + params: string | Function, + ownerQuery: Query, + options: Options, +) => { + let test; + + if (isFunction(params)) { + test = params; + } else if (!process.env.CSP_ENABLED) { + test = new Function("obj", "return " + params); + } else { + throw new Error( + `In CSP mode, sift does not support strings in "$where" condition`, + ); + } + + return new EqualsOperation((b) => test.bind(b)(b), ownerQuery, options); +}; diff --git a/backend/node_modules/sift/src/utils.ts b/backend/node_modules/sift/src/utils.ts new file mode 100644 index 0000000..02e2a75 --- /dev/null +++ b/backend/node_modules/sift/src/utils.ts @@ -0,0 +1,74 @@ +export type Key = string | number; +export type Comparator = (a, b) => boolean; +export const typeChecker = (type) => { + const typeString = "[object " + type + "]"; + return function (value): value is TType { + return getClassName(value) === typeString; + }; +}; + +const getClassName = (value) => Object.prototype.toString.call(value); + +export const comparable = (value: any) => { + if (value instanceof Date) { + return value.getTime(); + } else if (isArray(value)) { + return value.map(comparable); + } else if (value && typeof value.toJSON === "function") { + return value.toJSON(); + } + + return value; +}; + +export const coercePotentiallyNull = (value: any) => + value == null ? null : value; + +export const isArray = typeChecker>("Array"); +export const isObject = typeChecker("Object"); +export const isFunction = typeChecker("Function"); +export const isProperty = (item: any, key: any) => { + return item.hasOwnProperty(key) && !isFunction(item[key]); +}; +export const isVanillaObject = (value) => { + return ( + value && + (value.constructor === Object || + value.constructor === Array || + value.constructor.toString() === "function Object() { [native code] }" || + value.constructor.toString() === "function Array() { [native code] }") && + !value.toJSON + ); +}; + +export const equals = (a, b) => { + if (a == null && a == b) { + return true; + } + if (a === b) { + return true; + } + + if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) { + return false; + } + + if (isArray(a)) { + if (a.length !== b.length) { + return false; + } + for (let i = 0, { length } = a; i < length; i++) { + if (!equals(a[i], b[i])) return false; + } + return true; + } else if (isObject(a)) { + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (const key in a) { + if (!equals(a[key], b[key])) return false; + } + return true; + } + return false; +}; diff --git a/backend/package-lock.json b/backend/package-lock.json index ab174b2..5c8c10d 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -12,7 +12,8 @@ "cors": "^2.8.6", "dotenv": "^17.3.1", "express": "^5.2.1", - "mongodb": "^7.1.1" + "mongodb": "^7.1.1", + "mongoose": "^9.3.3" } }, "node_modules/@mongodb-js/saslprep": { @@ -513,6 +514,15 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/kareem": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.2.0.tgz", + "integrity": "sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -633,6 +643,45 @@ "node": ">=20.19.0" } }, + "node_modules/mongoose": { + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.3.3.tgz", + "integrity": "sha512-sfv5LOIPWeN5o/281kp4Rx9ZnuXb0g8CtvBTi7trYQs2PYYx8LWXegXxG3ar7VEns1o+d4h9LI/Dtc7dTTyYmA==", + "license": "MIT", + "dependencies": { + "kareem": "3.2.0", + "mongodb": "~7.1", + "mpath": "0.9.0", + "mquery": "6.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.0.tgz", + "integrity": "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -915,6 +964,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", diff --git a/backend/package.json b/backend/package.json index 3d4c3cf..c836aca 100644 --- a/backend/package.json +++ b/backend/package.json @@ -14,6 +14,7 @@ "cors": "^2.8.6", "dotenv": "^17.3.1", "express": "^5.2.1", - "mongodb": "^7.1.1" + "mongodb": "^7.1.1", + "mongoose": "^9.3.3" } } diff --git a/frontend/js/api.js b/frontend/js/api.js index 9a043ad..1172469 100644 --- a/frontend/js/api.js +++ b/frontend/js/api.js @@ -1,5 +1,4 @@ // GLOBALS - const $taskForm = document.getElementById('taskForm'); const $toDoList = document.getElementById('toDoList'); const $completedList = document.getElementById('completedList'); @@ -33,16 +32,18 @@ $taskForm.addEventListener("submit", (event) => { } }); }); + // EXAMPLE API -async function exampleAPI() { - const response = await fetch("http://localhost:3001/test"); - const data = await response.text(); - console.log(data); -} +// async function exampleAPI() { +// const response = await fetch("http://localhost:3001/test"); +// const data = await response.text(); +// console.log(data); +// } +// +// exampleAPI(); + -exampleAPI(); // DISPLAY TASKS - async function displayTasks() { const response = await fetch("http://localhost:3001/api/tasks") @@ -166,4 +167,4 @@ function deleteTask(taskId) { function convertDate(dateString) { return new Date(dateString).toLocaleDateString("en-AU"); -} \ No newline at end of file +} diff --git a/index.js b/index.js index bcab7c2..0ef4703 100644 --- a/index.js +++ b/index.js @@ -1,16 +1,15 @@ -const { MongoClient, ServerApiVersion } = require('mongodb'); -const uri = 'mongodb+srv://ugochikay_db_user:bt06DpE6QZqLHZ1x>@cluster0.aqxmamw.mongodb.net/?appName=Cluster0'; +// const { MongoClient, ServerApiVersion } = require('mongodb'); +// const uri = 'mongodb+srv://ugochikay_db_user:bt06DpE6QZqLHZ1x>@cluster0.aqxmamw.mongodb.net/?appName=Cluster0'; + //Dependancies const express = require("express"); -const cors = require("cors"); //Change +const cors = require("cors"); const port = 3000; - const app = express(); //Middleware setup app.use(express.json()); - app.use(cors("*")); //change //API routes @@ -19,31 +18,30 @@ app.get("/get/example", async (req, res) =>{ }) const tasks = [ - { - id: 1, - completed: false, - title:"Wash car", -decription: "Make sure I have washed the car well", -dueDate: "14/03/2026", -dateCreated: new Date(Date.now()).toLocaleString() -}, -{ -id: 2, -completed: true, -title: "Audit Report", -description: "The Audit Report must be ready by due date", -duedate: "16/03/2026", -dateCreated: new Date(Date.now()).toLocaleString() -}, - -{ -id: 3, -completed: true, -title: "Performance Report", -description: "The Performance Report must be ready by due date", -duedate: "16/03/2026", -dateCreated: new Date(Date.now()).toLocaleString() -}, + { + id: 1, + completed: false, + title:"Wash car", + decription: "Make sure I have washed the car well", + dueDate: "14/03/2026", + dateCreated: new Date(Date.now()).toLocaleString() + }, + { + id: 2, + completed: true, + title: "Audit Report", + description: "The Audit Report must be ready by due date", + duedate: "16/03/2026", + dateCreated: new Date(Date.now()).toLocaleString() + }, + { + id: 3, + completed: true, + title: "Performance Report", + description: "The Performance Report must be ready by due date", + duedate: "16/03/2026", + dateCreated: new Date(Date.now()).toLocaleString() + }, ]; //Get all Tasks @@ -89,4 +87,4 @@ app.post("/api/tasks/todo", async (req, res) => { //App startup app.listen(port, () => { console.log(`To Do App listening on port ${port}`); -}); \ No newline at end of file +});