@fluidframework/fluid-runner 2.0.0-internal.1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/.eslintrc.js +18 -0
  2. package/LICENSE +21 -0
  3. package/README.md +16 -0
  4. package/bin/fluidRunner +2 -0
  5. package/dist/codeLoaderBundle.d.ts +44 -0
  6. package/dist/codeLoaderBundle.d.ts.map +1 -0
  7. package/dist/codeLoaderBundle.js +17 -0
  8. package/dist/codeLoaderBundle.js.map +1 -0
  9. package/dist/exportFile.d.ts +20 -0
  10. package/dist/exportFile.d.ts.map +1 -0
  11. package/dist/exportFile.js +84 -0
  12. package/dist/exportFile.js.map +1 -0
  13. package/dist/fakeUrlResolver.d.ts +14 -0
  14. package/dist/fakeUrlResolver.d.ts.map +1 -0
  15. package/dist/fakeUrlResolver.js +42 -0
  16. package/dist/fakeUrlResolver.js.map +1 -0
  17. package/dist/fluidRunner.d.ts +6 -0
  18. package/dist/fluidRunner.d.ts.map +1 -0
  19. package/dist/fluidRunner.js +88 -0
  20. package/dist/fluidRunner.js.map +1 -0
  21. package/dist/getArgsValidationError.d.ts +6 -0
  22. package/dist/getArgsValidationError.d.ts.map +1 -0
  23. package/dist/getArgsValidationError.js +54 -0
  24. package/dist/getArgsValidationError.js.map +1 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +19 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/logger/FileLogger.d.ts +23 -0
  30. package/dist/logger/FileLogger.d.ts.map +1 -0
  31. package/dist/logger/FileLogger.js +58 -0
  32. package/dist/logger/FileLogger.js.map +1 -0
  33. package/dist/packageVersion.d.ts +9 -0
  34. package/dist/packageVersion.d.ts.map +1 -0
  35. package/dist/packageVersion.js +12 -0
  36. package/dist/packageVersion.js.map +1 -0
  37. package/lib/codeLoaderBundle.d.ts +44 -0
  38. package/lib/codeLoaderBundle.d.ts.map +1 -0
  39. package/lib/codeLoaderBundle.js +13 -0
  40. package/lib/codeLoaderBundle.js.map +1 -0
  41. package/lib/exportFile.d.ts +20 -0
  42. package/lib/exportFile.d.ts.map +1 -0
  43. package/lib/exportFile.js +57 -0
  44. package/lib/exportFile.js.map +1 -0
  45. package/lib/fakeUrlResolver.d.ts +14 -0
  46. package/lib/fakeUrlResolver.d.ts.map +1 -0
  47. package/lib/fakeUrlResolver.js +38 -0
  48. package/lib/fakeUrlResolver.js.map +1 -0
  49. package/lib/fluidRunner.d.ts +6 -0
  50. package/lib/fluidRunner.d.ts.map +1 -0
  51. package/lib/fluidRunner.js +67 -0
  52. package/lib/fluidRunner.js.map +1 -0
  53. package/lib/getArgsValidationError.d.ts +6 -0
  54. package/lib/getArgsValidationError.d.ts.map +1 -0
  55. package/lib/getArgsValidationError.js +31 -0
  56. package/lib/getArgsValidationError.js.map +1 -0
  57. package/lib/index.d.ts +7 -0
  58. package/lib/index.d.ts.map +1 -0
  59. package/lib/index.js +7 -0
  60. package/lib/index.js.map +1 -0
  61. package/lib/logger/FileLogger.d.ts +23 -0
  62. package/lib/logger/FileLogger.d.ts.map +1 -0
  63. package/lib/logger/FileLogger.js +35 -0
  64. package/lib/logger/FileLogger.js.map +1 -0
  65. package/lib/packageVersion.d.ts +9 -0
  66. package/lib/packageVersion.d.ts.map +1 -0
  67. package/lib/packageVersion.js +9 -0
  68. package/lib/packageVersion.js.map +1 -0
  69. package/package.json +86 -0
  70. package/src/codeLoaderBundle.ts +52 -0
  71. package/src/exportFile.ts +94 -0
  72. package/src/fakeUrlResolver.ts +49 -0
  73. package/src/fluidRunner.ts +83 -0
  74. package/src/getArgsValidationError.ts +36 -0
  75. package/src/index.ts +7 -0
  76. package/src/logger/FileLogger.ts +44 -0
  77. package/src/packageVersion.ts +9 -0
  78. package/tsconfig.esnext.json +7 -0
  79. package/tsconfig.json +14 -0
