Skip to content

Commit 6200ca4

Browse files
authored
refactor(container-runtime): Enable @typescript-eslint/no-unsafe-argument eslint rule and fix violations (#23697)
Part of a multi-PR process to migrate this package off of our deprecated "minimal" eslint config to our "recommended" one. [AB#3027](https://dev.azure.com/fluidframework/235294da-091d-4c29-84fc-cdfc3d90890b/_workitems/edit/3027)
1 parent e573f4f commit 6200ca4

12 files changed

+62
-41
lines changed

packages/runtime/container-runtime/.eslintrc.cjs

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module.exports = {
4242
ignoreRestArgs: true,
4343
},
4444
],
45+
"@typescript-eslint/no-unsafe-argument": "error",
4546
"@typescript-eslint/no-unsafe-assignment": "error",
4647
"@typescript-eslint/no-unsafe-call": "error",
4748
"@typescript-eslint/no-unsafe-member-access": "error",

packages/runtime/container-runtime/src/containerRuntime.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
ITelemetryBaseLogger,
3939
} from "@fluidframework/core-interfaces";
4040
import {
41+
type IErrorBase,
4142
IFluidHandleContext,
4243
type IFluidHandleInternal,
4344
IProvideFluidHandleContext,
@@ -804,15 +805,14 @@ async function createSummarizer(loader: ILoader, url: string): Promise<ISummariz
804805
// Older containers may not have the "getEntryPoint" API
805806
// ! This check will need to stay until LTS of loader moves past 2.0.0-internal.7.0.0
806807
if (resolvedContainer.getEntryPoint === undefined) {
807-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment
808-
const response = await (resolvedContainer as any).request({
808+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-explicit-any
809+
const response = (await (resolvedContainer as any).request({
809810
url: `/${summarizerRequestUrl}`,
810-
});
811-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
811+
})) as IResponse;
812812
if (response.status !== 200 || response.mimeType !== "fluid/object") {
813813
throw responseToException(response, request);
814814
}
815-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-assignment
815+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
816816
fluidObject = response.value;
817817
} else {
818818
fluidObject = await resolvedContainer.getEntryPoint();
@@ -1236,7 +1236,7 @@ export class ContainerRuntime
12361236
runtime.setConnectionStateCore(true, runtime.delayConnectClientId);
12371237
}
12381238
},
1239-
(error) => runtime.closeFn(error),
1239+
(error: IErrorBase) => runtime.closeFn(error),
12401240
);
12411241

