Skip to content

Commit 9a9e3e1

Browse files
Merge branch 'main' of https://github.com/microsoft/FluidFramework into local-sever-stress-tests
2 parents c582039 + d1dbda8 commit 9a9e3e1

File tree

134 files changed

+2408
-586
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

134 files changed

+2408
-586
lines changed

build-tools/packages/build-tools/src/fluidBuild/buildGraph.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -538,10 +538,12 @@ export class BuildGraph {
538538
const spinner = new Spinner("Checking incremental build task status...");
539539
spinner.start();
540540

541+
// Note: any console logging done here (e.g. in leafTask.ts' checkIsUpToDate()) runs the risk of getting truncated due to how picospinner works.
542+
// Ideally we shouldn't do console logging between starting and stopping a spinner.
541543
const isUpToDate = await this.isUpToDate();
542544

543-
timer?.time(`Check up to date completed`);
544545
spinner.succeed("Tasks loaded.");
546+
timer?.time(`Check up to date completed`);
545547

546548
log(
547549
`Start tasks '${chalk.cyanBright(this.buildTaskNames.join("', '"))}' in ${

common/build/eslint-plugin-fluid/pnpm-lock.yaml

+9-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/build/eslint-plugin-fluid/src/test/example/no-unchecked-record-access/fileWithOnlyArrayAccess.ts

+94-2
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,97 @@
33
* Licensed under the MIT License.
44
*/
55

6-
const arr2 = [1, 2, 3];
7-
const value2 = arr2[0]; // This should not report an error
6+
/*
7+
* Array Access Tests
8+
*/
9+
10+
/* Basic array access */
11+
const numberArray = [1, 2, 3];
12+
const stringArray = ["a", "b", "c"];
13+
const mixedArray = [1, "b", true];
14+
15+
// Direct index access
16+
const firstNumber = numberArray[0]; // ok: Accessing array with numeric literal
17+
const lastString = stringArray[2]; // ok: Accessing array with numeric literal
18+
const middleMixed = mixedArray[1]; // ok: Accessing array with numeric literal
19+
20+
// Variable index access
21+
const index = 1;
22+
const numberByVar = numberArray[index]; // ok: Accessing array with numeric variable
23+
const stringByVar = stringArray[index]; // ok: Accessing array with numeric variable
24+
25+
/* Readonly arrays */
26+
const readonlyArray: ReadonlyArray<number> = [1, 2, 3];
27+
const readonlyFirst = readonlyArray[0]; // ok: Accessing readonly array
28+
const readonlyByVar = readonlyArray[index]; // ok: Accessing readonly array with variable
29+
30+
/* Tuple types */
31+
const tuple: [string, number, boolean] = ["hello", 42, true];
32+
const tupleFirst = tuple[0]; // ok: Accessing tuple element
33+
const tupleSecond = tuple[1]; // ok: Accessing tuple element
34+
const tupleByVar = tuple[index]; // ok: Accessing tuple with variable
35+
36+
/* Array-like objects */
37+
const arrayLike = {
38+
length: 3,
39+
0: "zero",
40+
1: "one",
41+
2: "two"
42+
};
43+
const arrayLikeFirst = arrayLike[0]; // ok: Accessing array-like object with numeric index
44+
45+
/* Array methods that return arrays */
46+
const slicedArray = numberArray.slice(1); // Creates new array
47+
const slicedElement = slicedArray[0]; // ok: Accessing sliced array
48+
49+
/* Nested arrays */
50+
const nested = [[1, 2], [3, 4]];
51+
const nestedElement = nested[0][1]; // ok: Accessing nested array
52+
const nestedByVar = nested[index][index]; // ok: Accessing nested array with variables
53+
54+
/* Array with optional elements */
55+
const sparseArray: (number | undefined)[] = [1, undefined, 3];
56+
const sparseElement = sparseArray[1]; // ok: Accessing potentially undefined element
57+
58+
/* Array destructuring */
59+
const [first, second] = numberArray; // ok: Array destructuring
60+
61+
/* Array access with expressions */
62+
const expressionIndex = 1 + 1;
63+
const elementByExpression = numberArray[expressionIndex]; // ok: Accessing with expression
64+
const elementByComputation = numberArray[index + 1]; // ok: Accessing with computation
65+
66+
/* TypedArray access */
67+
const typedArray = new Int32Array([1, 2, 3]);
68+
const typedElement = typedArray[0]; // ok: Accessing typed array
69+
70+
/* Array subclass */
71+
class CustomArray extends Array<number> {}
72+
const customArray = new CustomArray(1, 2, 3);
73+
const customElement = customArray[0]; // ok: Accessing custom array subclass
74+
75+
/* Generic array types */
76+
function accessGenericArray<T>(arr: T[], index: number): T {
77+
return arr[index]; // ok: Accessing generic array
78+
}
79+
80+
/* String access (string is array-like) */
81+
const str = "hello";
82+
const char = str[0]; // ok: Accessing string character
83+
84+
/* Array-like DOM collections */
85+
const htmlCollection = document.getElementsByTagName("div");
86+
const firstElement = htmlCollection[0]; // ok: Accessing HTML collection
87+
88+
/* Iterator-based access */
89+
for (let i = 0; i < numberArray.length; i++) {
90+
const element = numberArray[i]; // ok: Accessing in loop
91+
}
92+
93+
/* Array with union type */
94+
const unionArray: (string | number)[] = ["a", 1, "b", 2];
95+
const unionElement = unionArray[0]; // ok: Accessing union type array
96+
97+
/* Readonly tuple */
98+
const readonlyTuple: readonly [number, string] = [1, "two"];
99+
const readonlyTupleElement = readonlyTuple[0]; // ok: Accessing readonly tuple

examples/apps/ai-collab/src/components/TaskCard.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
aiCollab,
1010
type AiCollabErrorResponse,
1111
type AiCollabSuccessResponse,
12+
type ApplyEditSuccess,
1213
type Difference,
1314
type DifferenceChange,
1415
type DifferenceMove,
@@ -179,8 +180,26 @@ export function TaskCard(props: {
179180
},
180181
planningStep: true,
181182
finalReviewStep: true,
182-
dumpDebugLog: true,
183183
validator: aiCollabLlmTreeNodeValidator,
184+
debugEventLogHandler: (event) => {
185+
console.log(`Received event: ${event.eventName}`);
186+
if (
187+
event.eventName === "APPLIED_EDIT_SUCCESS" ||
188+
event.eventName === "APPLIED_EDIT_FAILURE"
189+
) {
190+
console.log(
191+
`${
192+
event.eventName === "APPLIED_EDIT_SUCCESS"
193+
? "Succesfully applied"
194+
: "Failed to appply"
195+
} tree edit: ${JSON.stringify(
196+
(event as unknown as ApplyEditSuccess).edit,
197+
undefined,
198+
2,
199+
)}`,
200+
);
201+
}
202+
},
184203
});
185204

186205
if (response.status !== "success") {

examples/apps/ai-collab/src/components/TaskGroup.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
aiCollab,
88
type AiCollabErrorResponse,
99
type AiCollabSuccessResponse,
10+
type ApplyEditSuccess,
1011
type Difference,
1112
SharedTreeBranchManager,
1213
} from "@fluidframework/ai-collab/alpha";
@@ -229,8 +230,26 @@ export function TaskGroup(props: {
229230
},
230231
planningStep: true,
231232
finalReviewStep: false,
232-
dumpDebugLog: true,
233233
validator: aiCollabLlmTreeNodeValidator,
234+
debugEventLogHandler: (event) => {
235+
console.log(`Received event: ${event.eventName}`);
236+
if (
237+
event.eventName === "APPLIED_EDIT_SUCCESS" ||
238+
event.eventName === "APPLIED_EDIT_FAILURE"
239+
) {
240+
console.log(
241+
`${
242+
event.eventName === "APPLIED_EDIT_SUCCESS"
243+
? "Succesfully applied"
244+
: "Failed to appply"
245+
} tree edit: ${JSON.stringify(
246+
(event as unknown as ApplyEditSuccess).edit,
247+
undefined,
248+
2,
249+
)}`,
250+
);
251+
}
252+
},
234253
});
235254

236255
// 3. Handle the response from the ai collaboration

examples/apps/presence-tracker/tests/presenceTracker.test.ts

+13-29
Original file line numberDiff line numberDiff line change
@@ -74,18 +74,6 @@ describe("presence-tracker", () => {
7474
session1id = "session1id needs reloaded";
7575
});
7676

77-
async function throwWithPageAttendeeData(message: string, page: Page) {
78-
const attendeeData = await page.evaluate(() => ({
79-
/* eslint-disable @typescript-eslint/dot-notation */
80-
attendeeCount: `${window["fluidSessionAttendeeCount"]}`,
81-
attendees: window["fluidSessionAttendees"] ?? {},
82-
attendeeJoinedCalled: `${window["fluidAttendeeJoinedCalled"]}`,
83-
attendeeDisconnectedCalled: `${window["fluidAttendeeDisconnectedCalled"]}`,
84-
/* eslint-enable @typescript-eslint/dot-notation */
85-
}));
86-
throw new Error(`${message} (${JSON.stringify(attendeeData)})`);
87-
}
88-
8977
describe("Single client", () => {
9078
it("Document is connected", async () => {
9179
// Page's url should be updated to have document id
@@ -117,19 +105,10 @@ describe("presence-tracker", () => {
117105
});
118106

119107
it("First client shows single client connected", async () => {
120-
await page
121-
// eslint-disable-next-line @typescript-eslint/dot-notation
122-
.waitForFunction(() => window["fluidSessionAttendeeCount"] === 1, {
123-
// While the expected state should be immediately true, this timeout
124-
// appears to apply to the entire evaluation period which may not return
125-
// in 50ms 6-9% of the time (even if the evaluation is a simple `true`).
126-
// All evaluations of state when this fails have show an attendee count
127-
// of 1. So use 100ms which appears reliable.
128-
timeout: 100,
129-
})
130-
.catch(async () => {
131-
await throwWithPageAttendeeData("Attendee count is not 1", page);
132-
});
108+
// eslint-disable-next-line @typescript-eslint/dot-notation, @typescript-eslint/no-unsafe-return
109+
const attendeeCount = await page.evaluate(() => window["fluidSessionAttendeeCount"]);
110+
expect(attendeeCount).toBe(1);
111+
133112
const elementHandle = await page.waitForFunction(() =>
134113
document.getElementById("focus-div"),
135114
);
@@ -190,23 +169,28 @@ describe("presence-tracker", () => {
190169
timeoutErrorMessage: string,
191170
) {
192171
/* Disabled for common window["foo"] access. */
172+
/* eslint-disable @typescript-eslint/dot-notation */
193173
await page
194174
.waitForFunction(
195-
// Note: this is a block disable instead of line suppression as Biome reformats comment away from line
196-
/* eslint-disable @typescript-eslint/dot-notation */
197175
(expectation) =>
198176
(
199177
window["fluidSessionAttendeeCheck"] as (
200178
expected: Record<string, string>,
201179
) => boolean
202180
)(expectation),
203-
/* eslint-enable @typescript-eslint/dot-notation */
204181
{ timeout: 100 },
205182
expected,
206183
)
207184
.catch(async () => {
208-
await throwWithPageAttendeeData(timeoutErrorMessage, page);
185+
const attendeeData = await page.evaluate(() => ({
186+
attendeeCount: `${window["fluidSessionAttendeeCount"]}`,
187+
attendees: window["fluidSessionAttendees"] ?? {},
188+
attendeeJoinedCalled: `${window["fluidAttendeeJoinedCalled"]}`,
189+
attendeeDisconnectedCalled: `${window["fluidAttendeeDisconnectedCalled"]}`,
190+
}));
191+
throw new Error(`${timeoutErrorMessage} (${JSON.stringify(attendeeData)})`);
209192
});
193+
/* eslint-enable @typescript-eslint/dot-notation */
210194
}
211195

212196
it("Second client shows two clients connected", async () => {

examples/utils/import-testing/src/test/importer.spec.ts

+8
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,19 @@
55

66
import { strict as assert } from "node:assert";
77

8+
// eslint-disable-next-line import/no-internal-modules
9+
import { JsonArray } from "@fluidframework/tree/internal";
10+
811
import { RecursiveMap } from "../testExports.js";
912

1013
describe("import tests", () => {
1114
it("recursive map", () => {
1215
const r = new RecursiveMap([["", new RecursiveMap([])]]);
1316
assert.equal(r.size, 1);
1417
});
18+
19+
it("JsonArray", () => {
20+
const r = new JsonArray([1]);
21+
assert.equal(r[0], 1);
22+
});
1523
});

examples/utils/import-testing/src/testExports.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ export const config = new TreeViewConfiguration({ schema: Canvas });
8181

8282
// Recursive cases
8383
// This lint rule doesn't work well with our schema when using the lazy format
84-
/* eslint-disable @typescript-eslint/explicit-function-return-type */
8584
export class RecursiveObject extends schema.objectRecursive("RO", {
8685
x: [() => RecursiveObject, schema.number],
8786
}) {}
@@ -100,10 +99,8 @@ export class RecursiveMap extends schema.mapRecursive("RM", [() => RecursiveMap]
10099
* Workaround to avoid
101100
* `error TS2310: Type 'RecursiveArray' recursively references itself as a base type.` in the d.ts file.
102101
*/
103-
export declare const _RecursiveArrayWorkaround: FixRecursiveArraySchema<typeof RecursiveArray>;
102+
export declare type _RecursiveArrayWorkaround = FixRecursiveArraySchema<typeof RecursiveArray>;
104103
export class RecursiveArray extends schema.arrayRecursive("RA", [() => RecursiveArray]) {}
105104
{
106105
type _check = ValidateRecursiveSchema<typeof RecursiveArray>;
107106
}
108-
109-
/* eslint-enable @typescript-eslint/explicit-function-return-type */

packages/dds/map/src/directory.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -687,7 +687,7 @@ export class SharedDirectory
687687
/**
688688
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.reSubmitCore}
689689
*/
690-
protected reSubmitCore(content: unknown, localOpMetadata: unknown): void {
690+
protected override reSubmitCore(content: unknown, localOpMetadata: unknown): void {
691691
const message = content as IDirectoryOperation;
692692
const handler = this.messageHandlers.get(message.type);
693693
assert(handler !== undefined, 0x00d /* Missing message handler for message type */);
@@ -816,7 +816,7 @@ export class SharedDirectory
816816
/**
817817
* {@inheritDoc @fluidframework/shared-object-base#SharedObject.rollback}
818818
*/
819-
protected rollback(content: unknown, localOpMetadata: unknown): void {
819+
protected override rollback(content: unknown, localOpMetadata: unknown): void {
820820
const op: IDirectoryOperation = content as IDirectoryOperation;
821821
const subdir = this.getWorkingDirectory(op.path) as SubDirectory | undefined;
822822
if (subdir) {

0 commit comments

Comments
 (0)