From 7f5787796b8c26e85c3a58c347ba6c089b70dc86 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Wed, 27 Nov 2024 09:56:19 -0500 Subject: [PATCH 01/19] Update eslint config --- packages/dds/shared-object-base/.eslintrc.cjs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/dds/shared-object-base/.eslintrc.cjs b/packages/dds/shared-object-base/.eslintrc.cjs index 3514825ac914..7e30fd05f3d8 100644 --- a/packages/dds/shared-object-base/.eslintrc.cjs +++ b/packages/dds/shared-object-base/.eslintrc.cjs @@ -4,10 +4,7 @@ */ module.exports = { - extends: [ - require.resolve("@fluidframework/eslint-config-fluid/minimal-deprecated"), - "prettier", - ], + extends: [require.resolve("@fluidframework/eslint-config-fluid/recommended"), "prettier"], parserOptions: { project: ["./tsconfig.json", "./src/test/tsconfig.json"], }, From 8e94624619c9c0f752c7febaa9821823a9f739b6 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:01:49 -0500 Subject: [PATCH 02/19] Automated fixes (some manual adjustment) --- .../shared-object-base/src/sharedObject.ts | 10 +++--- .../src/summarySerializer.ts | 2 +- .../attachingBindingAndConnecting.spec.ts | 32 +++++++++---------- .../src/test/createSharedObjectKind.spec.ts | 2 +- .../src/test/serializer.spec.ts | 20 ++++++------ .../src/test/sharedObject.spec.ts | 2 +- packages/dds/shared-object-base/src/utils.ts | 2 +- 7 files changed, 34 insertions(+), 36 deletions(-) diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 86ceb2e62be6..3434d89536de 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -532,17 +532,17 @@ export abstract class SharedObjectCore< // Should I change the state at the end? So that we *can't* send new stuff before we send old? this._connected = connected; - if (!connected) { + if (connected) { + // Call this for now so that DDSes like ConsensusOrderedCollection that maintain their own pending + // messages will work. + this.onConnect(); + } else { // Things that are true now... // - if we had a connection we can no longer send messages over it // - if we had outbound messages some may or may not be ACK'd. Won't know until next message // // - nack could get a new msn - but might as well do it in the join? this.onDisconnect(); - } else { - // Call this for now so that DDSes like ConsensusOrderedCollection that maintain their own pending - // messages will work. - this.onConnect(); } } diff --git a/packages/dds/shared-object-base/src/summarySerializer.ts b/packages/dds/shared-object-base/src/summarySerializer.ts index aef9fc7af3d6..392f1ddc24ac 100644 --- a/packages/dds/shared-object-base/src/summarySerializer.ts +++ b/packages/dds/shared-object-base/src/summarySerializer.ts @@ -14,7 +14,7 @@ import { FluidSerializer } from "./serializer.js"; export class SummarySerializer extends FluidSerializer { private readonly serializedRoutes: Set = new Set(); public getSerializedRoutes(): string[] { - return Array.from(this.serializedRoutes); + return [...this.serializedRoutes]; } protected serializeHandle(handle: IFluidHandleInternal, bind: IFluidHandleInternal) { diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index 3e3ea55719a6..e5d29ea82f56 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { strict as assert } from "assert"; +import { strict as assert } from "node:assert"; import { TypedEventEmitter } from "@fluid-internal/client-utils"; import { generatePairwiseOptions } from "@fluid-private/test-pairwise-generator"; @@ -137,7 +137,7 @@ describe("SharedObject attaching binding and connecting", () => { }); describe("shared object after creation", () => { - runtimeAttachStateAndConnectedMatrix.forEach(({ connected, attachState }) => + for (const { connected, attachState } of runtimeAttachStateAndConnectedMatrix) { it(`!isAttached and !connected with runtime ${JSON.stringify({ connected, attachState, @@ -151,8 +151,8 @@ describe("SharedObject attaching binding and connecting", () => { assert.strictEqual(sharedObject.isAttached(), false, "!isAttached"); assert.strictEqual(sharedObject.connected, false, "!connected"); - }), - ); + }); + } it("!isAttached with detached transition to attach runtime", () => { const runtimeEvents = new TypedEventEmitter(); @@ -180,7 +180,7 @@ describe("SharedObject attaching binding and connecting", () => { }); describe("shared object after load", () => { - runtimeAttachStateAndConnectedMatrix.forEach(({ connected, attachState }) => + for (const { connected, attachState } of runtimeAttachStateAndConnectedMatrix) { it(`With runtime ${JSON.stringify({ connected, attachState, @@ -225,8 +225,8 @@ describe("SharedObject attaching binding and connecting", () => { connected && sharedObject.isAttached(), "connected", ); - }), - ); + }); + } it("isAttached with detached transition to attach runtime", async () => { const runtimeEvents = new TypedEventEmitter(); @@ -270,7 +270,7 @@ describe("SharedObject attaching binding and connecting", () => { }); describe("shared object after connect", () => { - runtimeAttachStateAndConnectedMatrix.forEach(({ connected, attachState }) => + for (const { connected, attachState } of runtimeAttachStateAndConnectedMatrix) { it(`With runtime ${JSON.stringify({ connected, attachState, @@ -310,8 +310,8 @@ describe("SharedObject attaching binding and connecting", () => { connected && sharedObject.isAttached(), "connected", ); - }), - ); + }); + } it("isAttached with detached transition to attach runtime", async () => { const runtimeEvents = new TypedEventEmitter(); @@ -351,7 +351,7 @@ describe("SharedObject attaching binding and connecting", () => { }); describe("shared object after load and connect", () => { - runtimeAttachStateAndConnectedMatrix.forEach(({ connected, attachState }) => + for (const { connected, attachState } of runtimeAttachStateAndConnectedMatrix) { it(`With runtime ${JSON.stringify({ connected, attachState, @@ -398,12 +398,12 @@ describe("SharedObject attaching binding and connecting", () => { connected && sharedObject.isAttached(), "connected", ); - }), - ); + }); + } }); describe("shared object after bindToContext", () => { - runtimeAttachStateAndConnectedMatrix.forEach(({ connected, attachState }) => + for (const { connected, attachState } of runtimeAttachStateAndConnectedMatrix) { it(`With runtime ${JSON.stringify({ connected, attachState, @@ -450,8 +450,8 @@ describe("SharedObject attaching binding and connecting", () => { connected && sharedObject.isAttached(), "connected", ); - }), - ); + }); + } it("isAttached with detached transition to attach runtime", async () => { const runtimeEvents = new TypedEventEmitter(); diff --git a/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts b/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts index f8de39c128c7..45f9a5d24b09 100644 --- a/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts +++ b/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { strict as assert } from "assert"; +import { strict as assert } from "node:assert"; import type { IFluidLoadable } from "@fluidframework/core-interfaces"; import type { diff --git a/packages/dds/shared-object-base/src/test/serializer.spec.ts b/packages/dds/shared-object-base/src/test/serializer.spec.ts index 809768f43198..9974c4497972 100644 --- a/packages/dds/shared-object-base/src/test/serializer.spec.ts +++ b/packages/dds/shared-object-base/src/test/serializer.spec.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { strict as assert } from "assert"; +import { strict as assert } from "node:assert"; import { RemoteFluidObjectHandle } from "../remoteObjectHandle.js"; import { FluidSerializer } from "../serializer.js"; @@ -15,23 +15,22 @@ describe("FluidSerializer", () => { function printHandle(target: any) { return JSON.stringify(target, (key, value) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return value?.IFluidHandle !== undefined ? "#HANDLE" : value; + return value?.IFluidHandle === undefined ? value : "#HANDLE"; }); } function createNestedCases(testCases: any[]) { - // Add an object where each field references one of the JSON serializable types. testCases.push( + // Add an object where each field references one of the JSON serializable types. testCases.reduce((o, value, index) => { o[`f${index}`] = value; // eslint-disable-next-line @typescript-eslint/no-unsafe-return return o; }, {}), + // Add an array that contains each of our constructed test cases. + [...testCases] ); - // Add an array that contains each of our constructed test cases. - testCases.push([...testCases]); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return testCases; } @@ -45,18 +44,17 @@ describe("FluidSerializer", () => { // are of particular interest. const simple = createNestedCases([false, true, 0, 1, "", "x", null, [], {}]); - // Add an object where each field references one of the JSON serializable types. simple.push( + // Add an object where each field references one of the JSON serializable types. simple.reduce((o, value, index) => { o[`f${index}`] = value; // eslint-disable-next-line @typescript-eslint/no-unsafe-return return o; }, {}), + // Add an array that contains each of our constructed test cases. + [...simple] ); - // Add an array that contains each of our constructed test cases. - simple.push([...simple]); - // Verify that `encode` is a no-op for these simple cases. for (const input of simple) { it(`${printHandle(input)} -> ${JSON.stringify(input)}`, () => { @@ -104,7 +102,7 @@ describe("FluidSerializer", () => { } // Non-finite numbers are coerced to null. Date is coerced to string. - const tricky = createNestedCases([-Infinity, NaN, +Infinity, new Date()]); + const tricky = createNestedCases([Number.NEGATIVE_INFINITY, Number.NaN, +Number.POSITIVE_INFINITY, new Date()]); // Undefined is extra special in that it can't appear at the root, but can appear // embedded in the tree, in which case the key is elided (if an object) or it is diff --git a/packages/dds/shared-object-base/src/test/sharedObject.spec.ts b/packages/dds/shared-object-base/src/test/sharedObject.spec.ts index 8928d4276427..ae708f8565a4 100644 --- a/packages/dds/shared-object-base/src/test/sharedObject.spec.ts +++ b/packages/dds/shared-object-base/src/test/sharedObject.spec.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -import { strict as assert } from "assert"; +import { strict as assert } from "node:assert"; import { IChannelAttributes, diff --git a/packages/dds/shared-object-base/src/utils.ts b/packages/dds/shared-object-base/src/utils.ts index f8c9cad15b6a..900cdbeadc53 100644 --- a/packages/dds/shared-object-base/src/utils.ts +++ b/packages/dds/shared-object-base/src/utils.ts @@ -25,7 +25,7 @@ export function serializeHandles( bind: IFluidHandle, ): string | undefined { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return value !== undefined ? serializer.stringify(value, bind) : value; + return value === undefined ? value : serializer.stringify(value, bind); } /** From 0dbb9b369df1d155ba0312a671ce944caacff253 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:29:39 -0500 Subject: [PATCH 03/19] Fixing lint violations - part 1 --- .../shared-object-base.legacy.alpha.api.md | 6 ++-- .../shared-object-base/src/sharedObject.ts | 32 +++++++++---------- .../src/test/createSharedObjectKind.spec.ts | 2 +- .../src/test/serializer.spec.ts | 11 +++++-- .../src/test/sharedObject.spec.ts | 18 +++++------ .../dds/shared-object-base/src/test/utils.ts | 10 +++--- packages/dds/shared-object-base/src/utils.ts | 15 ++++----- 7 files changed, 49 insertions(+), 45 deletions(-) diff --git a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md index 7adbb4b97953..86719f29cb02 100644 --- a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md +++ b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md @@ -33,10 +33,10 @@ export interface ISharedObjectKind { } // @alpha -export function makeHandlesSerializable(value: any, serializer: IFluidSerializer, bind: IFluidHandle): any; +export function makeHandlesSerializable(value: unknown, serializer: IFluidSerializer, bind: IFluidHandle): any; // @alpha -export function parseHandles(value: any, serializer: IFluidSerializer): any; +export function parseHandles(value: unknown, serializer: IFluidSerializer): any; // @alpha export abstract class SharedObject extends SharedObjectCore { @@ -79,7 +79,7 @@ export abstract class SharedObjectCore(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise; protected onConnect(): void; protected abstract onDisconnect(): any; - protected abstract processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): any; + protected abstract processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void; protected reSubmitCore(content: any, localOpMetadata: unknown): void; protected rollback(content: any, localOpMetadata: unknown): void; // (undocumented) diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 3434d89536de..6d43e03281f7 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -217,7 +217,7 @@ export abstract class SharedObjectCore< * would result in same error thrown. If called multiple times, only first error is remembered. * @param error - error object that is thrown whenever an attempt is made to modify this object */ - private closeWithError(error: any) { + private closeWithError(error: any): void { if (this.closeError === undefined) { this.closeError = error; } @@ -226,7 +226,7 @@ export abstract class SharedObjectCore< /** * Verifies that this object is not closed via closeWithError(). If it is, throws an error used to close it. */ - private verifyNotClosed() { + private verifyNotClosed(): void { if (this.closeError !== undefined) { throw this.closeError; } @@ -242,7 +242,7 @@ export abstract class SharedObjectCore< * DDS state does not match what user sees. Because of it DDS moves to "corrupted state" and does not * allow processing of ops or local changes, which very quickly results in container closure. */ - private eventListenerErrorHandler(event: EventEmitterEventType, e: any) { + private eventListenerErrorHandler(event: EventEmitterEventType, e: unknown): void { const error = DataProcessingError.wrapIfUnrecognized( e, "SharedObjectEventListenerException", @@ -253,7 +253,7 @@ export abstract class SharedObjectCore< throw error; } - private setBoundAndHandleAttach() { + private setBoundAndHandleAttach(): void { // Ensure didAttach is only called once, and we only register a single event // but we still call setConnectionState as our existing mocks don't // always propagate connection state @@ -312,7 +312,7 @@ export abstract class SharedObjectCore< /** * {@inheritDoc @fluidframework/datastore-definitions#(IChannel:interface).connect} */ - public connect(services: IChannelServices) { + public connect(services: IChannelServices): void { // handle the case where load is called // before connect; loading detached data stores if (this.services === undefined) { @@ -362,7 +362,7 @@ export abstract class SharedObjectCore< /** * Allows the distributed data type to perform custom local loading. */ - protected initializeLocalCore() { + protected initializeLocalCore(): void { return; } @@ -370,7 +370,7 @@ export abstract class SharedObjectCore< * Allows the distributive data type the ability to perform custom processing once an attach has happened. * Also called after non-local data type get loaded. */ - protected didAttach() { + protected didAttach(): void { return; } @@ -385,7 +385,7 @@ export abstract class SharedObjectCore< message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown, - ); + ): void; /** * Called when the object has disconnected from the delta stream. @@ -433,7 +433,7 @@ export abstract class SharedObjectCore< * Called when the object has fully connected to the delta stream * Default implementation for DDS, override if different behavior is required. */ - protected onConnect() {} + protected onConnect(): void {} /** * Called when a message has to be resubmitted. This typically happens after a reconnection for unacked messages. @@ -443,7 +443,7 @@ export abstract class SharedObjectCore< * @param content - The content of the original message. * @param localOpMetadata - The local metadata associated with the original message. */ - protected reSubmitCore(content: any, localOpMetadata: unknown) { + protected reSubmitCore(content: any, localOpMetadata: unknown): void { this.submitLocalMessage(content, localOpMetadata); } @@ -479,7 +479,7 @@ export abstract class SharedObjectCore< }); } - private attachDeltaHandler() { + private attachDeltaHandler(): void { // Services should already be there in case we are attaching delta handler. assert( this.services !== undefined, @@ -520,7 +520,7 @@ export abstract class SharedObjectCore< * Set the state of connection to services. * @param connected - true if connected, false otherwise. */ - private setConnectionState(connected: boolean) { + private setConnectionState(connected: boolean): void { // only an attached shared object can transition its // connected state. This is defensive, as some // of our test harnesses don't handle this correctly @@ -557,7 +557,7 @@ export abstract class SharedObjectCore< message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown, - ) { + ): void { this.verifyNotClosed(); // This will result in container closure. this.emitInternal("pre-op", message, local, this); @@ -581,7 +581,7 @@ export abstract class SharedObjectCore< * Process messages for this shared object. The messages here are contiguous messages for this object in a batch. * @param messageCollection - The collection of messages to process. */ - private processMessages(messagesCollection: IRuntimeMessageCollection) { + private processMessages(messagesCollection: IRuntimeMessageCollection): void { const { envelope, messagesContent, local } = messagesCollection; for (const { contents, localOpMetadata, clientSequenceNumber } of messagesContent) { this.process( @@ -602,14 +602,14 @@ export abstract class SharedObjectCore< * @param content - The content of the original message. * @param localOpMetadata - The local metadata associated with the original message. */ - private reSubmit(content: any, localOpMetadata: unknown) { + private reSubmit(content: unknown, localOpMetadata: unknown): void { this.reSubmitCore(content, localOpMetadata); } /** * Revert an op */ - protected rollback(content: any, localOpMetadata: unknown) { + protected rollback(content: any, localOpMetadata: unknown): void { throw new Error("rollback not supported"); } diff --git a/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts b/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts index 45f9a5d24b09..f89797443230 100644 --- a/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts +++ b/packages/dds/shared-object-base/src/test/createSharedObjectKind.spec.ts @@ -54,7 +54,7 @@ describe("createSharedObjectKind's return type", () => { const runtime = new MockFluidDataStoreRuntime(); runtime.createChannel = (id: string | undefined, type: string) => { createChannelCalls.push([id, type]); - return null as unknown as IChannel; + return undefined as unknown as IChannel; }; SharedFoo.create(runtime); assert.deepEqual(createChannelCalls, [[undefined, SharedFooFactory.Type]]); diff --git a/packages/dds/shared-object-base/src/test/serializer.spec.ts b/packages/dds/shared-object-base/src/test/serializer.spec.ts index 9974c4497972..ec055267bb2b 100644 --- a/packages/dds/shared-object-base/src/test/serializer.spec.ts +++ b/packages/dds/shared-object-base/src/test/serializer.spec.ts @@ -28,7 +28,7 @@ describe("FluidSerializer", () => { return o; }, {}), // Add an array that contains each of our constructed test cases. - [...testCases] + [...testCases], ); // eslint-disable-next-line @typescript-eslint/no-unsafe-return @@ -52,7 +52,7 @@ describe("FluidSerializer", () => { return o; }, {}), // Add an array that contains each of our constructed test cases. - [...simple] + [...simple], ); // Verify that `encode` is a no-op for these simple cases. @@ -102,7 +102,12 @@ describe("FluidSerializer", () => { } // Non-finite numbers are coerced to null. Date is coerced to string. - const tricky = createNestedCases([Number.NEGATIVE_INFINITY, Number.NaN, +Number.POSITIVE_INFINITY, new Date()]); + const tricky = createNestedCases([ + Number.NEGATIVE_INFINITY, + Number.NaN, + +Number.POSITIVE_INFINITY, + new Date(), + ]); // Undefined is extra special in that it can't appear at the root, but can appear // embedded in the tree, in which case the key is elided (if an object) or it is diff --git a/packages/dds/shared-object-base/src/test/sharedObject.spec.ts b/packages/dds/shared-object-base/src/test/sharedObject.spec.ts index ae708f8565a4..2dc20be22a37 100644 --- a/packages/dds/shared-object-base/src/test/sharedObject.spec.ts +++ b/packages/dds/shared-object-base/src/test/sharedObject.spec.ts @@ -40,13 +40,13 @@ class MySharedObject extends SharedObject { message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown, - ) { + ): void { throw new Error("Method not implemented."); } - protected onDisconnect() { + protected onDisconnect(): void { throw new Error("Method not implemented."); } - protected applyStashedOp(content: any): unknown { + protected applyStashedOp(content: unknown): void { throw new Error("Method not implemented."); } } @@ -60,7 +60,7 @@ class MySharedObjectCore extends SharedObjectCore { ); } - protected readonly serializer = {} as any as IFluidSerializer; + protected readonly serializer = {} as unknown as IFluidSerializer; protected summarizeCore(serializer: IFluidSerializer): ISummaryTreeWithStats { throw new Error("Method not implemented."); @@ -72,13 +72,13 @@ class MySharedObjectCore extends SharedObjectCore { message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown, - ) { + ): void { throw new Error("Method not implemented."); } - protected onDisconnect() { + protected onDisconnect(): void { throw new Error("Method not implemented."); } - protected applyStashedOp(content: any): unknown { + protected applyStashedOp(content: unknown): void { throw new Error("Method not implemented."); } public getAttachSummary(fullTree?: boolean, trackState?: boolean): ISummaryTreeWithStats { @@ -98,7 +98,7 @@ class MySharedObjectCore extends SharedObjectCore { describe("SharedObject", () => { it("rejects slashes in id", () => { const invalidId = "beforeSlash/afterSlash"; - const codeBlock = () => new MySharedObject(invalidId); + const codeBlock = (): SharedObject => new MySharedObject(invalidId); assert.throws(codeBlock, (e: Error) => validateAssertionError(e, "Id cannot contain slashes"), ); @@ -108,7 +108,7 @@ describe("SharedObject", () => { describe("SharedObjectCore", () => { it("rejects slashes in id", () => { const invalidId = "beforeSlash/afterSlash"; - const codeBlock = () => new MySharedObjectCore(invalidId); + const codeBlock = (): SharedObjectCore => new MySharedObjectCore(invalidId); assert.throws(codeBlock, (e: Error) => validateAssertionError(e, "Id cannot contain slashes"), ); diff --git a/packages/dds/shared-object-base/src/test/utils.ts b/packages/dds/shared-object-base/src/test/utils.ts index a1db1747f3d2..ee0f3988c3e9 100644 --- a/packages/dds/shared-object-base/src/test/utils.ts +++ b/packages/dds/shared-object-base/src/test/utils.ts @@ -4,13 +4,13 @@ */ import { IRequest } from "@fluidframework/core-interfaces"; -import { IFluidHandleContext } from "@fluidframework/core-interfaces/internal"; +import { IFluidHandleContext, type IResponse } from "@fluidframework/core-interfaces/internal"; import { Serializable } from "@fluidframework/datastore-definitions/internal"; import { create404Response } from "@fluidframework/runtime-utils/internal"; export class MockHandleContext implements IFluidHandleContext { public isAttached = false; - public get IFluidHandleContext() { + public get IFluidHandleContext(): IFluidHandleContext { return this; } @@ -23,11 +23,11 @@ export class MockHandleContext implements IFluidHandleContext { public readonly routeContext?: IFluidHandleContext, ) {} - public attachGraph() { + public attachGraph(): void { throw new Error("Method not implemented."); } - public async resolveHandle(request: IRequest) { + public async resolveHandle(request: IRequest): Promise { return create404Response(request); } } @@ -40,7 +40,7 @@ export function makeJson( breadth: number, depth: number, createLeaf: () => Serializable, -) { +): unknown { let depthInternal = depth; if (--depthInternal === 0) { return createLeaf(); diff --git a/packages/dds/shared-object-base/src/utils.ts b/packages/dds/shared-object-base/src/utils.ts index 900cdbeadc53..f22ac670ff16 100644 --- a/packages/dds/shared-object-base/src/utils.ts +++ b/packages/dds/shared-object-base/src/utils.ts @@ -20,11 +20,10 @@ import { IFluidSerializer } from "./serializer.js"; * @internal */ export function serializeHandles( - value: any, + value: unknown, serializer: IFluidSerializer, bind: IFluidHandle, ): string | undefined { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value === undefined ? value : serializer.stringify(value, bind); } @@ -43,11 +42,11 @@ export function serializeHandles( * @alpha */ export function makeHandlesSerializable( - value: any, + value: unknown, serializer: IFluidSerializer, bind: IFluidHandle, -) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) +): any { return serializer.encode(value, bind); } @@ -62,8 +61,8 @@ export function makeHandlesSerializable( * @legacy * @alpha */ -export function parseHandles(value: any, serializer: IFluidSerializer) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) +export function parseHandles(value: unknown, serializer: IFluidSerializer): any { return serializer.decode(value); } @@ -89,7 +88,7 @@ export function createSingleBlobSummary( * @internal */ export function bindHandles( - value: any, + value: unknown, serializer: IFluidSerializer, bind: IFluidHandle, ): void { From cc39f9a3d48a9c5dcd21b275c7437f755bdcbffd Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Wed, 27 Nov 2024 10:53:59 -0500 Subject: [PATCH 04/19] Fixing lint violations - part 2 --- .../shared-object-base.legacy.alpha.api.md | 6 +-- .../src/remoteObjectHandle.ts | 4 -- .../dds/shared-object-base/src/serializer.ts | 54 +++++++++++-------- .../src/summarySerializer.ts | 6 ++- .../src/test/serializer.spec.ts | 8 ++- 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md index 86719f29cb02..5e0fadf2d881 100644 --- a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md +++ b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md @@ -6,10 +6,10 @@ // @alpha (undocumented) export interface IFluidSerializer { - decode(input: any): any; - encode(value: any, bind: IFluidHandle): any; + decode(input: unknown): any; + encode(value: unknown, bind: IFluidHandle): any; parse(value: string): any; - stringify(value: any, bind: IFluidHandle): string; + stringify(value: unknown, bind: IFluidHandle): string; } // @alpha diff --git a/packages/dds/shared-object-base/src/remoteObjectHandle.ts b/packages/dds/shared-object-base/src/remoteObjectHandle.ts index 345ca4a927f8..58cc1a224ad2 100644 --- a/packages/dds/shared-object-base/src/remoteObjectHandle.ts +++ b/packages/dds/shared-object-base/src/remoteObjectHandle.ts @@ -20,10 +20,6 @@ import { FluidHandleBase, responseToException } from "@fluidframework/runtime-ut * IFluidHandle can be retrieved by calling `get` on it. */ export class RemoteFluidObjectHandle extends FluidHandleBase { - public get IFluidHandleContext() { - return this; - } - public readonly isAttached = true; private objectP: Promise | undefined; diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index 02e92e9e6f05..290c55c9d25e 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -11,11 +11,13 @@ import { IFluidHandleContext, type IFluidHandleInternal, } from "@fluidframework/core-interfaces/internal"; +import { assert } from "@fluidframework/core-utils/internal"; import { generateHandleContextPath, isSerializedHandle, isFluidHandle, toFluidHandleInternal, + type ISerializedHandle, } from "@fluidframework/runtime-utils/internal"; import { RemoteFluidObjectHandle } from "./remoteObjectHandle.js"; @@ -32,7 +34,8 @@ export interface IFluidSerializer { * The original `input` object is not mutated. This method will shallowly clones all objects in the path from * the root to any replaced handles. (If no handles are found, returns the original object.) */ - encode(value: any, bind: IFluidHandle): any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + encode(value: unknown, bind: IFluidHandle): any; /** * Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an @@ -43,17 +46,19 @@ export interface IFluidSerializer { * * The decoded handles are implicitly bound to the handle context of this serializer. */ - decode(input: any): any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + decode(input: unknown): any; /** * Stringifies a given value. Converts any IFluidHandle to its stringified equivalent. */ - stringify(value: any, bind: IFluidHandle): string; + stringify(value: unknown, bind: IFluidHandle): string; /** * Parses the given JSON input string and returns the JavaScript object defined by it. Any Fluid * handles will be realized as part of the parse */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) parse(value: string): any; } @@ -71,7 +76,7 @@ export class FluidSerializer implements IFluidSerializer { } } - public get IFluidSerializer() { + public get IFluidSerializer(): IFluidSerializer { return this; } @@ -84,7 +89,7 @@ export class FluidSerializer implements IFluidSerializer { * * Any unbound handles encountered are bound to the provided IFluidHandle. */ - public encode(input: any, bind: IFluidHandle) { + public encode(input: unknown, bind: IFluidHandleInternal): unknown { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions @@ -102,7 +107,7 @@ export class FluidSerializer implements IFluidSerializer { * * The decoded handles are implicitly bound to the handle context of this serializer. */ - public decode(input: any) { + public decode(input: unknown): unknown { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions @@ -111,28 +116,30 @@ export class FluidSerializer implements IFluidSerializer { : input; } - public stringify(input: unknown, bind: IFluidHandle) { + public stringify(input: unknown, bind: IFluidHandle): string { const bindInternal = toFluidHandleInternal(bind); return JSON.stringify(input, (key, value) => this.encodeValue(value, bindInternal)); } // Parses the serialized data - context must match the context with which the JSON was stringified - public parse(input: string) { + public parse(input: string): unknown { return JSON.parse(input, (key, value) => this.decodeValue(value)); } // If the given 'value' is an IFluidHandle, returns the encoded IFluidHandle. // Otherwise returns the original 'value'. Used by 'encode()' and 'stringify()'. - private readonly encodeValue = (value: unknown, bind: IFluidHandleInternal) => { + private readonly encodeValue = (value: unknown, bind?: IFluidHandleInternal): unknown => { // If 'value' is an IFluidHandle return its encoded form. - return isFluidHandle(value) - ? this.serializeHandle(toFluidHandleInternal(value), bind) - : value; + if (isFluidHandle(value)) { + assert(bind !== undefined, "Cannot encode a handle without a bind context"); + return this.serializeHandle(toFluidHandleInternal(value), bind); + } + return value; }; // If the given 'value' is an encoded IFluidHandle, returns the decoded IFluidHandle. // Otherwise returns the original 'value'. Used by 'decode()' and 'parse()'. - private readonly decodeValue = (value: any) => { + private readonly decodeValue = (value: unknown): unknown => { // If 'value' is a serialized IFluidHandle return the deserialized result. if (isSerializedHandle(value)) { // Old documents may have handles with relative path in their summaries. Convert these to absolute @@ -150,11 +157,11 @@ export class FluidSerializer implements IFluidSerializer { // Invoked for non-null objects to recursively replace references to IFluidHandles. // Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present, // returns the original `input`. - private recursivelyReplace( - input: any, - replacer: (input: any, context: any) => any, - context?: any, - ) { + private recursivelyReplace( + input: object, + replacer: (input: unknown, context?: T) => unknown, + context?: T, + ): unknown { // Note: Caller is responsible for ensuring that `input` is defined / non-null. // (Required for Object.keys() below.) @@ -171,7 +178,7 @@ export class FluidSerializer implements IFluidSerializer { // Otherwise descend into the object graph looking for IFluidHandle instances. let clone: object | undefined; for (const key of Object.keys(input)) { - const value = input[key]; + const value: unknown = input[key]; // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions if (!!value && typeof value === "object") { // Note: Except for IFluidHandle, `input` must not contain circular references (as object must @@ -184,18 +191,21 @@ export class FluidSerializer implements IFluidSerializer { // current property is replaced by the `replaced` value. if (replaced !== value) { // Lazily create a shallow clone of the `input` object if we haven't done so already. + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: not sure if there's a good solution clone = clone ?? (Array.isArray(input) ? [...input] : { ...input }); // Overwrite the current property `key` in the clone with the `replaced` value. - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - clone![key] = replaced; + clone[key] = replaced; } } } return clone ?? input; } - protected serializeHandle(handle: IFluidHandleInternal, bind: IFluidHandleInternal) { + protected serializeHandle( + handle: IFluidHandleInternal, + bind: IFluidHandleInternal, + ): ISerializedHandle { bind.bind(handle); return { type: "__fluid_handle__", diff --git a/packages/dds/shared-object-base/src/summarySerializer.ts b/packages/dds/shared-object-base/src/summarySerializer.ts index 392f1ddc24ac..f25297c9aac6 100644 --- a/packages/dds/shared-object-base/src/summarySerializer.ts +++ b/packages/dds/shared-object-base/src/summarySerializer.ts @@ -4,6 +4,7 @@ */ import { type IFluidHandleInternal } from "@fluidframework/core-interfaces/internal"; +import type { ISerializedHandle } from "@fluidframework/runtime-utils/internal"; import { FluidSerializer } from "./serializer.js"; @@ -17,7 +18,10 @@ export class SummarySerializer extends FluidSerializer { return [...this.serializedRoutes]; } - protected serializeHandle(handle: IFluidHandleInternal, bind: IFluidHandleInternal) { + protected serializeHandle( + handle: IFluidHandleInternal, + bind: IFluidHandleInternal, + ): ISerializedHandle { this.serializedRoutes.add(handle.absolutePath); return super.serializeHandle(handle, bind); } diff --git a/packages/dds/shared-object-base/src/test/serializer.spec.ts b/packages/dds/shared-object-base/src/test/serializer.spec.ts index ec055267bb2b..cd6d6055a939 100644 --- a/packages/dds/shared-object-base/src/test/serializer.spec.ts +++ b/packages/dds/shared-object-base/src/test/serializer.spec.ts @@ -282,7 +282,9 @@ describe("FluidSerializer", () => { }); // Parse a handle whose url is absolute path. - const parsedHandle: RemoteFluidObjectHandle = serializer.parse(serializedHandle); + const parsedHandle: RemoteFluidObjectHandle = serializer.parse( + serializedHandle, + ) as RemoteFluidObjectHandle; assert.strictEqual( parsedHandle.absolutePath, "/default/sharedDDS", @@ -303,7 +305,9 @@ describe("FluidSerializer", () => { // Parse a handle whose url is a path relative to its route context. The serializer will generate absolute // path for the handle and create a handle with it. - const parsedHandle: RemoteFluidObjectHandle = serializer.parse(serializedHandle); + const parsedHandle: RemoteFluidObjectHandle = serializer.parse( + serializedHandle, + ) as RemoteFluidObjectHandle; assert.strictEqual( parsedHandle.absolutePath, "/default/sharedDDS", From b655875d78eff68497991521ca4e9751ede7f61c Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:32:01 -0500 Subject: [PATCH 05/19] Fixing lint violations - part 3 --- .../shared-object-base.legacy.alpha.api.md | 4 +- .../shared-object-base/src/sharedObject.ts | 16 ++--- .../attachingBindingAndConnecting.spec.ts | 71 ++++++++++--------- 3 files changed, 49 insertions(+), 42 deletions(-) diff --git a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md index 5e0fadf2d881..177d89e6968d 100644 --- a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md +++ b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md @@ -53,7 +53,7 @@ export abstract class SharedObject extends EventEmitterWithErrorHandling implements ISharedObject { constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes); - protected abstract applyStashedOp(content: any): void; + protected abstract applyStashedOp(content: unknown): void; // (undocumented) readonly attributes: IChannelAttributes; bindToContext(): void; @@ -62,7 +62,7 @@ export abstract class SharedObjectCore; protected didAttach(): void; protected dirty(): void; - emit(event: EventEmitterEventType, ...args: any[]): boolean; + emit(event: EventEmitterEventType, ...args: unknown[]): boolean; abstract getAttachSummary(fullTree?: boolean, trackState?: boolean, telemetryContext?: ITelemetryContext): ISummaryTreeWithStats; abstract getGCData(fullGC?: boolean): IGarbageCollectionData; readonly handle: IFluidHandleInternal; diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 6d43e03281f7..158f65db58d1 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -504,13 +504,13 @@ export abstract class SharedObjectCore< setConnectionState: (connected: boolean) => { this.setConnectionState(connected); }, - reSubmit: (content: any, localOpMetadata: unknown) => { + reSubmit: (content: unknown, localOpMetadata: unknown) => { this.reSubmit(content, localOpMetadata); }, - applyStashedOp: (content: any): void => { + applyStashedOp: (content: unknown): void => { this.applyStashedOp(parseHandles(content, this.serializer)); }, - rollback: (content: any, localOpMetadata: unknown) => { + rollback: (content: unknown, localOpMetadata: unknown) => { this.rollback(content, localOpMetadata); }, } satisfies IDeltaHandler); @@ -630,7 +630,7 @@ export abstract class SharedObjectCore< * * @param content - Contents of a stashed op. */ - protected abstract applyStashedOp(content: any): void; + protected abstract applyStashedOp(content: unknown): void; /** * Emit an event. This function is only intended for use by DDS classes that extend SharedObject/SharedObjectCore, @@ -642,7 +642,7 @@ export abstract class SharedObjectCore< * @param args - Arguments to pass to the event listeners. * @returns `true` if the event had listeners, `false` otherwise. */ - public emit(event: EventEmitterEventType, ...args: any[]): boolean { + public emit(event: EventEmitterEventType, ...args: unknown[]): boolean { return this.callbacksHelper.measure(() => { return super.emit(event, ...args); }); @@ -655,7 +655,7 @@ export abstract class SharedObjectCore< * @param args - Arguments for the event * @returns Whatever `super.emit()` returns. */ - private emitInternal(event: EventEmitterEventType, ...args: any[]): boolean { + private emitInternal(event: EventEmitterEventType, ...args: unknown[]): boolean { return super.emit(event, ...args); } } @@ -793,7 +793,7 @@ export abstract class SharedObject< * Calls the serializer over all data in this object that reference other GC nodes. * Derived classes must override this to provide custom list of references to other GC nodes. */ - protected processGCDataCore(serializer: IFluidSerializer) { + protected processGCDataCore(serializer: IFluidSerializer): void { // We run the full summarize logic to get the list of outbound routes from this object. This is a little // expensive but its okay for now. It will be updated to not use full summarize and make it more efficient. // See: https://github.com/microsoft/FluidFramework/issues/4547 @@ -814,7 +814,7 @@ export abstract class SharedObject< propertyName: string, incrementBy: number, telemetryContext?: ITelemetryContext, - ) { + ): void { if (telemetryContext !== undefined) { // TelemetryContext needs to implment a get function assert( diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index e5d29ea82f56..29ed108ff90d 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -27,7 +27,10 @@ import { createChildLogger } from "@fluidframework/telemetry-utils/internal"; import { IFluidSerializer } from "../serializer.js"; import { SharedObject } from "../sharedObject.js"; -type Overridable = T extends ((...args: any) => any) | string | number | undefined | [] +/* eslint-disable-next-line @typescript-eslint/ban-types -- + Trying to use sepcfic function signatures here instead of Function makes it so some of the properties of + OverridableType below (summarizeCore, loadCore, processCore) end up not typed correctly */ +type Overridable = T extends Function | string | number | undefined | [] ? T : { -readonly [P in keyof T]?: Overridable; @@ -36,8 +39,8 @@ type Overridable = T extends ((...args: any) => any) | string | number | unde function createOverridableProxy( name: string, ...overrides: Overridable[] -) { - return new Proxy({} as any as T, { +): T { + return new Proxy({} as unknown as T, { get: (_, p, r) => { for (const override of overrides) { if (p in override) { @@ -49,37 +52,41 @@ function createOverridableProxy( }); } -function createTestSharedObject( - overrides: Overridable<{ - id: string; - runtime: IFluidDataStoreRuntime; - attributes: IChannelAttributes; - telemetryConfigPrefix: string; - summarizeCore: ( - this: SharedObject, - serializer: IFluidSerializer, - telemetryContext?: ITelemetryContext | undefined, - incrementalSummaryContext?: IExperimentalIncrementalSummaryContext | undefined, - ) => ISummaryTreeWithStats; - loadCore: (this: SharedObject, services: IChannelStorageService) => Promise; - processCore: ( - this: SharedObject, - - message: ISequencedDocumentMessage, - local: boolean, - localOpMetadata: unknown, - ) => void; - onDisconnect: (this: SharedObject) => void; - applyStashedOp: (this: SharedObject, content: any) => unknown; - didAttach: () => void; - }>, -) { +type OverridableType = Overridable<{ + id: string; + runtime: IFluidDataStoreRuntime; + attributes: IChannelAttributes; + telemetryConfigPrefix: string; + summarizeCore: ( + this: SharedObject, + serializer: IFluidSerializer, + telemetryContext?: ITelemetryContext | undefined, + incrementalSummaryContext?: IExperimentalIncrementalSummaryContext | undefined, + ) => ISummaryTreeWithStats; + loadCore: (this: SharedObject, services: IChannelStorageService) => Promise; + processCore: ( + this: SharedObject, + message: ISequencedDocumentMessage, + local: boolean, + localOpMetadata: unknown, + ) => void; + onDisconnect: (this: SharedObject) => void; + applyStashedOp: (this: SharedObject, content: unknown) => void; + didAttach: () => void; +}>; + +function createTestSharedObject(overrides: OverridableType): { + overrides: OverridableType; + sharedObject: SharedObject; +} { class TestSharedObject extends SharedObject { protected summarizeCore = overrides?.summarizeCore?.bind(this); - protected loadCore = overrides?.loadCore?.bind(this); - protected processCore = overrides?.processCore?.bind(this); - protected onDisconnect = overrides?.onDisconnect?.bind(this); - protected applyStashedOp = overrides?.applyStashedOp?.bind(this); + /* eslint-disable @typescript-eslint/no-non-null-assertion */ + protected loadCore = overrides?.loadCore!.bind(this); + protected processCore = overrides?.processCore!.bind(this); + protected onDisconnect = overrides?.onDisconnect!.bind(this); + protected applyStashedOp = overrides?.applyStashedOp!.bind(this); + /* eslint-enable @typescript-eslint/no-non-null-assertion */ protected didAttach = overrides.didAttach?.bind(this) ?? (() => assert.fail("didAttach not set")); } From 4b96932d697244a1838c870e82f3f9d9191becc5 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:31:10 -0500 Subject: [PATCH 06/19] WIP --- .../src/remoteObjectHandle.ts | 2 +- .../attachingBindingAndConnecting.spec.ts | 21 +++++++++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/dds/shared-object-base/src/remoteObjectHandle.ts b/packages/dds/shared-object-base/src/remoteObjectHandle.ts index 58cc1a224ad2..6b12e075e21a 100644 --- a/packages/dds/shared-object-base/src/remoteObjectHandle.ts +++ b/packages/dds/shared-object-base/src/remoteObjectHandle.ts @@ -48,7 +48,7 @@ export class RemoteFluidObjectHandle extends FluidHandleBase { }; this.objectP = this.routeContext.resolveHandle(request).then((response) => { if (response.mimeType === "fluid/object") { - const fluidObject: FluidObject = response.value; + const fluidObject: FluidObject = response.value as FluidObject; return fluidObject; } throw responseToException(response, request); diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index 29ed108ff90d..7452531c45a0 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -81,12 +81,10 @@ function createTestSharedObject(overrides: OverridableType): { } { class TestSharedObject extends SharedObject { protected summarizeCore = overrides?.summarizeCore?.bind(this); - /* eslint-disable @typescript-eslint/no-non-null-assertion */ - protected loadCore = overrides?.loadCore!.bind(this); - protected processCore = overrides?.processCore!.bind(this); - protected onDisconnect = overrides?.onDisconnect!.bind(this); - protected applyStashedOp = overrides?.applyStashedOp!.bind(this); - /* eslint-enable @typescript-eslint/no-non-null-assertion */ + protected loadCore = overrides?.loadCore?.bind(this); + protected processCore = overrides?.processCore?.bind(this); + protected onDisconnect = overrides?.onDisconnect?.bind(this); + protected applyStashedOp = overrides?.applyStashedOp?.bind(this); protected didAttach = overrides.didAttach?.bind(this) ?? (() => assert.fail("didAttach not set")); } @@ -116,7 +114,7 @@ function createTestSharedObject(overrides: OverridableType): { createOverridableProxy( "runtime", runtime, - new TypedEventEmitter() as any as Overridable, + new TypedEventEmitter() as unknown as Overridable, ), createOverridableProxy("attributes", attributes), overrides?.telemetryConfigPrefix ?? "testSharedObject", @@ -167,7 +165,7 @@ describe("SharedObject attaching binding and connecting", () => { let didAttach = 0; const { overrides, sharedObject } = createTestSharedObject({ runtime: { - ...(runtimeEvents as any as Overridable), + ...(runtimeEvents as unknown as Overridable), attachState: AttachState.Detached, }, didAttach: () => didAttach++, @@ -242,7 +240,7 @@ describe("SharedObject attaching binding and connecting", () => { let loaded = false; const { overrides, sharedObject } = createTestSharedObject({ runtime: { - ...(runtimeEvents as any as Overridable), + ...(runtimeEvents as unknown as Overridable), attachState: AttachState.Detached, connected: true, }, @@ -326,7 +324,7 @@ describe("SharedObject attaching binding and connecting", () => { let didAttach = 0; const { overrides, sharedObject } = createTestSharedObject({ runtime: { - ...(runtimeEvents as any as Overridable), + ...(runtimeEvents as unknown as Overridable), attachState: AttachState.Detached, connected: true, }, @@ -462,11 +460,12 @@ describe("SharedObject attaching binding and connecting", () => { it("isAttached with detached transition to attach runtime", async () => { const runtimeEvents = new TypedEventEmitter(); + let didAttach = 0; let attachCalled = false; const { overrides, sharedObject } = createTestSharedObject({ runtime: { - ...(runtimeEvents as any), + // ...runtimeEvents, attachState: AttachState.Detached, connected: false, bindChannel: (channel) => { From 483b1edc6eeb660cc761e5e5793975fff92684d9 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:11:04 +0000 Subject: [PATCH 07/19] Compilation works again --- packages/dds/shared-object-base/src/serializer.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index 290c55c9d25e..656576a18bdb 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -89,12 +89,14 @@ export class FluidSerializer implements IFluidSerializer { * * Any unbound handles encountered are bound to the provided IFluidHandle. */ - public encode(input: unknown, bind: IFluidHandleInternal): unknown { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: ddsFuzzHarness breaks + public encode(input: any, bind: IFluidHandleInternal): any { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions return !!input && typeof input === "object" - ? this.recursivelyReplace(input, this.encodeValue, bind) + ? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- TODO: ddsFuzzHarness breaks + this.recursivelyReplace(input, this.encodeValue, bind) : input; } @@ -107,7 +109,8 @@ export class FluidSerializer implements IFluidSerializer { * * The decoded handles are implicitly bound to the handle context of this serializer. */ - public decode(input: unknown): unknown { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: ddsFuzzHarness breaks + public decode(input: unknown): any { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions From 10031aca1d9f1ce34dff34f3f74eb1d8e5dd75bd Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:12:55 +0000 Subject: [PATCH 08/19] Cleanup unused disable --- packages/dds/shared-object-base/src/serializer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index 656576a18bdb..dcd48e25295e 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -4,7 +4,6 @@ */ // RATIONALE: Many methods consume and return 'any' by necessity. -/* eslint-disable @typescript-eslint/no-unsafe-return */ import { IFluidHandle } from "@fluidframework/core-interfaces"; import { From e0afcf7dce80a0f387e69158b04c2b70f07af993 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:22:36 +0000 Subject: [PATCH 09/19] Address sharedObject.ts --- .../shared-object-base/src/sharedObject.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 158f65db58d1..03cf49385564 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -48,6 +48,7 @@ import { loggerToMonitoringContext, tagCodeArtifacts, type ICustomData, + type IFluidErrorBase, } from "@fluidframework/telemetry-utils/internal"; import { v4 as uuid } from "uuid"; @@ -77,7 +78,7 @@ export abstract class SharedObjectCore< extends EventEmitterWithErrorHandling implements ISharedObject { - public get IFluidLoadable() { + public get IFluidLoadable(): this { return this; } @@ -136,7 +137,9 @@ export abstract class SharedObjectCore< protected runtime: IFluidDataStoreRuntime, public readonly attributes: IChannelAttributes, ) { - super((event: EventEmitterEventType, e: any) => this.eventListenerErrorHandler(event, e)); + super((event: EventEmitterEventType, e: unknown) => + this.eventListenerErrorHandler(event, e), + ); assert(!id.includes("/"), 0x304 /* Id cannot contain slashes */); @@ -217,7 +220,7 @@ export abstract class SharedObjectCore< * would result in same error thrown. If called multiple times, only first error is remembered. * @param error - error object that is thrown whenever an attempt is made to modify this object */ - private closeWithError(error: any): void { + private closeWithError(error: IFluidErrorBase | undefined): void { if (this.closeError === undefined) { this.closeError = error; } @@ -259,7 +262,8 @@ export abstract class SharedObjectCore< // always propagate connection state this.setBoundAndHandleAttach = () => this.setConnectionState(this.runtime.connected); this._isBoundToContext = true; - const runDidAttach = () => { + // eslint-disable-next-line unicorn/consistent-function-scoping + const runDidAttach: () => void = () => { // Allows objects to do any custom processing if it is attached. this.didAttach(); this.setConnectionState(this.runtime.connected); @@ -390,7 +394,9 @@ export abstract class SharedObjectCore< /** * Called when the object has disconnected from the delta stream. */ - protected abstract onDisconnect(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: change return type to void (legacy breaking) + protected abstract onDisconnect(): any; /** * The serializer to serialize / parse handles. @@ -405,6 +411,7 @@ export abstract class SharedObjectCore< * and not sent to the server. This will be sent back when this message is received back from the server. This is * also sent if we are asked to resubmit the message. */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) protected submitLocalMessage(content: any, localOpMetadata: unknown = undefined): void { this.verifyNotClosed(); if (this.isAttached()) { @@ -443,6 +450,7 @@ export abstract class SharedObjectCore< * @param content - The content of the original message. * @param localOpMetadata - The local metadata associated with the original message. */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) protected reSubmitCore(content: any, localOpMetadata: unknown): void { this.submitLocalMessage(content, localOpMetadata); } @@ -456,6 +464,7 @@ export abstract class SharedObjectCore< protected async newAckBasedPromise( executor: ( resolve: (value: T | PromiseLike) => void, + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) reject: (reason?: any) => void, ) => void, ): Promise { @@ -609,6 +618,7 @@ export abstract class SharedObjectCore< /** * Revert an op */ + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) protected rollback(content: any, localOpMetadata: unknown): void { throw new Error("rollback not supported"); } From a763db82928b3eb5faaa65231fc900d47fdb3384 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 00:07:52 +0000 Subject: [PATCH 10/19] Build successful! --- .../src/test/serializer.spec.ts | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/dds/shared-object-base/src/test/serializer.spec.ts b/packages/dds/shared-object-base/src/test/serializer.spec.ts index cd6d6055a939..5b2bdd5e815b 100644 --- a/packages/dds/shared-object-base/src/test/serializer.spec.ts +++ b/packages/dds/shared-object-base/src/test/serializer.spec.ts @@ -5,6 +5,8 @@ import { strict as assert } from "node:assert"; +import { isFluidHandle } from "@fluidframework/runtime-utils/internal"; + import { RemoteFluidObjectHandle } from "../remoteObjectHandle.js"; import { FluidSerializer } from "../serializer.js"; import { makeHandlesSerializable, parseHandles } from "../utils.js"; @@ -12,26 +14,25 @@ import { makeHandlesSerializable, parseHandles } from "../utils.js"; import { MockHandleContext, makeJson } from "./utils.js"; describe("FluidSerializer", () => { - function printHandle(target: any) { + function printHandle(target: unknown): string { return JSON.stringify(target, (key, value) => { // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return value?.IFluidHandle === undefined ? value : "#HANDLE"; + return isFluidHandle(value) ? "#HANDLE" : value; }); } - function createNestedCases(testCases: any[]) { + function createNestedCases(testCases: unknown[]): unknown[] { testCases.push( // Add an object where each field references one of the JSON serializable types. - testCases.reduce((o, value, index) => { + // eslint-disable-next-line unicorn/no-array-reduce -- Not sure how to refactor this correctly + testCases.reduce((o, value, index) => { o[`f${index}`] = value; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return o; }, {}), // Add an array that contains each of our constructed test cases. [...testCases], ); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return testCases; } @@ -42,13 +43,14 @@ describe("FluidSerializer", () => { // Start with the various JSON-serializable types. A mix of "truthy" and "falsy" values // are of particular interest. + // eslint-disable-next-line unicorn/no-null -- Explicitly testing null. const simple = createNestedCases([false, true, 0, 1, "", "x", null, [], {}]); simple.push( // Add an object where each field references one of the JSON serializable types. - simple.reduce((o, value, index) => { + // eslint-disable-next-line unicorn/no-array-reduce -- Not sure how to refactor this correctly + simple.reduce((o, value, index) => { o[`f${index}`] = value; - // eslint-disable-next-line @typescript-eslint/no-unsafe-return return o; }, {}), // Add an array that contains each of our constructed test cases. @@ -58,6 +60,7 @@ describe("FluidSerializer", () => { // Verify that `encode` is a no-op for these simple cases. for (const input of simple) { it(`${printHandle(input)} -> ${JSON.stringify(input)}`, () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test deals with several object shapes const actual = serializer.encode(input, handle); assert.strictEqual( actual, @@ -65,6 +68,7 @@ describe("FluidSerializer", () => { "encode() on input with no handles must return original input.", ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test deals with several object shapes const decoded = serializer.decode(actual); assert.strictEqual( decoded, @@ -116,6 +120,7 @@ describe("FluidSerializer", () => { for (const input of tricky) { it(`${printHandle(input)} -> ${JSON.stringify(input)}`, () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test deals with several object shapes const actual = serializer.encode(input, handle); assert.strictEqual( actual, @@ -123,6 +128,7 @@ describe("FluidSerializer", () => { "encode() on input with no handles must return original input.", ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- test deals with several object shapes const decoded = serializer.decode(actual); assert.strictEqual( decoded, @@ -169,8 +175,9 @@ describe("FluidSerializer", () => { url: "/root", }; - function check(decodedForm, encodedForm) { + function check(decodedForm, encodedForm): void { it(`${printHandle(decodedForm)} -> ${JSON.stringify(encodedForm)}`, () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.encode() const replaced = serializer.encode(decodedForm, handle); assert.notStrictEqual( replaced, @@ -179,9 +186,11 @@ describe("FluidSerializer", () => { ); assert.deepStrictEqual(replaced, encodedForm, "encode() must return expected output."); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.encode() const replacedTwice = serializer.encode(replaced, handle); assert.deepStrictEqual(replacedTwice, replaced, "encode should be idempotent"); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.decode() const decodedRoundTrip = serializer.decode(replaced); assert.notStrictEqual( decodedRoundTrip, @@ -194,6 +203,7 @@ describe("FluidSerializer", () => { "input must round-trip through encode()/decode().", ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.decode() const decodedTwice = serializer.decode(decodedRoundTrip); assert.deepStrictEqual(decodedTwice, decodedRoundTrip, "decode should be idempotent"); @@ -224,6 +234,7 @@ describe("FluidSerializer", () => { ); it(`sizable json tree`, () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- The test works on funky objects const input: any = makeJson( /* breadth: */ 4, /* depth: */ 4, @@ -237,9 +248,10 @@ describe("FluidSerializer", () => { ); // Add some handles to intermediate objects. - input.h = handle; - input.o1.h = handle; + input.h = handle; // eslint-disable-line @typescript-eslint/no-unsafe-member-access + input.o1.h = handle; // eslint-disable-line @typescript-eslint/no-unsafe-member-access + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.encode() const replaced = serializer.encode(input, handle); assert.notStrictEqual( replaced, @@ -247,6 +259,7 @@ describe("FluidSerializer", () => { "encode() must shallow-clone rather than mutate original object.", ); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment -- TODO: update when we can fix the return type of serializer.decode() const decoded = serializer.decode(replaced); assert.notStrictEqual( decoded, @@ -327,12 +340,16 @@ describe("FluidSerializer", () => { const bind = new RemoteFluidObjectHandle("/", new MockHandleContext()); const handle = new RemoteFluidObjectHandle("/okay", new MockHandleContext()); const input = { x: handle, y: 123 }; - const serializedOnce = makeHandlesSerializable(input, serializer, bind); + const serializedOnce = makeHandlesSerializable(input, serializer, bind) as { + x: { type: "__fluid_handle__" }; + }; assert( serializedOnce.x.type === "__fluid_handle__", "Serialized handle should be a handle", ); - const serializedTwice = makeHandlesSerializable(serializedOnce, serializer, bind); + const serializedTwice = makeHandlesSerializable(serializedOnce, serializer, bind) as { + x: { type: "__fluid_handle__" }; + }; assert( serializedTwice.x.type === "__fluid_handle__", "Twice-Serialized handle should be a handle", @@ -344,12 +361,14 @@ describe("FluidSerializer", () => { url: "/root", }; const input = { x: serializedHandle, y: 123 }; - const parsedOnce = parseHandles(input, serializer); + const parsedOnce = parseHandles(input, serializer) as { x: RemoteFluidObjectHandle }; assert( parsedOnce.x instanceof RemoteFluidObjectHandle, "Parsed handle should be an instance of RemoteFluidObjectHandle", ); - const parsedTwice = parseHandles(parsedOnce, serializer); + const parsedTwice = parseHandles(parsedOnce, serializer) as { + x: RemoteFluidObjectHandle; + }; assert( parsedTwice.x instanceof RemoteFluidObjectHandle, "Twice-Parsed handle should be an instance of RemoteFluidObjectHandle", From e7f4f2eb71a82c2c84a6f7aab0808822ec3de2fb Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:15:26 -0600 Subject: [PATCH 11/19] Remove old comment Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> --- packages/dds/shared-object-base/src/serializer.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index dcd48e25295e..c1ee7895677d 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. */ -// RATIONALE: Many methods consume and return 'any' by necessity. import { IFluidHandle } from "@fluidframework/core-interfaces"; import { From f41a546874670344f02ce13afd415bbe972a5642 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:38:52 -0600 Subject: [PATCH 12/19] Typo Co-authored-by: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> --- .../src/test/attachingBindingAndConnecting.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index 7452531c45a0..affa29a5c1b3 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -28,7 +28,7 @@ import { IFluidSerializer } from "../serializer.js"; import { SharedObject } from "../sharedObject.js"; /* eslint-disable-next-line @typescript-eslint/ban-types -- - Trying to use sepcfic function signatures here instead of Function makes it so some of the properties of + Trying to use specific function signatures here instead of Function makes it so some of the properties of OverridableType below (summarizeCore, loadCore, processCore) end up not typed correctly */ type Overridable = T extends Function | string | number | undefined | [] ? T From de3d53beb5b0d5742f1d879f99ea40759a4dbb9b Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:24:03 +0000 Subject: [PATCH 13/19] Update generic type --- packages/dds/shared-object-base/src/serializer.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index c1ee7895677d..8b4c6db53d67 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -158,10 +158,10 @@ export class FluidSerializer implements IFluidSerializer { // Invoked for non-null objects to recursively replace references to IFluidHandles. // Clones as-needed to avoid mutating the `input` object. If no IFluidHandes are present, // returns the original `input`. - private recursivelyReplace( + private recursivelyReplace( input: object, - replacer: (input: unknown, context?: T) => unknown, - context?: T, + replacer: (input: unknown, context?: TContext) => unknown, + context?: TContext, ): unknown { // Note: Caller is responsible for ensuring that `input` is defined / non-null. // (Required for Object.keys() below.) From 106fd2b9000b879e45e9b96b4de2f3e8a667b892 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 14:39:31 +0000 Subject: [PATCH 14/19] Adding ADO item --- packages/dds/shared-object-base/src/serializer.ts | 6 +++--- packages/dds/shared-object-base/src/sharedObject.ts | 8 ++++---- packages/dds/shared-object-base/src/utils.ts | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index 8b4c6db53d67..7bb82b19dd67 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -32,7 +32,7 @@ export interface IFluidSerializer { * The original `input` object is not mutated. This method will shallowly clones all objects in the path from * the root to any replaced handles. (If no handles are found, returns the original object.) */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) encode(value: unknown, bind: IFluidHandle): any; /** @@ -44,7 +44,7 @@ export interface IFluidSerializer { * * The decoded handles are implicitly bound to the handle context of this serializer. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) decode(input: unknown): any; /** @@ -56,7 +56,7 @@ export interface IFluidSerializer { * Parses the given JSON input string and returns the JavaScript object defined by it. Any Fluid * handles will be realized as part of the parse */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) parse(value: string): any; } diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 03cf49385564..b74f48a771ec 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -411,7 +411,7 @@ export abstract class SharedObjectCore< * and not sent to the server. This will be sent back when this message is received back from the server. This is * also sent if we are asked to resubmit the message. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) protected submitLocalMessage(content: any, localOpMetadata: unknown = undefined): void { this.verifyNotClosed(); if (this.isAttached()) { @@ -450,7 +450,7 @@ export abstract class SharedObjectCore< * @param content - The content of the original message. * @param localOpMetadata - The local metadata associated with the original message. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) protected reSubmitCore(content: any, localOpMetadata: unknown): void { this.submitLocalMessage(content, localOpMetadata); } @@ -464,7 +464,7 @@ export abstract class SharedObjectCore< protected async newAckBasedPromise( executor: ( resolve: (value: T | PromiseLike) => void, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) reject: (reason?: any) => void, ) => void, ): Promise { @@ -618,7 +618,7 @@ export abstract class SharedObjectCore< /** * Revert an op */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: use unknown instead of any (legacy breaking) + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) protected rollback(content: any, localOpMetadata: unknown): void { throw new Error("rollback not supported"); } diff --git a/packages/dds/shared-object-base/src/utils.ts b/packages/dds/shared-object-base/src/utils.ts index f22ac670ff16..c80ef0d506c1 100644 --- a/packages/dds/shared-object-base/src/utils.ts +++ b/packages/dds/shared-object-base/src/utils.ts @@ -45,7 +45,7 @@ export function makeHandlesSerializable( value: unknown, serializer: IFluidSerializer, bind: IFluidHandle, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) ): any { return serializer.encode(value, bind); } @@ -61,7 +61,7 @@ export function makeHandlesSerializable( * @legacy * @alpha */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: use unknown (breaking change) +// eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) export function parseHandles(value: unknown, serializer: IFluidSerializer): any { return serializer.decode(value); } From 2e289a618603a5d581910f5caf64bcd574bd4d9a Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:02:20 +0000 Subject: [PATCH 15/19] Undo potentially legacy breaking changes --- .../api-report/shared-object-base.legacy.alpha.api.md | 6 +++--- packages/dds/shared-object-base/src/serializer.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md index 177d89e6968d..c7482946ccf4 100644 --- a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md +++ b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md @@ -6,10 +6,10 @@ // @alpha (undocumented) export interface IFluidSerializer { - decode(input: unknown): any; - encode(value: unknown, bind: IFluidHandle): any; + decode(input: any): any; + encode(value: any, bind: IFluidHandle): any; parse(value: string): any; - stringify(value: unknown, bind: IFluidHandle): string; + stringify(value: any, bind: IFluidHandle): string; } // @alpha diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index 7bb82b19dd67..ff5ffba436fe 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. */ - import { IFluidHandle } from "@fluidframework/core-interfaces"; import { IFluidHandleContext, @@ -33,7 +32,7 @@ export interface IFluidSerializer { * the root to any replaced handles. (If no handles are found, returns the original object.) */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) - encode(value: unknown, bind: IFluidHandle): any; + encode(value: any, bind: IFluidHandle): any; /** * Given a fully-jsonable object tree that may have encoded handle objects embedded within, will return an @@ -45,12 +44,13 @@ export interface IFluidSerializer { * The decoded handles are implicitly bound to the handle context of this serializer. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) - decode(input: unknown): any; + decode(input: any): any; /** * Stringifies a given value. Converts any IFluidHandle to its stringified equivalent. */ - stringify(value: unknown, bind: IFluidHandle): string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) + stringify(value: any, bind: IFluidHandle): string; /** * Parses the given JSON input string and returns the JavaScript object defined by it. Any Fluid From d074520653f15f618aaeb69a2a6a74d8b2de4ef0 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:23:42 +0000 Subject: [PATCH 16/19] Undo more legacy breaking changes --- .../api-report/shared-object-base.legacy.alpha.api.md | 6 +++--- packages/dds/shared-object-base/src/sharedObject.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md index c7482946ccf4..de8c9114e47c 100644 --- a/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md +++ b/packages/dds/shared-object-base/api-report/shared-object-base.legacy.alpha.api.md @@ -53,7 +53,7 @@ export abstract class SharedObject extends EventEmitterWithErrorHandling implements ISharedObject { constructor(id: string, runtime: IFluidDataStoreRuntime, attributes: IChannelAttributes); - protected abstract applyStashedOp(content: unknown): void; + protected abstract applyStashedOp(content: any): void; // (undocumented) readonly attributes: IChannelAttributes; bindToContext(): void; @@ -62,7 +62,7 @@ export abstract class SharedObjectCore; protected didAttach(): void; protected dirty(): void; - emit(event: EventEmitterEventType, ...args: unknown[]): boolean; + emit(event: EventEmitterEventType, ...args: any[]): boolean; abstract getAttachSummary(fullTree?: boolean, trackState?: boolean, telemetryContext?: ITelemetryContext): ISummaryTreeWithStats; abstract getGCData(fullGC?: boolean): IGarbageCollectionData; readonly handle: IFluidHandleInternal; @@ -79,7 +79,7 @@ export abstract class SharedObjectCore(executor: (resolve: (value: T | PromiseLike) => void, reject: (reason?: any) => void) => void): Promise; protected onConnect(): void; protected abstract onDisconnect(): any; - protected abstract processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): void; + protected abstract processCore(message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown): any; protected reSubmitCore(content: any, localOpMetadata: unknown): void; protected rollback(content: any, localOpMetadata: unknown): void; // (undocumented) diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index b74f48a771ec..94c355e67e7d 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -389,7 +389,8 @@ export abstract class SharedObjectCore< message: ISequencedDocumentMessage, local: boolean, localOpMetadata: unknown, - ): void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 use void instead of any (legacy breaking) + ): any; /** * Called when the object has disconnected from the delta stream. @@ -640,7 +641,8 @@ export abstract class SharedObjectCore< * * @param content - Contents of a stashed op. */ - protected abstract applyStashedOp(content: unknown): void; + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: AB#26129 use unknown instead of any (legacy breaking) + protected abstract applyStashedOp(content: any): void; /** * Emit an event. This function is only intended for use by DDS classes that extend SharedObject/SharedObjectCore, @@ -652,8 +654,9 @@ export abstract class SharedObjectCore< * @param args - Arguments to pass to the event listeners. * @returns `true` if the event had listeners, `false` otherwise. */ - public emit(event: EventEmitterEventType, ...args: unknown[]): boolean { + public emit(event: EventEmitterEventType, ...args: any[]): boolean { return this.callbacksHelper.measure(() => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument return super.emit(event, ...args); }); } From 950843fc51b8614755f7ba5cc151c0c6a199b610 Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:28:26 +0000 Subject: [PATCH 17/19] Fixes --- packages/dds/shared-object-base/src/serializer.ts | 6 +++--- packages/dds/shared-object-base/src/sharedObject.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/dds/shared-object-base/src/serializer.ts b/packages/dds/shared-object-base/src/serializer.ts index ff5ffba436fe..7da2b8ade8ae 100644 --- a/packages/dds/shared-object-base/src/serializer.ts +++ b/packages/dds/shared-object-base/src/serializer.ts @@ -87,13 +87,13 @@ export class FluidSerializer implements IFluidSerializer { * * Any unbound handles encountered are bound to the provided IFluidHandle. */ - // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: ddsFuzzHarness breaks + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- TODO: AB#26129 ddsFuzzHarness breaks when we update any->unknown public encode(input: any, bind: IFluidHandleInternal): any { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. // eslint-disable-next-line @typescript-eslint/strict-boolean-expressions return !!input && typeof input === "object" - ? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- TODO: ddsFuzzHarness breaks + ? // eslint-disable-next-line @typescript-eslint/no-unsafe-argument -- TODO: AB#26129 ddsFuzzHarness breaks when we update any->unknown this.recursivelyReplace(input, this.encodeValue, bind) : input; } @@ -107,7 +107,7 @@ export class FluidSerializer implements IFluidSerializer { * * The decoded handles are implicitly bound to the handle context of this serializer. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: ddsFuzzHarness breaks + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 ddsFuzzHarness breaks when we update any->unknown public decode(input: unknown): any { // If the given 'input' cannot contain handles, return it immediately. Otherwise, // return the result of 'recursivelyReplace()'. diff --git a/packages/dds/shared-object-base/src/sharedObject.ts b/packages/dds/shared-object-base/src/sharedObject.ts index 94c355e67e7d..de02418c1e7c 100644 --- a/packages/dds/shared-object-base/src/sharedObject.ts +++ b/packages/dds/shared-object-base/src/sharedObject.ts @@ -396,7 +396,7 @@ export abstract class SharedObjectCore< * Called when the object has disconnected from the delta stream. */ - // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: change return type to void (legacy breaking) + // eslint-disable-next-line @typescript-eslint/no-explicit-any -- TODO: AB#26129 change return type to void (legacy breaking) protected abstract onDisconnect(): any; /** From 564ecc84695ef66801e1e9ba1d66895f6fe6f89d Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:49:07 +0000 Subject: [PATCH 18/19] Fix in test --- .../src/test/attachingBindingAndConnecting.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index affa29a5c1b3..e79c1dd124ab 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -465,7 +465,7 @@ describe("SharedObject attaching binding and connecting", () => { let attachCalled = false; const { overrides, sharedObject } = createTestSharedObject({ runtime: { - // ...runtimeEvents, + ...(runtimeEvents as unknown as Overridable), attachState: AttachState.Detached, connected: false, bindChannel: (channel) => { From d5e0c82380b973f501370c047350e909534036ce Mon Sep 17 00:00:00 2001 From: Alex Villarreal <716334+alexvy86@users.noreply.github.com> Date: Tue, 3 Dec 2024 15:51:22 +0000 Subject: [PATCH 19/19] Remove empty line --- .../src/test/attachingBindingAndConnecting.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts index e79c1dd124ab..b8640c0ffc1f 100644 --- a/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts +++ b/packages/dds/shared-object-base/src/test/attachingBindingAndConnecting.spec.ts @@ -460,7 +460,6 @@ describe("SharedObject attaching binding and connecting", () => { it("isAttached with detached transition to attach runtime", async () => { const runtimeEvents = new TypedEventEmitter(); - let didAttach = 0; let attachCalled = false; const { overrides, sharedObject } = createTestSharedObject({