From 2b4f4d5e5ec47c9a5eded1f2667bcdfd7844bc5b Mon Sep 17 00:00:00 2001 From: Kian Thompson Date: Mon, 27 Apr 2026 16:13:20 -0700 Subject: [PATCH 1/3] [fluid-runner] Expose createFluidRunnerContainerAndExecute and createFluidRunnerLogger Co-authored-by: Copilot --- .changeset/expose-fluid-runner-helpers.md | 9 +++ .../fluid-runner.legacy.beta.api.md | 21 ++++++ packages/tools/fluid-runner/src/exportFile.ts | 70 +++++++++++++------ packages/tools/fluid-runner/src/index.ts | 4 +- .../fluid-runner/src/logger/fileLogger.ts | 6 +- .../fluid-runner/src/logger/loggerUtils.ts | 31 ++++---- .../src/parseBundleAndExportFile.ts | 20 ++++-- .../fluid-runner/src/test/exportFile.spec.ts | 4 +- .../fluid-runner/src/test/loggerUtils.spec.ts | 4 +- 9 files changed, 123 insertions(+), 46 deletions(-) create mode 100644 .changeset/expose-fluid-runner-helpers.md diff --git a/.changeset/expose-fluid-runner-helpers.md b/.changeset/expose-fluid-runner-helpers.md new file mode 100644 index 000000000000..33f02c85ad76 --- /dev/null +++ b/.changeset/expose-fluid-runner-helpers.md @@ -0,0 +1,9 @@ +--- +"@fluidframework/fluid-runner": minor +"__section": other +--- +Expose `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` as `@legacy @beta` + +The lower-level helpers `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` from `@fluidframework/fluid-runner` are now part of the legacy/beta public API surface. Use `createFluidRunnerContainerAndExecute` to load a container from an ODSP snapshot and run caller-provided code against it. Use `createFluidRunnerLogger` to obtain a file-backed telemetry logger that can be passed to `createFluidRunnerContainerAndExecute`. + +The `IFileLogger` and `ITelemetryOptions` types — already exported from the package — have likewise been promoted from `@internal` to `@legacy @beta` so they can be referenced by consumers of these APIs. The signatures of `createFluidRunnerLogger` and `createFluidRunnerContainerAndExecute` use the public `ITelemetryBaseLogger` type from `@fluidframework/core-interfaces`. diff --git a/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md b/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md index ebc8f8ff21c1..7fc85931b8b2 100644 --- a/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md +++ b/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md @@ -4,6 +4,15 @@ ```ts +// @beta @legacy +export function createFluidRunnerContainerAndExecute(localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, logger: ITelemetryBaseLogger, options?: string, timeout?: number, disableNetworkFetch?: boolean): Promise; + +// @beta @legacy +export function createFluidRunnerLogger(filePath: string, options?: ITelemetryOptions): { + logger: ITelemetryBaseLogger; + fileLogger: IFileLogger; +}; + // @beta @legacy (undocumented) export type IExportFileResponse = IExportFileResponseSuccess | IExportFileResponseFailure; @@ -25,6 +34,11 @@ export interface IExportFileResponseSuccess { success: true; } +// @beta @legacy +export interface IFileLogger extends ITelemetryBaseLogger { + close(): Promise; +} + // @beta @legacy export interface IFluidFileConverter { execute(container: IContainer, options?: string): Promise; @@ -32,6 +46,13 @@ export interface IFluidFileConverter { getScope?(logger: ITelemetryBaseLogger): Promise; } +// @beta @legacy +export interface ITelemetryOptions { + defaultProps?: Record; + eventsPerFlush?: number; + outputFormat?: OutputFormat; +} + // @beta @legacy export enum OutputFormat { // (undocumented) diff --git a/packages/tools/fluid-runner/src/exportFile.ts b/packages/tools/fluid-runner/src/exportFile.ts index 6f3226f3e90a..19f0c8cb7711 100644 --- a/packages/tools/fluid-runner/src/exportFile.ts +++ b/packages/tools/fluid-runner/src/exportFile.ts @@ -11,17 +11,18 @@ import { waitContainerToCatchUp, type ILoaderProps, } from "@fluidframework/container-loader/internal"; +import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; import { createLocalOdspDocumentServiceFactory } from "@fluidframework/odsp-driver/internal"; -import { - type ITelemetryLoggerExt, - PerformanceEvent, -} from "@fluidframework/telemetry-utils/internal"; +import { createChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils/internal"; import type { IFluidFileConverter } from "./codeLoaderBundle.js"; import { FakeUrlResolver } from "./fakeUrlResolver.js"; /* eslint-disable import-x/no-internal-modules */ import type { ITelemetryOptions } from "./logger/fileLogger.js"; -import { createLogger, getTelemetryFileValidationError } from "./logger/loggerUtils.js"; +import { + createFluidRunnerLogger, + getTelemetryFileValidationError, +} from "./logger/loggerUtils.js"; import { getArgsValidationError, getSnapshotFileContent, timeoutPromise } from "./utils.js"; /* eslint-enable import-x/no-internal-modules */ @@ -50,7 +51,8 @@ export interface IExportFileResponseFailure { const clientArgsValidationError = "Client_ArgsValidationError"; /** - * Execute code on Container based on ODSP snapshot and write result to file + * Execute code on a Fluid {@link @fluidframework/container-definitions#IContainer} loaded from an ODSP snapshot + * file and write the resulting string to disk. * @internal */ export async function exportFile( @@ -68,7 +70,11 @@ export async function exportFile( const eventName = clientArgsValidationError; return { success: false, eventName, errorMessage: telemetryArgError }; } - const { fileLogger, logger } = createLogger(telemetryFile, telemetryOptions); + const { fileLogger, logger: baseLogger } = createFluidRunnerLogger( + telemetryFile, + telemetryOptions, + ); + const logger = createChildLogger({ logger: baseLogger }); try { return await PerformanceEvent.timedExecAsync( @@ -84,7 +90,7 @@ export async function exportFile( fs.writeFileSync( outputFile, - await createContainerAndExecute( + await createFluidRunnerContainerAndExecute( getSnapshotFileContent(inputFile), fluidFileConverter, logger, @@ -107,18 +113,38 @@ export async function exportFile( } /** - * Create the container based on an ODSP snapshot and execute code on it - * @returns result of execution - * @internal + * Create a Fluid {@link @fluidframework/container-definitions#IContainer} from an ODSP snapshot and run + * caller-provided code against it. + * + * @remarks + * The container is loaded with `opsBeforeReturn: "cached"` and {@link @fluidframework/container-loader#waitContainerToCatchUp} + * is invoked before {@link IFluidFileConverter.execute} runs. The container is disposed once `execute` resolves + * (or rejects). + * + * @param localOdspSnapshot - The ODSP snapshot to load the container from. May be either the JSON snapshot + * as a string or the binary snapshot as a `Uint8Array`. + * @param fluidFileConverter - Caller-provided code loader and execution logic. See {@link IFluidFileConverter}. + * @param logger - Telemetry logger that will receive events emitted during load and execution. Typically + * obtained from {@link createFluidRunnerLogger}. + * @param options - Opaque, caller-defined string passed through to {@link IFluidFileConverter.execute}. + * @param timeout - Optional timeout in milliseconds. If the operation does not complete within this period + * the returned promise rejects. When omitted, no timeout is applied. + * @param disableNetworkFetch - When `true`, replaces `global.fetch` with an implementation that throws, + * ensuring the container load is fully serviced from the provided snapshot. Defaults to `false`. + * @returns The string result returned by {@link IFluidFileConverter.execute}. + * + * @legacy + * @beta */ -export async function createContainerAndExecute( +export async function createFluidRunnerContainerAndExecute( localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, - logger: ITelemetryLoggerExt, + logger: ITelemetryBaseLogger, options?: string, timeout?: number, disableNetworkFetch: boolean = false, ): Promise { + const extLogger = createChildLogger({ logger }); const fn = async (): Promise => { if (disableNetworkFetch) { global.fetch = async () => { @@ -145,13 +171,17 @@ export async function createContainerAndExecute( }); await waitContainerToCatchUp(container); - return PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () => { - try { - return await fluidFileConverter.execute(container, options); - } finally { - container.dispose(); - } - }); + return PerformanceEvent.timedExecAsync( + extLogger, + { eventName: "ExportFile" }, + async () => { + try { + return await fluidFileConverter.execute(container, options); + } finally { + container.dispose(); + } + }, + ); }; // eslint-disable-next-line unicorn/prefer-ternary diff --git a/packages/tools/fluid-runner/src/index.ts b/packages/tools/fluid-runner/src/index.ts index a5cc0a08e213..b84c70857836 100644 --- a/packages/tools/fluid-runner/src/index.ts +++ b/packages/tools/fluid-runner/src/index.ts @@ -6,7 +6,7 @@ /* eslint-disable import-x/no-internal-modules */ export type { ICodeLoaderBundle, IFluidFileConverter } from "./codeLoaderBundle.js"; export { - createContainerAndExecute, + createFluidRunnerContainerAndExecute, exportFile, type IExportFileResponse, type IExportFileResponseSuccess, @@ -19,7 +19,7 @@ export { type IFileLogger, } from "./logger/fileLogger.js"; export { - createLogger, + createFluidRunnerLogger, getTelemetryFileValidationError, validateAndParseTelemetryOptions, } from "./logger/loggerUtils.js"; diff --git a/packages/tools/fluid-runner/src/logger/fileLogger.ts b/packages/tools/fluid-runner/src/logger/fileLogger.ts index 597f5f080411..2cce895f75a9 100644 --- a/packages/tools/fluid-runner/src/logger/fileLogger.ts +++ b/packages/tools/fluid-runner/src/logger/fileLogger.ts @@ -7,7 +7,8 @@ import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; /** * Contract for logger that writes telemetry to a file - * @internal + * @legacy + * @beta */ export interface IFileLogger extends ITelemetryBaseLogger { /** @@ -27,7 +28,8 @@ export enum OutputFormat { /** * Options to provide upon creation of IFileLogger - * @internal + * @legacy + * @beta */ export interface ITelemetryOptions { /** Desired output format used to create a specific IFileLogger implementation */ diff --git a/packages/tools/fluid-runner/src/logger/loggerUtils.ts b/packages/tools/fluid-runner/src/logger/loggerUtils.ts index 9a523a52fa8e..2f20c79f15c9 100644 --- a/packages/tools/fluid-runner/src/logger/loggerUtils.ts +++ b/packages/tools/fluid-runner/src/logger/loggerUtils.ts @@ -5,33 +5,38 @@ import * as fs from "fs"; -import { - type ITelemetryLoggerExt, - createChildLogger, -} from "@fluidframework/telemetry-utils/internal"; +import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; +import { createChildLogger } from "@fluidframework/telemetry-utils/internal"; import { CSVFileLogger } from "./csvFileLogger.js"; import { type IFileLogger, type ITelemetryOptions, OutputFormat } from "./fileLogger.js"; import { JSONFileLogger } from "./jsonFileLogger.js"; /** - * Create an {@link @fluidframework/telemetry-utils#ITelemetryLoggerExt} wrapped around provided {@link IFileLogger}. + * Create an {@link @fluidframework/telemetry-utils#ITelemetryLoggerExt} wrapped around an {@link IFileLogger} + * that writes telemetry events to the file at `filePath`. * * @remarks + * All telemetry events should be sent through the returned `logger`. The returned `fileLogger` is the + * underlying sink — its `close()` method must be called at the end of execution to flush any buffered + * events to disk. * - * It is expected that all events be sent through the returned "logger" value. + * If `options.outputFormat` is not supplied, telemetry is written as JSON. Use {@link OutputFormat.CSV} + * to write CSV instead. See {@link ITelemetryOptions} for supported options including default properties + * applied to every event and flush batching. * - * The "fileLogger" value should have its "close()" method called at the end of execution. + * @param filePath - Path to the file telemetry will be written to. Must not already exist. + * @param options - Optional telemetry configuration. See {@link ITelemetryOptions}. + * @returns The wrapped telemetry logger to send events through, and the underlying `IFileLogger` + * which must be closed when telemetry collection is finished. * - * Note: if an output format is not supplied, default is JSON. - * - * @returns Both the `IFileLogger` implementation and `ITelemetryLoggerExt` wrapper to be called. - * @internal + * @legacy + * @beta */ -export function createLogger( +export function createFluidRunnerLogger( filePath: string, options?: ITelemetryOptions, -): { logger: ITelemetryLoggerExt; fileLogger: IFileLogger } { +): { logger: ITelemetryBaseLogger; fileLogger: IFileLogger } { const fileLogger = options?.outputFormat === OutputFormat.CSV ? new CSVFileLogger(filePath, options?.eventsPerFlush, options?.defaultProps) diff --git a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts index 509a62464dff..afa02176cdf5 100644 --- a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts +++ b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts @@ -6,13 +6,19 @@ import * as fs from "node:fs"; import * as path from "node:path"; -import { PerformanceEvent } from "@fluidframework/telemetry-utils/internal"; +import { createChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils/internal"; import { isCodeLoaderBundle, isFluidFileConverter } from "./codeLoaderBundle.js"; -import { type IExportFileResponse, createContainerAndExecute } from "./exportFile.js"; +import { + type IExportFileResponse, + createFluidRunnerContainerAndExecute, +} from "./exportFile.js"; /* eslint-disable import-x/no-internal-modules */ import type { ITelemetryOptions } from "./logger/fileLogger.js"; -import { createLogger, getTelemetryFileValidationError } from "./logger/loggerUtils.js"; +import { + createFluidRunnerLogger, + getTelemetryFileValidationError, +} from "./logger/loggerUtils.js"; /* eslint-enable import-x/no-internal-modules */ import { getArgsValidationError, getSnapshotFileContent } from "./utils.js"; @@ -38,7 +44,11 @@ export async function parseBundleAndExportFile( const eventName = clientArgsValidationError; return { success: false, eventName, errorMessage: telemetryArgError }; } - const { fileLogger, logger } = createLogger(telemetryFile, telemetryOptions); + const { fileLogger, logger: baseLogger } = createFluidRunnerLogger( + telemetryFile, + telemetryOptions, + ); + const logger = createChildLogger({ logger: baseLogger }); try { return await PerformanceEvent.timedExecAsync( @@ -76,7 +86,7 @@ export async function parseBundleAndExportFile( fs.writeFileSync( outputFile, - await createContainerAndExecute( + await createFluidRunnerContainerAndExecute( getSnapshotFileContent(inputFile), fluidExport, logger, diff --git a/packages/tools/fluid-runner/src/test/exportFile.spec.ts b/packages/tools/fluid-runner/src/test/exportFile.spec.ts index caa0c732c3d0..913575a30573 100644 --- a/packages/tools/fluid-runner/src/test/exportFile.spec.ts +++ b/packages/tools/fluid-runner/src/test/exportFile.spec.ts @@ -10,7 +10,7 @@ import path from "path"; import { MockLogger } from "@fluidframework/telemetry-utils/internal"; /* eslint-disable import-x/no-internal-modules */ -import { createContainerAndExecute, exportFile } from "../exportFile.js"; +import { createFluidRunnerContainerAndExecute, exportFile } from "../exportFile.js"; import { getSnapshotFileContent } from "../utils.js"; import { _dirname } from "./dirname.cjs"; @@ -56,7 +56,7 @@ describe("exportFile", () => { }); it("Execution result is correct", async () => { - const result = await createContainerAndExecute( + const result = await createFluidRunnerContainerAndExecute( getSnapshotFileContent(path.join(snapshotFolder, snapshotFileName)), fluidExport, new MockLogger().toTelemetryLogger(), diff --git a/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts b/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts index 4a0332a56516..8decf0389764 100644 --- a/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts +++ b/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts @@ -9,7 +9,7 @@ import path from "path"; /* eslint-disable import-x/no-internal-modules */ import { OutputFormat } from "../logger/fileLogger.js"; import { - createLogger, + createFluidRunnerLogger, getTelemetryFileValidationError, validateAndParseTelemetryOptions, } from "../logger/loggerUtils.js"; @@ -176,7 +176,7 @@ describe("logger utils", () => { describe("createLogger", () => { [-1, 0, 1, 25].forEach((eventsPerFlush) => { it(`sets eventsPerFlush [${eventsPerFlush}] properly`, () => { - const { fileLogger } = createLogger("fake/path", { + const { fileLogger } = createFluidRunnerLogger("fake/path", { outputFormat: OutputFormat.CSV, eventsPerFlush, }); From 01dcc3cccfae4b4d0be64b8f6383aa02fbd9e44f Mon Sep 17 00:00:00 2001 From: Kian Thompson Date: Tue, 28 Apr 2026 09:46:11 -0700 Subject: [PATCH 2/3] Code review Co-authored-by: Copilot --- .changeset/expose-fluid-runner-helpers.md | 2 +- .../fluid-runner.legacy.beta.api.md | 18 ++++++------ packages/tools/fluid-runner/src/exportFile.ts | 28 ++++++++----------- packages/tools/fluid-runner/src/index.ts | 2 +- .../fluid-runner/src/logger/fileLogger.ts | 2 +- .../fluid-runner/src/logger/loggerUtils.ts | 18 ++++++++---- .../src/parseBundleAndExportFile.ts | 4 +-- .../fluid-runner/src/test/loggerUtils.spec.ts | 2 +- 8 files changed, 39 insertions(+), 37 deletions(-) diff --git a/.changeset/expose-fluid-runner-helpers.md b/.changeset/expose-fluid-runner-helpers.md index 33f02c85ad76..2f2b0cdc2569 100644 --- a/.changeset/expose-fluid-runner-helpers.md +++ b/.changeset/expose-fluid-runner-helpers.md @@ -6,4 +6,4 @@ Expose `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` as ` The lower-level helpers `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` from `@fluidframework/fluid-runner` are now part of the legacy/beta public API surface. Use `createFluidRunnerContainerAndExecute` to load a container from an ODSP snapshot and run caller-provided code against it. Use `createFluidRunnerLogger` to obtain a file-backed telemetry logger that can be passed to `createFluidRunnerContainerAndExecute`. -The `IFileLogger` and `ITelemetryOptions` types — already exported from the package — have likewise been promoted from `@internal` to `@legacy @beta` so they can be referenced by consumers of these APIs. The signatures of `createFluidRunnerLogger` and `createFluidRunnerContainerAndExecute` use the public `ITelemetryBaseLogger` type from `@fluidframework/core-interfaces`. +The `IFileLogger` and `IFileLoggerTelemetryOptions` types — already exported from the package — have likewise been promoted from `@internal` to `@legacy @beta` so they can be referenced by consumers of these APIs. The signatures of `createFluidRunnerLogger` and `createFluidRunnerContainerAndExecute` use the public `ITelemetryBaseLogger` type from `@fluidframework/core-interfaces`. diff --git a/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md b/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md index 7fc85931b8b2..d716f800945e 100644 --- a/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md +++ b/packages/tools/fluid-runner/api-report/fluid-runner.legacy.beta.api.md @@ -5,10 +5,10 @@ ```ts // @beta @legacy -export function createFluidRunnerContainerAndExecute(localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, logger: ITelemetryBaseLogger, options?: string, timeout?: number, disableNetworkFetch?: boolean): Promise; +export function createFluidRunnerContainerAndExecute(localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, baseLogger: ITelemetryBaseLogger, options?: string, timeout?: number, disableNetworkFetch?: boolean): Promise; // @beta @legacy -export function createFluidRunnerLogger(filePath: string, options?: ITelemetryOptions): { +export function createFluidRunnerLogger(filePath: string, options?: IFileLoggerTelemetryOptions): { logger: ITelemetryBaseLogger; fileLogger: IFileLogger; }; @@ -39,6 +39,13 @@ export interface IFileLogger extends ITelemetryBaseLogger { close(): Promise; } +// @beta @legacy +export interface IFileLoggerTelemetryOptions { + defaultProps?: Record; + eventsPerFlush?: number; + outputFormat?: OutputFormat; +} + // @beta @legacy export interface IFluidFileConverter { execute(container: IContainer, options?: string): Promise; @@ -46,13 +53,6 @@ export interface IFluidFileConverter { getScope?(logger: ITelemetryBaseLogger): Promise; } -// @beta @legacy -export interface ITelemetryOptions { - defaultProps?: Record; - eventsPerFlush?: number; - outputFormat?: OutputFormat; -} - // @beta @legacy export enum OutputFormat { // (undocumented) diff --git a/packages/tools/fluid-runner/src/exportFile.ts b/packages/tools/fluid-runner/src/exportFile.ts index 19f0c8cb7711..31610f249ec0 100644 --- a/packages/tools/fluid-runner/src/exportFile.ts +++ b/packages/tools/fluid-runner/src/exportFile.ts @@ -18,7 +18,7 @@ import { createChildLogger, PerformanceEvent } from "@fluidframework/telemetry-u import type { IFluidFileConverter } from "./codeLoaderBundle.js"; import { FakeUrlResolver } from "./fakeUrlResolver.js"; /* eslint-disable import-x/no-internal-modules */ -import type { ITelemetryOptions } from "./logger/fileLogger.js"; +import type { IFileLoggerTelemetryOptions } from "./logger/fileLogger.js"; import { createFluidRunnerLogger, getTelemetryFileValidationError, @@ -61,7 +61,7 @@ export async function exportFile( outputFile: string, telemetryFile: string, options?: string, - telemetryOptions?: ITelemetryOptions, + telemetryOptions?: IFileLoggerTelemetryOptions, timeout?: number, disableNetworkFetch?: boolean, ): Promise { @@ -124,7 +124,7 @@ export async function exportFile( * @param localOdspSnapshot - The ODSP snapshot to load the container from. May be either the JSON snapshot * as a string or the binary snapshot as a `Uint8Array`. * @param fluidFileConverter - Caller-provided code loader and execution logic. See {@link IFluidFileConverter}. - * @param logger - Telemetry logger that will receive events emitted during load and execution. Typically + * @param baseLogger - Telemetry logger that will receive events emitted during load and execution. Typically * obtained from {@link createFluidRunnerLogger}. * @param options - Opaque, caller-defined string passed through to {@link IFluidFileConverter.execute}. * @param timeout - Optional timeout in milliseconds. If the operation does not complete within this period @@ -139,12 +139,12 @@ export async function exportFile( export async function createFluidRunnerContainerAndExecute( localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, - logger: ITelemetryBaseLogger, + baseLogger: ITelemetryBaseLogger, options?: string, timeout?: number, disableNetworkFetch: boolean = false, ): Promise { - const extLogger = createChildLogger({ logger }); + const logger = createChildLogger({ logger: baseLogger }); const fn = async (): Promise => { if (disableNetworkFetch) { global.fetch = async () => { @@ -171,17 +171,13 @@ export async function createFluidRunnerContainerAndExecute( }); await waitContainerToCatchUp(container); - return PerformanceEvent.timedExecAsync( - extLogger, - { eventName: "ExportFile" }, - async () => { - try { - return await fluidFileConverter.execute(container, options); - } finally { - container.dispose(); - } - }, - ); + return PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () => { + try { + return await fluidFileConverter.execute(container, options); + } finally { + container.dispose(); + } + }); }; // eslint-disable-next-line unicorn/prefer-ternary diff --git a/packages/tools/fluid-runner/src/index.ts b/packages/tools/fluid-runner/src/index.ts index b84c70857836..1bc0df5eb319 100644 --- a/packages/tools/fluid-runner/src/index.ts +++ b/packages/tools/fluid-runner/src/index.ts @@ -15,7 +15,7 @@ export { export { fluidRunner } from "./fluidRunner.js"; export { OutputFormat, - type ITelemetryOptions, + type IFileLoggerTelemetryOptions, type IFileLogger, } from "./logger/fileLogger.js"; export { diff --git a/packages/tools/fluid-runner/src/logger/fileLogger.ts b/packages/tools/fluid-runner/src/logger/fileLogger.ts index 2cce895f75a9..34f32ab16752 100644 --- a/packages/tools/fluid-runner/src/logger/fileLogger.ts +++ b/packages/tools/fluid-runner/src/logger/fileLogger.ts @@ -31,7 +31,7 @@ export enum OutputFormat { * @legacy * @beta */ -export interface ITelemetryOptions { +export interface IFileLoggerTelemetryOptions { /** Desired output format used to create a specific IFileLogger implementation */ outputFormat?: OutputFormat; diff --git a/packages/tools/fluid-runner/src/logger/loggerUtils.ts b/packages/tools/fluid-runner/src/logger/loggerUtils.ts index 2f20c79f15c9..b6de32279aef 100644 --- a/packages/tools/fluid-runner/src/logger/loggerUtils.ts +++ b/packages/tools/fluid-runner/src/logger/loggerUtils.ts @@ -9,11 +9,15 @@ import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; import { createChildLogger } from "@fluidframework/telemetry-utils/internal"; import { CSVFileLogger } from "./csvFileLogger.js"; -import { type IFileLogger, type ITelemetryOptions, OutputFormat } from "./fileLogger.js"; +import { + type IFileLogger, + type IFileLoggerTelemetryOptions, + OutputFormat, +} from "./fileLogger.js"; import { JSONFileLogger } from "./jsonFileLogger.js"; /** - * Create an {@link @fluidframework/telemetry-utils#ITelemetryLoggerExt} wrapped around an {@link IFileLogger} + * Create an {@link @fluidframework/core-interfaces#ITelemetryBaseLogger} wrapped around an {@link IFileLogger} * that writes telemetry events to the file at `filePath`. * * @remarks @@ -22,11 +26,11 @@ import { JSONFileLogger } from "./jsonFileLogger.js"; * events to disk. * * If `options.outputFormat` is not supplied, telemetry is written as JSON. Use {@link OutputFormat.CSV} - * to write CSV instead. See {@link ITelemetryOptions} for supported options including default properties + * to write CSV instead. See {@link IFileLoggerTelemetryOptions} for supported options including default properties * applied to every event and flush batching. * * @param filePath - Path to the file telemetry will be written to. Must not already exist. - * @param options - Optional telemetry configuration. See {@link ITelemetryOptions}. + * @param options - Optional telemetry configuration. See {@link IFileLoggerTelemetryOptions}. * @returns The wrapped telemetry logger to send events through, and the underlying `IFileLogger` * which must be closed when telemetry collection is finished. * @@ -35,7 +39,7 @@ import { JSONFileLogger } from "./jsonFileLogger.js"; */ export function createFluidRunnerLogger( filePath: string, - options?: ITelemetryOptions, + options?: IFileLoggerTelemetryOptions, ): { logger: ITelemetryBaseLogger; fileLogger: IFileLogger } { const fileLogger = options?.outputFormat === OutputFormat.CSV @@ -78,7 +82,9 @@ export function validateAndParseTelemetryOptions( format?: string, props?: (string | number)[], eventsPerFlush?: number, -): { success: false; error: string } | { success: true; telemetryOptions: ITelemetryOptions } { +): + | { success: false; error: string } + | { success: true; telemetryOptions: IFileLoggerTelemetryOptions } { let outputFormat: OutputFormat | undefined; const defaultProps: Record = {}; diff --git a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts index afa02176cdf5..74e5b2b23f34 100644 --- a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts +++ b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts @@ -14,7 +14,7 @@ import { createFluidRunnerContainerAndExecute, } from "./exportFile.js"; /* eslint-disable import-x/no-internal-modules */ -import type { ITelemetryOptions } from "./logger/fileLogger.js"; +import type { IFileLoggerTelemetryOptions } from "./logger/fileLogger.js"; import { createFluidRunnerLogger, getTelemetryFileValidationError, @@ -35,7 +35,7 @@ export async function parseBundleAndExportFile( outputFile: string, telemetryFile: string, options?: string, - telemetryOptions?: ITelemetryOptions, + telemetryOptions?: IFileLoggerTelemetryOptions, timeout?: number, disableNetworkFetch?: boolean, ): Promise { diff --git a/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts b/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts index 8decf0389764..c83c7dd9b1e3 100644 --- a/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts +++ b/packages/tools/fluid-runner/src/test/loggerUtils.spec.ts @@ -173,7 +173,7 @@ describe("logger utils", () => { }); }); - describe("createLogger", () => { + describe("createFluidRunnerLogger", () => { [-1, 0, 1, 25].forEach((eventsPerFlush) => { it(`sets eventsPerFlush [${eventsPerFlush}] properly`, () => { const { fileLogger } = createFluidRunnerLogger("fake/path", { From d8cc3c830d1abd778f5051b25156c798157301e7 Mon Sep 17 00:00:00 2001 From: Kian Thompson <102998837+kian-thompson@users.noreply.github.com> Date: Tue, 9 Jun 2026 23:40:35 +0000 Subject: [PATCH 3/3] Add @deprecated @internal back-compat aliases for renamed fluid-runner helpers Keep the previous names available as deprecated @internal aliases so existing internal consumers continue to work: - createContainerAndExecute -> createFluidRunnerContainerAndExecute - createLogger -> createFluidRunnerLogger - ITelemetryOptions -> IFileLoggerTelemetryOptions The deprecated functions retain the original implementations. The new @legacy @beta exports are thin wrappers that take/return the public ITelemetryBaseLogger surface. Also fixes a few coupled bugs introduced by the rename (missing createChildLogger import, stale baseLogger reference, stale ITelemetryOptions type reference) and updates the changeset to document the back-compat aliases. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/expose-fluid-runner-helpers.md | 7 ++++ packages/tools/fluid-runner/src/exportFile.ts | 39 +++++++++++++++---- packages/tools/fluid-runner/src/index.ts | 3 ++ .../fluid-runner/src/logger/fileLogger.ts | 7 ++++ .../fluid-runner/src/logger/loggerUtils.ts | 33 ++++++++++++++-- .../src/parseBundleAndExportFile.ts | 18 ++++----- 6 files changed, 86 insertions(+), 21 deletions(-) diff --git a/.changeset/expose-fluid-runner-helpers.md b/.changeset/expose-fluid-runner-helpers.md index 2f2b0cdc2569..baaa6eccd879 100644 --- a/.changeset/expose-fluid-runner-helpers.md +++ b/.changeset/expose-fluid-runner-helpers.md @@ -7,3 +7,10 @@ Expose `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` as ` The lower-level helpers `createFluidRunnerContainerAndExecute` and `createFluidRunnerLogger` from `@fluidframework/fluid-runner` are now part of the legacy/beta public API surface. Use `createFluidRunnerContainerAndExecute` to load a container from an ODSP snapshot and run caller-provided code against it. Use `createFluidRunnerLogger` to obtain a file-backed telemetry logger that can be passed to `createFluidRunnerContainerAndExecute`. The `IFileLogger` and `IFileLoggerTelemetryOptions` types — already exported from the package — have likewise been promoted from `@internal` to `@legacy @beta` so they can be referenced by consumers of these APIs. The signatures of `createFluidRunnerLogger` and `createFluidRunnerContainerAndExecute` use the public `ITelemetryBaseLogger` type from `@fluidframework/core-interfaces`. + +For back-compatibility, the previous `@internal` names are retained as `@deprecated` aliases: + +- `createContainerAndExecute` → use `createFluidRunnerContainerAndExecute` +- `createLogger` → use `createFluidRunnerLogger` +- `ITelemetryOptions` → use `IFileLoggerTelemetryOptions` + diff --git a/packages/tools/fluid-runner/src/exportFile.ts b/packages/tools/fluid-runner/src/exportFile.ts index 287573210db1..39cffaa459fc 100644 --- a/packages/tools/fluid-runner/src/exportFile.ts +++ b/packages/tools/fluid-runner/src/exportFile.ts @@ -16,6 +16,7 @@ import { createLocalOdspDocumentServiceFactory } from "@fluidframework/odsp-driv import { type TelemetryLoggerExt, PerformanceEvent, + createChildLogger, } from "@fluidframework/telemetry-utils/internal"; import type { IFluidFileConverter } from "./codeLoaderBundle.js"; @@ -23,7 +24,8 @@ import { FakeUrlResolver } from "./fakeUrlResolver.js"; /* eslint-disable import-x/no-internal-modules */ import type { IFileLoggerTelemetryOptions } from "./logger/fileLogger.js"; import { - createFluidRunnerLogger, + // eslint-disable-next-line import-x/no-deprecated + createLogger, getTelemetryFileValidationError, } from "./logger/loggerUtils.js"; import { getArgsValidationError, getSnapshotFileContent, timeoutPromise } from "./utils.js"; @@ -73,11 +75,8 @@ export async function exportFile( const eventName = clientArgsValidationError; return { success: false, eventName, errorMessage: telemetryArgError }; } - const { fileLogger, logger: baseLogger } = createFluidRunnerLogger( - telemetryFile, - telemetryOptions, - ); - const logger = createChildLogger({ logger: baseLogger }); + // eslint-disable-next-line import-x/no-deprecated + const { fileLogger, logger } = createLogger(telemetryFile, telemetryOptions); try { return await PerformanceEvent.timedExecAsync( @@ -93,7 +92,7 @@ export async function exportFile( fs.writeFileSync( outputFile, - await createFluidRunnerContainerAndExecute( + await createContainerAndExecute( getSnapshotFileContent(inputFile), fluidFileConverter, logger, @@ -140,6 +139,31 @@ export async function exportFile( * @beta */ export async function createFluidRunnerContainerAndExecute( + localOdspSnapshot: string | Uint8Array, + fluidFileConverter: IFluidFileConverter, + baseLogger: ITelemetryBaseLogger, + options?: string, + timeout?: number, + disableNetworkFetch?: boolean, +): Promise { + const logger = createChildLogger({ logger: baseLogger }); + return createContainerAndExecute( + localOdspSnapshot, + fluidFileConverter, + logger, + options, + timeout, + disableNetworkFetch, + ); +} + +/** + * Create the container based on an ODSP snapshot and execute code on it + * @returns result of execution + * @deprecated Use {@link createFluidRunnerContainerAndExecute}. + * @internal + */ +export async function createContainerAndExecute( localOdspSnapshot: string | Uint8Array, fluidFileConverter: IFluidFileConverter, logger: TelemetryLoggerExt, @@ -147,7 +171,6 @@ export async function createFluidRunnerContainerAndExecute( timeout?: number, disableNetworkFetch: boolean = false, ): Promise { - const logger = createChildLogger({ logger: baseLogger }); const fn = async (): Promise => { if (disableNetworkFetch) { global.fetch = async () => { diff --git a/packages/tools/fluid-runner/src/index.ts b/packages/tools/fluid-runner/src/index.ts index 1bc0df5eb319..e2c2bdb291d6 100644 --- a/packages/tools/fluid-runner/src/index.ts +++ b/packages/tools/fluid-runner/src/index.ts @@ -6,6 +6,7 @@ /* eslint-disable import-x/no-internal-modules */ export type { ICodeLoaderBundle, IFluidFileConverter } from "./codeLoaderBundle.js"; export { + createContainerAndExecute, createFluidRunnerContainerAndExecute, exportFile, type IExportFileResponse, @@ -17,9 +18,11 @@ export { OutputFormat, type IFileLoggerTelemetryOptions, type IFileLogger, + type ITelemetryOptions, } from "./logger/fileLogger.js"; export { createFluidRunnerLogger, + createLogger, getTelemetryFileValidationError, validateAndParseTelemetryOptions, } from "./logger/loggerUtils.js"; diff --git a/packages/tools/fluid-runner/src/logger/fileLogger.ts b/packages/tools/fluid-runner/src/logger/fileLogger.ts index 34f32ab16752..fcca8cfccab9 100644 --- a/packages/tools/fluid-runner/src/logger/fileLogger.ts +++ b/packages/tools/fluid-runner/src/logger/fileLogger.ts @@ -49,3 +49,10 @@ export interface IFileLoggerTelemetryOptions { /** Number of telemetry events per flush to telemetry file */ eventsPerFlush?: number; } + +/** + * Options to provide upon creation of IFileLogger + * @deprecated Use {@link IFileLoggerTelemetryOptions}. + * @internal + */ +export type ITelemetryOptions = IFileLoggerTelemetryOptions; diff --git a/packages/tools/fluid-runner/src/logger/loggerUtils.ts b/packages/tools/fluid-runner/src/logger/loggerUtils.ts index 7321a0885481..e999e197016d 100644 --- a/packages/tools/fluid-runner/src/logger/loggerUtils.ts +++ b/packages/tools/fluid-runner/src/logger/loggerUtils.ts @@ -5,6 +5,7 @@ import * as fs from "fs"; +import type { ITelemetryBaseLogger } from "@fluidframework/core-interfaces"; import { type TelemetryLoggerExt, createChildLogger, @@ -14,12 +15,14 @@ import { CSVFileLogger } from "./csvFileLogger.js"; import { type IFileLogger, type IFileLoggerTelemetryOptions, + // eslint-disable-next-line import-x/no-deprecated + type ITelemetryOptions, OutputFormat, } from "./fileLogger.js"; import { JSONFileLogger } from "./jsonFileLogger.js"; /** - * Create an {@link @fluidframework/telemetry-utils#TelemetryLoggerExt} wrapped around provided {@link IFileLogger}. + * Create a telemetry logger wrapped around an {@link IFileLogger} that writes to the given file path. * * @remarks * All telemetry events should be sent through the returned `logger`. The returned `fileLogger` is the @@ -27,21 +30,43 @@ import { JSONFileLogger } from "./jsonFileLogger.js"; * events to disk. * * If `options.outputFormat` is not supplied, telemetry is written as JSON. Use {@link OutputFormat.CSV} - * to write CSV instead. See {@link IFileLoggerTelemetryOptions} for supported options including default properties - * applied to every event and flush batching. + * to write CSV instead. See {@link IFileLoggerTelemetryOptions} for supported options including default + * properties applied to every event and flush batching. * * @param filePath - Path to the file telemetry will be written to. Must not already exist. * @param options - Optional telemetry configuration. See {@link IFileLoggerTelemetryOptions}. * @returns The wrapped telemetry logger to send events through, and the underlying `IFileLogger` * which must be closed when telemetry collection is finished. * + * @legacy + * @beta + */ +export function createFluidRunnerLogger( + filePath: string, + options?: IFileLoggerTelemetryOptions, +): { logger: ITelemetryBaseLogger; fileLogger: IFileLogger } { + return createLogger(filePath, options); +} + +/** + * Create an {@link @fluidframework/telemetry-utils#TelemetryLoggerExt} wrapped around provided {@link IFileLogger}. + * + * @remarks + * + * It is expected that all events be sent through the returned "logger" value. + * + * The "fileLogger" value should have its "close()" method called at the end of execution. + * * Note: if an output format is not supplied, default is JSON. * * @returns Both the `IFileLogger` implementation and `TelemetryLoggerExt` wrapper to be called. + * + * @deprecated Use {@link createFluidRunnerLogger}. * @internal */ -export function createFluidRunnerLogger( +export function createLogger( filePath: string, + // eslint-disable-next-line import-x/no-deprecated options?: ITelemetryOptions, ): { logger: TelemetryLoggerExt; fileLogger: IFileLogger } { const fileLogger = diff --git a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts index 74e5b2b23f34..f3776be69af3 100644 --- a/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts +++ b/packages/tools/fluid-runner/src/parseBundleAndExportFile.ts @@ -6,17 +6,19 @@ import * as fs from "node:fs"; import * as path from "node:path"; -import { createChildLogger, PerformanceEvent } from "@fluidframework/telemetry-utils/internal"; +import { PerformanceEvent } from "@fluidframework/telemetry-utils/internal"; import { isCodeLoaderBundle, isFluidFileConverter } from "./codeLoaderBundle.js"; import { type IExportFileResponse, - createFluidRunnerContainerAndExecute, + // eslint-disable-next-line import-x/no-deprecated + createContainerAndExecute, } from "./exportFile.js"; /* eslint-disable import-x/no-internal-modules */ import type { IFileLoggerTelemetryOptions } from "./logger/fileLogger.js"; import { - createFluidRunnerLogger, + // eslint-disable-next-line import-x/no-deprecated + createLogger, getTelemetryFileValidationError, } from "./logger/loggerUtils.js"; /* eslint-enable import-x/no-internal-modules */ @@ -44,11 +46,8 @@ export async function parseBundleAndExportFile( const eventName = clientArgsValidationError; return { success: false, eventName, errorMessage: telemetryArgError }; } - const { fileLogger, logger: baseLogger } = createFluidRunnerLogger( - telemetryFile, - telemetryOptions, - ); - const logger = createChildLogger({ logger: baseLogger }); + // eslint-disable-next-line import-x/no-deprecated + const { fileLogger, logger } = createLogger(telemetryFile, telemetryOptions); try { return await PerformanceEvent.timedExecAsync( @@ -86,7 +85,8 @@ export async function parseBundleAndExportFile( fs.writeFileSync( outputFile, - await createFluidRunnerContainerAndExecute( + // eslint-disable-next-line import-x/no-deprecated + await createContainerAndExecute( getSnapshotFileContent(inputFile), fluidExport, logger,