@@ -20,16 +20,7 @@ import type { AggregateOptions } from './operations/aggregate';
2020import type { OperationParent } from './operations/command';
2121import type { ServerSessionId } from './sessions';
2222import { CSOTTimeoutContext, type TimeoutContext } from './timeout';
23- import { filterOptions, getTopology, type MongoDBNamespace, squashError } from './utils';
24-
25- const CHANGE_STREAM_OPTIONS = [
26- 'resumeAfter',
27- 'startAfter',
28- 'startAtOperationTime',
29- 'fullDocument',
30- 'fullDocumentBeforeChange',
31- 'showExpandedEvents'
32- ] as const;
23+ import { type AnyOptions, getTopology, type MongoDBNamespace, squashError } from './utils';
3324
3425const CHANGE_DOMAIN_TYPES = {
3526 COLLECTION: Symbol('Collection'),
@@ -43,6 +34,14 @@ const NO_RESUME_TOKEN_ERROR =
4334 'A change stream document has been received that lacks a resume token (_id).';
4435const CHANGESTREAM_CLOSED_ERROR = 'ChangeStream is closed';
4536
37+ const INVALID_STAGE_OPTIONS = buildDisallowedChangeStreamOptions();
38+
39+ export function filterOutOptions(options: AnyOptions): AnyOptions {
40+ return Object.fromEntries(
41+ Object.entries(options).filter(([k, _]) => !INVALID_STAGE_OPTIONS.has(k))
42+ );
43+ }
44+
4645/**
4746 * Represents the logical starting point for a new ChangeStream or resuming a ChangeStream on the server.
4847 * @see https://www.mongodb.com/docs/manual/changeStreams/#std-label-change-stream-resume
@@ -898,7 +897,7 @@ export class ChangeStream<
898897 private _createChangeStreamCursor(
899898 options: ChangeStreamOptions | ChangeStreamCursorOptions
900899 ): ChangeStreamCursor<TSchema, TChange> {
901- const changeStreamStageOptions = filterOptions (options, CHANGE_STREAM_OPTIONS );
900+ const changeStreamStageOptions: Document = filterOutOptions (options);
902901 if (this.type === CHANGE_DOMAIN_TYPES.CLUSTER) {
903902 changeStreamStageOptions.allChangesForCluster = true;
904903 }
@@ -1084,3 +1083,76 @@ export class ChangeStream<
10841083 }
10851084 }
10861085}
1086+
1087+ /**
1088+ * This function returns a list of options that are *not* supported by the $changeStream
1089+ * aggregation stage. This is best-effort - it uses the options "officially supported" by the driver
1090+ * to derive a list of known, unsupported options for the $changeStream stage.
1091+ *
1092+ * Notably, at runtime, users can still provide options unknown to the driver and the driver will
1093+ * *not* filter them out of the options object (see NODE-5510).
1094+ */
1095+ function buildDisallowedChangeStreamOptions(): Set<string> {
1096+ /** hard-coded list of allowed ChangeStream options */
1097+ type CSOptions =
1098+ | 'resumeAfter'
1099+ | 'startAfter'
1100+ | 'startAtOperationTime'
1101+ | 'fullDocument'
1102+ | 'fullDocumentBeforeChange'
1103+ | 'showExpandedEvents';
1104+
1105+ /**
1106+ * a type representing all known options that the driver supports that are *not* change stream stage options.
1107+ *
1108+ * each known key is mapped to a non-optional string, so that if new driver-specific options are added, the
1109+ * instantiation of `denyList` below results in a TS error.
1110+ */
1111+ type DisallowedOptions = {
1112+ [k in Exclude<
1113+ keyof ChangeStreamOptions & { timeoutContext: TimeoutContext },
1114+ CSOptions
1115+ >]: string;
1116+ };
1117+
1118+ const denyList: DisallowedOptions = {
1119+ allowDiskUse: '',
1120+ authdb: '',
1121+ batchSize: '',
1122+ bsonRegExp: '',
1123+ bypassDocumentValidation: '',
1124+ bypassPinningCheck: '',
1125+ checkKeys: '',
1126+ collation: '',
1127+ comment: '',
1128+ cursor: '',
1129+ dbName: '',
1130+ enableUtf8Validation: '',
1131+ explain: '',
1132+ fieldsAsRaw: '',
1133+ hint: '',
1134+ ignoreUndefined: '',
1135+ let: '',
1136+ maxAwaitTimeMS: '',
1137+ maxTimeMS: '',
1138+ omitMaxTimeMS: '',
1139+ out: '',
1140+ promoteBuffers: '',
1141+ promoteLongs: '',
1142+ promoteValues: '',
1143+ raw: '',
1144+ rawData: '',
1145+ readConcern: '',
1146+ readPreference: '',
1147+ serializeFunctions: '',
1148+ session: '',
1149+ timeoutContext: '',
1150+ timeoutMS: '',
1151+ timeoutMode: '',
1152+ useBigInt64: '',
1153+ willRetryWrite: '',
1154+ writeConcern: ''
1155+ };
1156+
1157+ return new Set(Object.keys(denyList));
1158+ }
0 commit comments