package/package.json ADDED
@@ -0,0 +1,86 @@
1
+ {
2
+ "name": "@fluidframework/fluid-runner",
3
+ "version": "2.0.0-internal.1.0.0",
4
+ "description": "Utility for running various functionality inside a Fluid Framework environment",
5
+ "homepage": "https://fluidframework.com",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/microsoft/FluidFramework.git",
9
+ "directory": "packages/tools/fluid-runner"
10
+ },
11
+ "license": "MIT",
12
+ "author": "Microsoft and contributors",
13
+ "main": "dist/index.js",
14
+ "types": "dist/index.d.ts",
15
+ "bin": {
16
+ "fluid-runner": "bin/fluidRunner"
17
+ },
18
+ "scripts": {
19
+ "build": "concurrently npm:build:compile npm:lint",
20
+ "build:commonjs": "npm run tsc && npm run build:test",
21
+ "build:compile": "concurrently npm:build:commonjs npm:build:esnext",
22
+ "build:esnext": "tsc --project ./tsconfig.esnext.json",
23
+ "build:full": "npm run build",
24
+ "build:full:compile": "npm run build:compile",
25
+ "build:test": "tsc --project ./src/test/tsconfig.json",
26
+ "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
27
+ "eslint": "eslint --format stylish src",
28
+ "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
29
+ "lint": "npm run eslint",
30
+ "lint:fix": "npm run eslint:fix",
31
+ "test": "npm run test:mocha",
32
+ "test:coverage": "nyc npm test -- --reporter xunit --reporter-option output=nyc/junit-report.xml",
33
+ "test:mocha": "mocha --ignore 'dist/test/types/*' --recursive dist/test -r node_modules/@fluidframework/mocha-test-setup --unhandled-rejections=strict",
34
+ "test:mocha:verbose": "cross-env FLUID_TEST_VERBOSE=1 npm run test:mocha",
35
+ "tsc": "tsc",
36
+ "tsfmt": "tsfmt --verify",
37
+ "tsfmt:fix": "tsfmt --replace"
38
+ },
39
+ "nyc": {
40
+ "all": true,
41
+ "cache-dir": "nyc/.cache",
42
+ "exclude": [
43
+ "src/test/**/*.ts",
44
+ "dist/test/**/*.js"
45
+ ],
46
+ "exclude-after-remap": false,
47
+ "include": [
48
+ "src/**/*.ts",
49
+ "dist/**/*.js"
50
+ ],
51
+ "report-dir": "nyc/report",
52
+ "reporter": [
53
+ "cobertura",
54
+ "html",
55
+ "text"
56
+ ],
57
+ "temp-directory": "nyc/.nyc_output"
58
+ },
59
+ "dependencies": {
60
+ "@fluidframework/aqueduct": "^2.0.0-internal.1.0.0",
61
+ "@fluidframework/common-definitions": "^0.20.1",
62
+ "@fluidframework/container-definitions": "^2.0.0-internal.1.0.0",
63
+ "@fluidframework/container-loader": "^2.0.0-internal.1.0.0",
64
+ "@fluidframework/core-interfaces": "^2.0.0-internal.1.0.0",
65
+ "@fluidframework/driver-definitions": "^2.0.0-internal.1.0.0",
66
+ "@fluidframework/odsp-driver": "^2.0.0-internal.1.0.0",
67
+ "@fluidframework/odsp-driver-definitions": "^2.0.0-internal.1.0.0",
68
+ "@fluidframework/telemetry-utils": "^2.0.0-internal.1.0.0",
69
+ "yargs": "13.2.2"
70
+ },
71
+ "devDependencies": {
72
+ "@fluidframework/build-common": "^0.24.0",
73
+ "@fluidframework/eslint-config-fluid": "^0.28.2000",
74
+ "@fluidframework/mocha-test-setup": "^2.0.0-internal.1.0.0",
75
+ "@rushstack/eslint-config": "^2.5.1",
76
+ "@types/mocha": "^9.1.1",
77
+ "@types/node": "^14.18.0",
78
+ "concurrently": "^6.2.0",
79
+ "cross-env": "^7.0.2",
80
+ "eslint": "~8.6.0",
81
+ "mocha": "^10.0.0",
82
+ "nyc": "^15.0.0",
83
+ "rimraf": "^2.6.2",
84
+ "typescript": "~4.5.5"
85
+ }
86
+ }
@@ -0,0 +1,52 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { ITelemetryBaseLogger } from "@fluidframework/common-definitions";
7
+ import { ICodeDetailsLoader, IContainer } from "@fluidframework/container-definitions";
8
+ import { FluidObject } from "@fluidframework/core-interfaces";
9
+
10
+ /**
11
+ * Contract that defines the necessary exports for the bundle provided at runtime
12
+ * For an example, see "src/test/sampleCodeLoader.ts"
13
+ */
14
+ export interface ICodeLoaderBundle {
15
+ /**
16
+ * Fluid export of all the required objects and functions
17
+ */
18
+ fluidExport: Promise<IFluidFileConverter>;
19
+ }
20
+
21
+ /**
22
+ * Instance that holds all the details for Fluid file conversion
23
+ */
24
+ export interface IFluidFileConverter {
25
+ /**
26
+ * Code loader details to provide at Loader creation
27
+ */
28
+ codeLoader: ICodeDetailsLoader;
29
+
30
+ /**
31
+ * Scope object to provide at Loader creation
32
+ */
33
+ scope?: FluidObject;
34
+
35
+ /**
36
+ * Execute code and return the results
37
+ * @param container - container created by this application
38
+ * @param scenario - scenario this execution is related to
39
+ * @param logger - passed through logger object
40
+ * @returns - object containing file names as property keys and file content as values
41
+ */
42
+ execute(container: IContainer, scenario: string, logger: ITelemetryBaseLogger): Promise<Record<string, string>>;
43
+ }
44
+
45
+ /**
46
+ * Type cast to ensure necessary methods are present in the provided bundle
47
+ * @param bundle - bundle provided to this application
48
+ */
49
+ export function isCodeLoaderBundle(bundle: any): bundle is ICodeLoaderBundle {
50
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
51
+ return bundle?.fluidExport && typeof bundle.fluidExport === "object";
52
+ }
@@ -0,0 +1,94 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import * as fs from "fs";
7
+ import path from "path";
8
+ import { ITelemetryLogger } from "@fluidframework/common-definitions";
9
+ import { LoaderHeader } from "@fluidframework/container-definitions";
10
+ import { Loader } from "@fluidframework/container-loader";
11
+ import { createLocalOdspDocumentServiceFactory } from "@fluidframework/odsp-driver";
12
+ import { PerformanceEvent } from "@fluidframework/telemetry-utils";
13
+ import { getArgsValidationError } from "./getArgsValidationError";
14
+ import { IFluidFileConverter, isCodeLoaderBundle } from "./codeLoaderBundle";
15
+ import { FakeUrlResolver } from "./fakeUrlResolver";
16
+
17
+ export type IExportFileResponse = IExportFileResponseSuccess | IExportFileResponseFailure;
18
+
19
+ interface IExportFileResponseSuccess {
20
+ success: true;
21
+ }
22
+
23
+ interface IExportFileResponseFailure {
24
+ success: false;
25
+ eventName: string;
26
+ errorMessage: string;
27
+ error?: any;
28
+ }
29
+
30
+ const clientArgsValidationError = "Client_ArgsValidationError";
31
+
32
+ export async function exportFile(
33
+ codeLoader: string,
34
+ inputFile: string,
35
+ outputFolder: string,
36
+ scenario: string,
37
+ logger: ITelemetryLogger,
38
+ ): Promise<IExportFileResponse> {
39
+ try {
40
+ return await PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () => {
41
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
42
+ const codeLoaderBundle = require(codeLoader);
43
+ if (!isCodeLoaderBundle(codeLoaderBundle)) {
44
+ const eventName = clientArgsValidationError;
45
+ const message = "Code loader bundle is not of type CodeLoaderBundle";
46
+ return { success: false, eventName, errorMessage: message };
47
+ }
48
+
49
+ const argsValidationError = getArgsValidationError(inputFile, outputFolder, scenario);
50
+ if (argsValidationError) {
51
+ const eventName = clientArgsValidationError;
52
+ return { success: false, eventName, errorMessage: argsValidationError };
53
+ }
54
+
55
+ // TODO: read file stream
56
+ const inputFileContent = fs.readFileSync(inputFile, { encoding: "utf-8" });
57
+
58
+ const results = await createContainerAndExecute(
59
+ inputFileContent,
60
+ await codeLoaderBundle.fluidExport,
61
+ scenario,
62
+ logger,
63
+ );
64
+ // eslint-disable-next-line guard-for-in, no-restricted-syntax
65
+ for (const key in results) {
66
+ fs.appendFileSync(path.join(outputFolder, key), results[key]);
67
+ }
68
+ return { success: true };
69
+ });
70
+ } catch (error) {
71
+ const eventName = "Client_UnexpectedError";
72
+ return { success: false, eventName, errorMessage: "Unexpected error", error };
73
+ }
74
+ }
75
+
76
+ export async function createContainerAndExecute(
77
+ localOdspSnapshot: string,
78
+ fluidFileConverter: IFluidFileConverter,
79
+ scenario: string,
80
+ logger: ITelemetryLogger,
81
+ ): Promise<Record<string, string>> {
82
+ const loader = new Loader({
83
+ urlResolver: new FakeUrlResolver(),
84
+ documentServiceFactory: createLocalOdspDocumentServiceFactory(localOdspSnapshot),
85
+ codeLoader: fluidFileConverter.codeLoader,
86
+ scope: fluidFileConverter.scope,
87
+ });
88
+
89
+ const container = await loader.resolve({ url: "/fakeUrl/", headers: {
90
+ [LoaderHeader.loadMode]: { opsBeforeReturn: "cached" } } });
91
+
92
+ return PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () =>
93
+ fluidFileConverter.execute(container, scenario, logger));
94
+ }
@@ -0,0 +1,49 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IRequest } from "@fluidframework/core-interfaces";
7
+ import { IContainerPackageInfo, IResolvedUrl, IUrlResolver } from "@fluidframework/driver-definitions";
8
+ import { IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions";
9
+
10
+ const fakeId = "FakeUrlResolver";
11
+ const fakeUrl = "/FakeUrlResolver/";
12
+
13
+ /**
14
+ * Fake URL resolver that returns hard coded values on every request
15
+ */
16
+ export class FakeUrlResolver implements IUrlResolver {
17
+ public async resolve(_request: IRequest): Promise<IResolvedUrl | undefined> {
18
+ const fakeOdspResolvedUrl: IOdspResolvedUrl = {
19
+ type: "fluid",
20
+ odspResolvedUrl: true,
21
+ id: fakeId,
22
+ siteUrl: fakeUrl,
23
+ driveId: fakeId,
24
+ itemId: fakeId,
25
+ url: fakeUrl,
26
+ hashedDocumentId: fakeId,
27
+ endpoints: {
28
+ snapshotStorageUrl: fakeUrl,
29
+ attachmentPOSTStorageUrl: fakeUrl,
30
+ attachmentGETStorageUrl: fakeUrl,
31
+ deltaStorageUrl: fakeUrl,
32
+ },
33
+ tokens: {},
34
+ fileName: fakeId,
35
+ summarizer: false,
36
+ fileVersion: fakeId,
37
+ };
38
+
39
+ return fakeOdspResolvedUrl;
40
+ }
41
+
42
+ public async getAbsoluteUrl(
43
+ _resolvedUrl: IResolvedUrl,
44
+ _relativeUrl: string,
45
+ _packageInfoSource?: IContainerPackageInfo,
46
+ ): Promise<string> {
47
+ return "";
48
+ }
49
+ }
@@ -0,0 +1,83 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import * as fs from "fs";
7
+ import * as yargs from "yargs";
8
+ import { ChildLogger } from "@fluidframework/telemetry-utils";
9
+ import { exportFile } from "./exportFile";
10
+ // eslint-disable-next-line import/no-internal-modules
11
+ import { FileLogger } from "./logger/FileLogger";
12
+
13
+ function fluidRunner() {
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
15
+ yargs
16
+ .strict()
17
+ .version(false)
18
+ .command(
19
+ "exportFile",
20
+ "Generate an output for a local snapshot",
21
+ // eslint-disable-next-line @typescript-eslint/no-shadow
22
+ (yargs) =>
23
+ yargs
24
+ .option("codeLoader", {
25
+ describe: "Name of the code loader bundle",
26
+ type: "string",
27
+ demandOption: true,
28
+ })
29
+ .option("inputFile", {
30
+ describe: "Name of the file containing local ODSP snapshot",
31
+ type: "string",
32
+ demandOption: true,
33
+ })
34
+ .option("outputFolder", {
35
+ describe: "Name of the output file",
36
+ type: "string",
37
+ demandOption: true,
38
+ })
39
+ .option("scenario", {
40
+ describe: "Name of scenario to invoke",
41
+ type: "string",
42
+ demandOption: true,
43
+ })
44
+ .option("telemetryFile", {
45
+ describe: "Config and session data for telemetry",
46
+ type: "string",
47
+ demandOption: true,
48
+ }),
49
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
50
+ async (argv) => {
51
+ if (fs.existsSync(argv.telemetryFile)) {
52
+ console.error(`Telemetry file already exists [${argv.telemetryFile}]`);
53
+ process.exit(1);
54
+ }
55
+
56
+ const fileLogger = new FileLogger(argv.telemetryFile);
57
+
58
+ const logger = ChildLogger.create(fileLogger, "LocalSnapshotRunnerApp",
59
+ { all: { Event_Time: () => Date.now() } });
60
+
61
+ const result = await exportFile(
62
+ argv.codeLoader,
63
+ argv.inputFile,
64
+ argv.outputFolder,
65
+ argv.scenario,
66
+ logger,
67
+ );
68
+
69
+ if (!result.success) {
70
+ console.error(`${result.eventName}: ${result.errorMessage}`);
71
+ logger.sendErrorEvent({ eventName: result.eventName, message: result.errorMessage }, result.error);
72
+ await fileLogger.flush();
73
+ process.exit(1);
74
+ } else {
75
+ await fileLogger.flush();
76
+ }
77
+ },
78
+ )
79
+ .help()
80
+ .demandCommand().argv;
81
+ }
82
+
83
+ fluidRunner();
@@ -0,0 +1,36 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import * as fs from "fs";
7
+
8
+ export function getArgsValidationError(
9
+ inputFile: string,
10
+ outputFolder: string,
11
+ scenario: string,
12
+ ): string | undefined {
13
+ // Validate input file
14
+ if (!inputFile) {
15
+ // TODO: Do not log file name. It can be customer content
16
+ return "Input file name is missing.";
17
+ } else if (!fs.existsSync(inputFile)) {
18
+ return "Input file does not exist.";
19
+ }
20
+
21
+ // Validate output file
22
+ if (!outputFolder) {
23
+ return "Output folder name is missing.";
24
+ } else if (!fs.existsSync(outputFolder)) {
25
+ return "Output folder does not exist.";
26
+ } else if (fs.existsSync(`${outputFolder}/index.html`)) {
27
+ return "Output file already exists.";
28
+ }
29
+
30
+ // Validate scenario name
31
+ if (!scenario) {
32
+ return "Scenario name is missing.";
33
+ }
34
+
35
+ return undefined;
36
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export * from "./codeLoaderBundle";
7
+ export * from "./exportFile";
@@ -0,0 +1,44 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import * as fs from "fs";
7
+ import { ITelemetryBaseEvent, ITelemetryBaseLogger } from "@fluidframework/common-definitions";
8
+
9
+ /**
10
+ * Logger that writes events into a defined file
11
+ */
12
+ export class FileLogger implements ITelemetryBaseLogger {
13
+ public supportsTags?: true | undefined;
14
+
15
+ /** Hold events in memory until flushed */
16
+ private events: string[] = [];
17
+
18
+ /**
19
+ * @param filePath - file path to write logs to
20
+ * @param eventsPerFlush - number of events per flush
21
+ */
22
+ public constructor(
23
+ private readonly filePath: string,
24
+ private readonly eventsPerFlush: number = 50,
25
+ ) { }
26
+
27
+ public async flush(): Promise<void> {
28
+ if (this.events.length > 0) {
29
+ fs.appendFileSync(this.filePath, this.events.join("\n"));
30
+ this.events = [];
31
+ }
32
+ }
33
+
34
+ public send(event: ITelemetryBaseEvent): void {
35
+ const logEvent = JSON.stringify(event);
36
+
37
+ this.events.push(logEvent);
38
+
39
+ if (this.events.length >= this.eventsPerFlush || event.category === "error") {
40
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
41
+ this.flush();
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,9 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ *
5
+ * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
+ */
7
+
8
+ export const pkgName = "@fluidframework/fluid-runner";
9
+ export const pkgVersion = "1.2.0";
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "module": "esnext"
6
+ },
7
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "extends": "@fluidframework/build-common/ts-common-config.json",
3
+ "exclude": [
4
+ "src/test/**/*"
5
+ ],
6
+ "compilerOptions": {
7
+ "rootDir": "./src",
8
+ "outDir": "./dist",
9
+ "composite": true
10
+ },
11
+ "include": [
12
+ "src/**/*"
13
+ ]
14
+ }