Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
40a8727
WIP
addaleax Jun 3, 2024
4b2991c
use DeepInspectServiceProviderWrapper, install our own inspect functi…
lerouxb Nov 20, 2025
f85dc9f
you cannot extend the return value of initializeBulkOp
lerouxb Nov 20, 2025
24ef043
apparently inspect passed as the final parameter isn't always a thing
lerouxb Nov 20, 2025
cda077c
better name
lerouxb Nov 24, 2025
7d008f7
inspect a shallow copy
lerouxb Nov 24, 2025
a27b349
fix comment
lerouxb Nov 24, 2025
8c523bb
leave bson values alone, don't accidentally override existing custom …
lerouxb Nov 24, 2025
aa2e830
don't remove things
lerouxb Nov 24, 2025
6307dd3
wrap every type of cursor
lerouxb Nov 24, 2025
70aa3ee
don't accidentally override our custom Date and RegExp inpect functions
lerouxb Nov 24, 2025
8624b68
don't depend on inspect
lerouxb Nov 24, 2025
cae4017
more indirection
lerouxb Nov 24, 2025
7d2d0a3
fill out more things on the stub
lerouxb Nov 24, 2025
d12548c
how did this work before?
lerouxb Nov 24, 2025
12fc59b
Merge branch 'main' into print-output-full
lerouxb Nov 25, 2025
22e67f8
add the custom inspect symbol as not-enumerable
lerouxb Nov 25, 2025
f4f64bc
pull bsonLibrary off _sp rather
lerouxb Nov 25, 2025
709de79
Merge branch 'main' into print-output-full
lerouxb Nov 26, 2025
77f64f2
don't wrap the service provider in java land
lerouxb Nov 27, 2025
72d8679
Update packages/shell-api/src/custom-inspect.ts
lerouxb Nov 27, 2025
a93b8fd
some unit tests
lerouxb Nov 27, 2025
0ce95c3
more unit tests
lerouxb Nov 27, 2025
69d3d13
adjust runtime indepdendence tests
lerouxb Nov 27, 2025
209c9f5
Update packages/shell-api/src/custom-inspect.ts
lerouxb Nov 27, 2025
c023d46
fixup: remove wrappable flag, skip async iter support for now, use fn…
addaleax Nov 27, 2025
e31ba35
fixup: merge cursor implementations, move to separate subdir
addaleax Nov 27, 2025
2bc07c3
fixup: fix runtime independence test again
addaleax Nov 27, 2025
370288f
fixup: add e2e tests
addaleax Nov 27, 2025
28dd205
fixup: oidc test
addaleax Nov 27, 2025
7202eb2
fixup: stub out more cli-repl test sp usage
addaleax Nov 28, 2025
1b72c5b
feat(cli-repl): add flag to control deep inspect behavior
addaleax Nov 28, 2025
f783da4
fixup: prettier doesnt run on subdirs?
addaleax Nov 28, 2025
0a9c1bf
fixup: add missing waitForPrompt call
addaleax Nov 28, 2025
caaa723
fixup: simplify using waitForCleanOutput
addaleax Nov 28, 2025
02e70c7
Merge remote-tracking branch 'origin/main' into print-output-full
addaleax Nov 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/e2e-tests/test/e2e-oidc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ describe('OIDC auth e2e', function () {

// Internal hack to get a state-share server as e.g. Compass or the VSCode extension would
let handle = await shell.executeLine(
'db.getMongo()._serviceProvider.currentClientOptions.parentState.getStateShareServer()'
'db.getMongo()._serviceProvider._sp.currentClientOptions.parentState.getStateShareServer()'
);
// `handle` can include the next prompt when returned by `shell.executeLine()`,
// so look for the longest prefix of it that is valid JSON.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ internal class JavaServiceProvider(private var client: MongoClient?,
@HostAccess.Export
val platform = "JavaShell"

@JvmField
@HostAccess.Export
val deepInspectWrappable = false

@HostAccess.Export
override fun runCommand(database: String, spec: Value): Value = promise {
getDatabase(database, null).map { db ->
Expand Down
7 changes: 5 additions & 2 deletions packages/service-provider-core/src/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type {
AutoEncryptionOptions,
Collection,
} from './all-transport-types';
import type { ConnectionExtraInfo } from './index';
import type { ConnectionExtraInfo, ServiceProvider } from './index';
import type { ReplPlatform } from './platform';
import type {
AWSEncryptionKeyOptions,
Expand Down Expand Up @@ -90,7 +90,10 @@ export default interface Admin {
* @param uri
* @param options
*/
getNewConnection(uri: string, options: MongoClientOptions): Promise<any>; // returns the ServiceProvider instance
getNewConnection(
uri: string,
options: MongoClientOptions
): Promise<ServiceProvider>;

/**
* Return the URI for the current connection, if this ServiceProvider is connected.
Expand Down
5 changes: 4 additions & 1 deletion packages/service-provider-core/src/service-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ export default interface ServiceProvider
extends Readable,
Writable,
Closable,
Admin {}
Admin {
deepInspectWrappable: boolean;
}

export class ServiceProviderCore {
public bsonLibrary: BSON;
public deepInspectWrappable = true;
constructor(bsonLibrary?: BSON) {
if (bsonLibrary === undefined) {
throw new MongoshInternalError('BSON Library is undefined.');
Expand Down
55 changes: 55 additions & 0 deletions packages/shell-api/src/custom-inspect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import type { InspectOptions, inspect as _inspect } from 'util';

const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');

function customDocumentInspect(
this: Document,
depth: number,
inspectOptions: InspectOptions,
inspect: typeof _inspect
) {
const newInspectOptions = {
...inspectOptions,
depth: Infinity,
maxArrayLength: Infinity,
maxStringLength: Infinity,
};

// reuse the standard inpect logic for an object without causing infinite
// recursion
const copyToInspect: any = Array.isArray(this) ? this.slice() : { ...this };
delete copyToInspect[customInspectSymbol];
return inspect(copyToInspect, newInspectOptions);
}

function addInspectSymbol(obj: any) {
if (!(obj as any)[customInspectSymbol]) {
Object.defineProperty(obj, customInspectSymbol, {
value: customDocumentInspect,
enumerable: false,
writable: true,
configurable: true,
});
}
}

export function addCustomInspect(obj: any) {
if (Array.isArray(obj)) {
addInspectSymbol(obj);
for (const item of obj) {
addCustomInspect(item);
}
} else if (
obj &&
typeof obj === 'object' &&
obj !== null &&
!obj._bsontype &&
!(obj instanceof Date) &&
!(obj instanceof RegExp)
) {
addInspectSymbol(obj);
for (const value of Object.values(obj)) {
addCustomInspect(value);
}
}
}
142 changes: 142 additions & 0 deletions packages/shell-api/src/deep-inspect-aggregation-cursor-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import type {
Document,
ReadConcernLike,
ReadPreferenceLike,
ServiceProviderAggregationCursor,
} from '@mongosh/service-provider-core';
import type { PickMethodsByReturnType } from './pick-methods-by-return-type';
import { addCustomInspect } from './custom-inspect';

export class DeepInspectAggregationCursorWrapper<TSchema = Document>
implements ServiceProviderAggregationCursor<TSchema>
{
_cursor: ServiceProviderAggregationCursor<TSchema>;

constructor(cursor: ServiceProviderAggregationCursor<TSchema>) {
this._cursor = cursor;
}

project = forwardedMethod('project');
skip = forwardedMethod('skip');
sort = forwardedMethod('sort');
explain = forwardedMethod('explain');
addCursorFlag = forwardedMethod('addCursorFlag');
withReadPreference = (readPreference: ReadPreferenceLike) => {
this._cursor.withReadPreference(readPreference);
return this;
};
withReadConcern(readConcern: ReadConcernLike) {
this._cursor.withReadConcern(readConcern);
return this;
}
batchSize = forwardedMethod('batchSize');
hasNext = forwardedMethod('hasNext');
close = forwardedMethod('close');
maxTimeMS = forwardedMethod('maxTimeMS');
bufferedCount = forwardedMethod('bufferedCount');

next = forwardResultPromise<TSchema, 'next'>('next');
tryNext = forwardResultPromise<TSchema, 'tryNext'>('tryNext');

toArray = forwardResultsPromise<TSchema, 'toArray'>('toArray');
readBufferedDocuments = forwardResults<TSchema, 'readBufferedDocuments'>(
'readBufferedDocuments'
);

get closed(): boolean {
return this._cursor.closed;
}

async *[Symbol.asyncIterator]() {
yield* this._cursor;
return;
}
}

function forwardResultPromise<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderAggregationCursor<TSchema>,
Promise<TSchema | null>
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
return async function (
this: DeepInspectAggregationCursorWrapper,
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
): // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore The returntype already contains a promise
ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
const result = await (this._cursor[key] as any)(...args);
if (result) {
addCustomInspect(result);
}
return result;
};
}

function forwardResultsPromise<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderAggregationCursor<TSchema>,
Promise<TSchema[]>
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
return async function (
this: DeepInspectAggregationCursorWrapper,
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
): // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore The returntype already contains a promise
ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
const results = await (this._cursor[key] as any)(...args);
addCustomInspect(results);
return results;
};
}

function forwardResults<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderAggregationCursor<TSchema>,
TSchema[]
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
return function (
this: DeepInspectAggregationCursorWrapper,
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
): ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
const results = (this._cursor[key] as any)(...args);
addCustomInspect(results);
return results;
};
}

function forwardedMethod<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderAggregationCursor<TSchema>,
any
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
return function (
this: DeepInspectAggregationCursorWrapper,
...args: Parameters<Required<ServiceProviderAggregationCursor<TSchema>>[K]>
): ReturnType<Required<ServiceProviderAggregationCursor<TSchema>>[K]> {
return (this._cursor[key] as any)(...args);
};
}
80 changes: 80 additions & 0 deletions packages/shell-api/src/deep-inspect-change-stream-wrapper.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import type {
Document,
ResumeToken,
ServiceProviderChangeStream,
} from '@mongosh/service-provider-core';
import type { PickMethodsByReturnType } from './pick-methods-by-return-type';
import { addCustomInspect } from './custom-inspect';

export class DeepInspectChangeStreamWrapper<TSchema = Document>
implements ServiceProviderChangeStream<TSchema>
{
_cursor: ServiceProviderChangeStream<TSchema>;

constructor(cursor: ServiceProviderChangeStream<TSchema>) {
this._cursor = cursor;
}

get resumeToken(): ResumeToken {
return this._cursor.resumeToken;
}

hasNext = forwardedMethod('hasNext');
close = forwardedMethod('close');

next = forwardResultPromise<TSchema, 'next'>('next');
tryNext = forwardResultPromise<TSchema, 'tryNext'>('tryNext');

get closed(): boolean {
return this._cursor.closed;
}

async *[Symbol.asyncIterator]() {
yield* this._cursor;
return;
}
}

function forwardResultPromise<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderChangeStream<TSchema>,
Promise<TSchema | null>
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderChangeStream<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderChangeStream<TSchema>>[K]> {
return async function (
this: DeepInspectChangeStreamWrapper,
...args: Parameters<Required<ServiceProviderChangeStream<TSchema>>[K]>
): // eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore The returntype already contains a promise
ReturnType<Required<ServiceProviderChangeStream<TSchema>>[K]> {
const result = await (this._cursor[key] as any)(...args);
if (result) {
addCustomInspect(result);
}
return result;
};
}

function forwardedMethod<
TSchema,
K extends keyof PickMethodsByReturnType<
ServiceProviderChangeStream<TSchema>,
any
>
>(
key: K
): (
...args: Parameters<Required<ServiceProviderChangeStream<TSchema>>[K]>
) => ReturnType<Required<ServiceProviderChangeStream<TSchema>>[K]> {
return function (
this: DeepInspectChangeStreamWrapper,
...args: Parameters<Required<ServiceProviderChangeStream<TSchema>>[K]>
): ReturnType<Required<ServiceProviderChangeStream<TSchema>>[K]> {
return (this._cursor[key] as any)(...args);
};
}
Loading
Loading