12421242
// Apply stashed ops with a reference sequence number equal to the sequence number of the snapshot,
@@ -2120,7 +2120,7 @@ export class ContainerRuntime
21202120
"summarizerStart",
21212121
"summarizerStartupFailed",
21222122
]) {
2123-
this.summaryManager?.on(eventName, (...args: any[]) => {
2123+
this.summaryManager?.on(eventName, (...args: unknown[]) => {
21242124
this.emit(eventName, ...args);
21252125
});
21262126
}

packages/runtime/container-runtime/src/gc/gcConfigs.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ export function generateGCConfigs(
104104
// Note that if no generation option is provided, Sweep is allowed for any document.
105105
const sweepAllowed = shouldAllowGcSweep(
106106
persistedGcFeatureMatrix ?? {} /* featureMatrix */,
107-
createParams.gcOptions[gcGenerationOptionName] /* currentGeneration */,
107+
createParams.gcOptions[gcGenerationOptionName] as
108+
| number
109+
| undefined /* currentGeneration */,
108110
);
109111

110112
/**

packages/runtime/container-runtime/src/summary/runningSummarizer.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ export class RunningSummarizer
196196
private totalSuccessfulAttempts = 0;
197197
private initialized = false;
198198

199-
private readonly runtimeListener;
199+
private readonly runtimeListener: (
200+
op: ISequencedDocumentMessage,
201+
runtimeMessage?: boolean,
202+
) => void;
200203

201204
/**
202205
* The maximum number of summary attempts to do when submit summary fails.
@@ -956,7 +959,7 @@ export class RunningSummarizer
956959
const { reason, ...summarizeOptions } = options;
957960
if (options.retryOnFailure === true) {
958961
this.summarizeOnDemandWithRetries(`onDemand;${reason}`, resultsBuilder).catch(
959-
(error) => {
962+
(error: IRetriableFailureError) => {
960963
resultsBuilder.fail("summarize failed", error);
961964
},
962965
);

packages/runtime/container-runtime/src/summary/summarizer.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import {
3939
// eslint-disable-next-line import/no-deprecated
4040
ISummarizerRuntime,
4141
ISummarizingWarning,
42+
type IRetriableFailureError,
4243
} from "./summarizerTypes.js";
4344
import { SummaryCollection } from "./summaryCollection.js";
4445
import { SummarizeResultBuilder } from "./summaryGenerator.js";
@@ -369,11 +370,11 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
369370
runCoordinator.stop(stopReason);
370371
this.close();
371372
})
372-
.catch((error) => {
373+
.catch((error: IRetriableFailureError) => {
373374
builder.fail("Failed to start summarizer", error);
374375
});
375376
})
376-
.catch((error) => {
377+
.catch((error: IRetriableFailureError) => {
377378
builder.fail("Failed to create cancellation token", error);
378379
});
379380

@@ -402,11 +403,11 @@ export class Summarizer extends TypedEventEmitter<ISummarizerEvents> implements
402403

403404
private setupForwardedEvents(): void {
404405
for (const event of ["summarize", "summarizeAllAttemptsFailed"]) {
405-
const listener = (...args: any[]): void => {
406+
const listener = (...args: unknown[]): void => {
406407
this.emit(event, ...args);
407408
};
408409
// TODO: better typing here
409-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
410+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
410411
this.runningSummarizer?.on(event as any, listener);
411412
this.forwardedEvents.set(event, listener);
412413
}

packages/runtime/container-runtime/src/summary/summarizerNode/summarizerNodeWithGc.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ export class SummarizerNodeWithGC extends SummarizerNode implements IRootSummari
427427
const pendingSummaryWithGC = pendingSummary as PendingSummaryInfoWithGC;
428428
if (pendingSummaryWithGC.serializedUsedRoutes !== undefined) {
429429
const childNodeUsedRoutes = unpackChildNodesUsedRoutes(
430-
JSON.parse(pendingSummaryWithGC.serializedUsedRoutes),
430+
JSON.parse(pendingSummaryWithGC.serializedUsedRoutes) as string[],
431431
);
432432
const newSerializedRoutes = childNodeUsedRoutes.get(id) ?? [""];
433433
const childPendingSummaryInfo = {

packages/runtime/container-runtime/src/summary/summaryGenerator.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,13 @@ export class SummaryGenerator {
242242
summaryOptions: ISubmitSummaryOptions,
243243
resultsBuilder = new SummarizeResultBuilder(),
244244
): ISummarizeResults {
245-
this.summarizeCore(summaryOptions, resultsBuilder).catch((error) => {
246-
const message = "UnexpectedSummarizeError";
247-
summaryOptions.summaryLogger.sendErrorEvent({ eventName: message }, error);
248-
resultsBuilder.fail(message, error);
249-
});
245+
this.summarizeCore(summaryOptions, resultsBuilder).catch(
246+
(error: IRetriableFailureError) => {
247+
const message = "UnexpectedSummarizeError";
248+
summaryOptions.summaryLogger.sendErrorEvent({ eventName: message }, error);
249+
resultsBuilder.fail(message, error);
250+
},
251+
);
250252

251253
return resultsBuilder.build();
252254
}

packages/runtime/container-runtime/src/summary/summaryManager.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ export class SummaryManager
408408
}
409409

410410
if (delayMs > 0) {
411-
let timer;
411+
let timer: number | undefined;
412412
let resolveOpPromiseFn: (value: void | PromiseLike<void>) => void;
413413
// Create a listener that will break the delay if we've exceeded the initial delay ops count.
414414
const opsListenerFn = (): void => {
@@ -466,19 +466,19 @@ export class SummaryManager
466466
"summarizerStart",
467467
"summarizerStartupFailed",
468468
]) {
469-
const listener = (...args: any[]): void => {
469+
const listener = (...args: unknown[]): void => {
470470
this.emit(event, ...args);
471471
};
472472
// TODO: better typing here
473-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
473+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
474474
this.summarizer?.on(event as any, listener);
475475
this.forwardedEvents.set(event, listener);
476476
}
477477
}
478478

479479
private cleanupForwardedEvents(): void {
480480
for (const [event, listener] of this.forwardedEvents.entries()) {
481-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
481+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
482482
this.summarizer?.off(event as any, listener);
483483
}
484484
this.forwardedEvents.clear();

packages/runtime/container-runtime/src/test/blobManager.spec.ts

+14-8
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export class MockRuntime
121121

122122
public getStorage(): IDocumentStorageService {
123123
return {
124-
createBlob: async (blob) => {
124+
createBlob: async (blob: ArrayBufferLike) => {
125125
if (this.processing) {
126126
return this.storage.createBlob(blob);
127127
}
@@ -139,7 +139,7 @@ export class MockRuntime
139139
this.blobPs.push(P);
140140
return P;
141141
},
142-
readBlob: async (id) => this.storage.readBlob(id),
142+
readBlob: async (id: string) => this.storage.readBlob(id),
143143
} as unknown as IDocumentStorageService;
144144
}
145145

@@ -234,7 +234,7 @@ export class MockRuntime
234234
redirectTable: [string, string][] | undefined;
235235
}> {
236236
if (this.detachedStorage.blobs.size > 0) {
237-
const table = new Map();
237+
const table = new Map<string, string>();
238238
for (const [detachedId, blob] of this.detachedStorage.blobs) {
239239
const { id } = await this.attachedStorage.createBlob(blob);
240240
table.set(detachedId, id);
@@ -259,7 +259,7 @@ export class MockRuntime
259259
for (const op of ops) {
260260
// TODO: better typing
261261
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
262-
this.blobManager.reSubmit((op as any).metadata);
262+
this.blobManager.reSubmit((op as any).metadata as Record<string, unknown> | undefined);
263263
}
264264
}
265265

@@ -319,7 +319,11 @@ export const validateSummary = (
319319
assert.strictEqual(key, redirectTableBlobName);
320320
assert(attachment.type === SummaryType.Blob);
321321
assert(typeof attachment.content === "string");
322-
redirectTable = [...new Map<string, string>(JSON.parse(attachment.content)).entries()];
322+
redirectTable = [
323+
...new Map<string, string>(
324+
JSON.parse(attachment.content) as [string, string][],
325+
).entries(),
326+
];
323327
}
324328
}
325329
return { ids, redirectTable };
@@ -869,7 +873,7 @@ describe("BlobManager", () => {
869873
await runtime.attach();
870874
await runtime.connect();
871875
const ac = new AbortController();
872-
let handleP;
876+
let handleP: Promise<IFluidHandleInternal<ArrayBufferLike>> | undefined;
873877
try {
874878
const blob = IsoBuffer.from("blob", "utf8");
875879
handleP = runtime.createBlob(blob, ac.signal);
@@ -922,7 +926,7 @@ describe("BlobManager", () => {
922926
await runtime.attach();
923927
await runtime.connect();
924928
const ac = new AbortController();
925-
let handleP;
929+
let handleP: Promise<IFluidHandleInternal<ArrayBufferLike>> | undefined;
926930
try {
927931
handleP = runtime.createBlob(IsoBuffer.from("blob", "utf8"), ac.signal);
928932
const p1 = runtime.processBlobs(true);
@@ -945,7 +949,9 @@ describe("BlobManager", () => {
945949
}
946950
await runtime.connect();
947951
runtime.processOps();
948-
await assert.rejects(handleP);
952+
953+
// TODO: `handleP` can be `undefined`; this should be made safer.
954+
await assert.rejects(handleP as Promise<IFluidHandleInternal<ArrayBufferLike>>);
949955
const summaryData = validateSummary(runtime);
950956
assert.strictEqual(summaryData.ids.length, 0);
951957
assert.strictEqual(summaryData.redirectTable, undefined);

packages/runtime/container-runtime/src/test/containerRuntime.spec.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,17 @@ describe("Runtime", () => {
425425

426426
if (enableOfflineLoad) {
427427
assert(
428-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
429-
batchIdMatchesUnsentFormat(submittedOps[0].metadata?.batchId),
428+
batchIdMatchesUnsentFormat(
429+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
430+
submittedOps[0].metadata?.batchId as string | undefined,
431+
),
430432
"expected unsent batchId format (0)",
431433
);
432434
assert(
433-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
434-
batchIdMatchesUnsentFormat(submittedOps[1].metadata?.batchId),
435+
batchIdMatchesUnsentFormat(
436+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
437+
submittedOps[1].metadata?.batchId as string | undefined,
438+
),
435439
"expected unsent batchId format (0)",
436440
);
437441
} else {
@@ -1700,9 +1704,9 @@ describe("Runtime", () => {
17001704
it("summary fails after generate if there are pending ops", async () => {
17011705
// Patch the summarize function to submit messages during it. This way there will be pending
17021706
// messages after generating the summary.
1703-
const patch = (fn: (...args) => Promise<ISummaryTreeWithStats>) => {
1707+
const patch = (fn: (...args: any[]) => Promise<ISummaryTreeWithStats>) => {
17041708
const boundFn = fn.bind(containerRuntime);
1705-
return async (...args: any[]) => {
1709+
return async (...args: unknown[]) => {
17061710
// Submit an op and yield for it to be flushed from outbox to pending state manager.
17071711
submitDataStoreOp(containerRuntime, "fakeId", "fakeContents");
17081712
await yieldEventLoop();
@@ -2312,7 +2316,7 @@ describe("Runtime", () => {
23122316
JSON.stringify(
23132317
'{"gcNodes":{"/":{"outboundRoutes":["/rootDOId"]},"/rootDOId":{"outboundRoutes":["/rootDOId/de68ca53-be31-479e-8d34-a267958997e4","/rootDOId/root"]},"/rootDOId/de68ca53-be31-479e-8d34-a267958997e4":{"outboundRoutes":["/rootDOId"]},"/rootDOId/root":{"outboundRoutes":["/rootDOId","/rootDOId/de68ca53-be31-479e-8d34-a267958997e4"]}}}',
23142318
),
2315-
),
2319+
) as string,
23162320
"utf8",
23172321
),
23182322
],

packages/runtime/container-runtime/src/test/opLifecycle/duplicateBatchDetector.spec.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ describe("DuplicateBatchDetector", () => {
146146
detector.processInboundBatch(inboundBatch1);
147147

148148
const summaryPayload = JSON.stringify(detector.getRecentBatchInfoForSummary());
149-
const detector2 = new DuplicateBatchDetector(JSON.parse(summaryPayload));
149+
const detector2 = new DuplicateBatchDetector(
150+
JSON.parse(summaryPayload) as [number, string][] | undefined,
151+
);
150152

151153
const inboundBatch2 = makeBatch({
152154
sequenceNumber: seqNum++, // 2

packages/runtime/container-runtime/src/test/summary/runningSummarizer.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ describe("Runtime", () => {
11641164
"should be nack",
11651165
);
11661166
assert(
1167-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
1167+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
11681168
JSON.parse((ackNackResult.data.summaryNackOp as any).data).message === "test-nack",
11691169
"summary nack error should be test-nack",
11701170
);

0 commit comments

Comments
 (0)