@fluidframework/fluid-runner 2.0.0-dev.1.3.0.96595

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 (134) hide show
  1. package/.eslintrc.js +18 -0
  2. package/.mocharc.js +12 -0
  3. package/LICENSE +21 -0
  4. package/README.md +68 -0
  5. package/bin/fluid-runner +2 -0
  6. package/dist/codeLoaderBundle.d.ts +44 -0
  7. package/dist/codeLoaderBundle.d.ts.map +1 -0
  8. package/dist/codeLoaderBundle.js +23 -0
  9. package/dist/codeLoaderBundle.js.map +1 -0
  10. package/dist/exportFile.d.ts +28 -0
  11. package/dist/exportFile.d.ts.map +1 -0
  12. package/dist/exportFile.js +87 -0
  13. package/dist/exportFile.js.map +1 -0
  14. package/dist/fakeUrlResolver.d.ts +15 -0
  15. package/dist/fakeUrlResolver.d.ts.map +1 -0
  16. package/dist/fakeUrlResolver.js +43 -0
  17. package/dist/fakeUrlResolver.js.map +1 -0
  18. package/dist/fluidRunner.d.ts +10 -0
  19. package/dist/fluidRunner.d.ts.map +1 -0
  20. package/dist/fluidRunner.js +106 -0
  21. package/dist/fluidRunner.js.map +1 -0
  22. package/dist/getArgsValidationError.d.ts +6 -0
  23. package/dist/getArgsValidationError.d.ts.map +1 -0
  24. package/dist/getArgsValidationError.js +46 -0
  25. package/dist/getArgsValidationError.js.map +1 -0
  26. package/dist/index.d.ts +12 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +30 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/logger/baseFileLogger.d.ts +28 -0
  31. package/dist/logger/baseFileLogger.d.ts.map +1 -0
  32. package/dist/logger/baseFileLogger.js +72 -0
  33. package/dist/logger/baseFileLogger.js.map +1 -0
  34. package/dist/logger/csvFileLogger.d.ts +18 -0
  35. package/dist/logger/csvFileLogger.d.ts.map +1 -0
  36. package/dist/logger/csvFileLogger.js +60 -0
  37. package/dist/logger/csvFileLogger.js.map +1 -0
  38. package/dist/logger/fileLogger.d.ts +38 -0
  39. package/dist/logger/fileLogger.d.ts.map +1 -0
  40. package/dist/logger/fileLogger.js +17 -0
  41. package/dist/logger/fileLogger.js.map +1 -0
  42. package/dist/logger/jsonFileLogger.d.ts +14 -0
  43. package/dist/logger/jsonFileLogger.d.ts.map +1 -0
  44. package/dist/logger/jsonFileLogger.js +44 -0
  45. package/dist/logger/jsonFileLogger.js.map +1 -0
  46. package/dist/logger/loggerUtils.d.ts +36 -0
  47. package/dist/logger/loggerUtils.d.ts.map +1 -0
  48. package/dist/logger/loggerUtils.js +90 -0
  49. package/dist/logger/loggerUtils.js.map +1 -0
  50. package/dist/packageVersion.d.ts +9 -0
  51. package/dist/packageVersion.d.ts.map +1 -0
  52. package/dist/packageVersion.js +12 -0
  53. package/dist/packageVersion.js.map +1 -0
  54. package/dist/parseBundleAndExportFile.d.ts +12 -0
  55. package/dist/parseBundleAndExportFile.d.ts.map +1 -0
  56. package/dist/parseBundleAndExportFile.js +84 -0
  57. package/dist/parseBundleAndExportFile.js.map +1 -0
  58. package/dist/utils.d.ts +24 -0
  59. package/dist/utils.d.ts.map +1 -0
  60. package/dist/utils.js +63 -0
  61. package/dist/utils.js.map +1 -0
  62. package/lib/codeLoaderBundle.d.ts +44 -0
  63. package/lib/codeLoaderBundle.d.ts.map +1 -0
  64. package/lib/codeLoaderBundle.js +18 -0
  65. package/lib/codeLoaderBundle.js.map +1 -0
  66. package/lib/exportFile.d.ts +28 -0
  67. package/lib/exportFile.d.ts.map +1 -0
  68. package/lib/exportFile.js +63 -0
  69. package/lib/exportFile.js.map +1 -0
  70. package/lib/fakeUrlResolver.d.ts +15 -0
  71. package/lib/fakeUrlResolver.d.ts.map +1 -0
  72. package/lib/fakeUrlResolver.js +39 -0
  73. package/lib/fakeUrlResolver.js.map +1 -0
  74. package/lib/fluidRunner.d.ts +10 -0
  75. package/lib/fluidRunner.d.ts.map +1 -0
  76. package/lib/fluidRunner.js +83 -0
  77. package/lib/fluidRunner.js.map +1 -0
  78. package/lib/getArgsValidationError.d.ts +6 -0
  79. package/lib/getArgsValidationError.d.ts.map +1 -0
  80. package/lib/getArgsValidationError.js +23 -0
  81. package/lib/getArgsValidationError.js.map +1 -0
  82. package/lib/index.d.ts +12 -0
  83. package/lib/index.d.ts.map +1 -0
  84. package/lib/index.js +12 -0
  85. package/lib/index.js.map +1 -0
  86. package/lib/logger/baseFileLogger.d.ts +28 -0
  87. package/lib/logger/baseFileLogger.d.ts.map +1 -0
  88. package/lib/logger/baseFileLogger.js +49 -0
  89. package/lib/logger/baseFileLogger.js.map +1 -0
  90. package/lib/logger/csvFileLogger.d.ts +18 -0
  91. package/lib/logger/csvFileLogger.d.ts.map +1 -0
  92. package/lib/logger/csvFileLogger.js +37 -0
  93. package/lib/logger/csvFileLogger.js.map +1 -0
  94. package/lib/logger/fileLogger.d.ts +38 -0
  95. package/lib/logger/fileLogger.d.ts.map +1 -0
  96. package/lib/logger/fileLogger.js +14 -0
  97. package/lib/logger/fileLogger.js.map +1 -0
  98. package/lib/logger/jsonFileLogger.d.ts +14 -0
  99. package/lib/logger/jsonFileLogger.d.ts.map +1 -0
  100. package/lib/logger/jsonFileLogger.js +21 -0
  101. package/lib/logger/jsonFileLogger.js.map +1 -0
  102. package/lib/logger/loggerUtils.d.ts +36 -0
  103. package/lib/logger/loggerUtils.d.ts.map +1 -0
  104. package/lib/logger/loggerUtils.js +65 -0
  105. package/lib/logger/loggerUtils.js.map +1 -0
  106. package/lib/packageVersion.d.ts +9 -0
  107. package/lib/packageVersion.d.ts.map +1 -0
  108. package/lib/packageVersion.js +9 -0
  109. package/lib/packageVersion.js.map +1 -0
  110. package/lib/parseBundleAndExportFile.d.ts +12 -0
  111. package/lib/parseBundleAndExportFile.d.ts.map +1 -0
  112. package/lib/parseBundleAndExportFile.js +61 -0
  113. package/lib/parseBundleAndExportFile.js.map +1 -0
  114. package/lib/utils.d.ts +24 -0
  115. package/lib/utils.d.ts.map +1 -0
  116. package/lib/utils.js +38 -0
  117. package/lib/utils.js.map +1 -0
  118. package/package.json +87 -0
  119. package/src/codeLoaderBundle.ts +57 -0
  120. package/src/exportFile.ts +104 -0
  121. package/src/fakeUrlResolver.ts +50 -0
  122. package/src/fluidRunner.ts +106 -0
  123. package/src/getArgsValidationError.ts +27 -0
  124. package/src/index.ts +14 -0
  125. package/src/logger/baseFileLogger.ts +58 -0
  126. package/src/logger/csvFileLogger.ts +40 -0
  127. package/src/logger/fileLogger.ts +45 -0
  128. package/src/logger/jsonFileLogger.ts +27 -0
  129. package/src/logger/loggerUtils.ts +81 -0
  130. package/src/packageVersion.ts +9 -0
  131. package/src/parseBundleAndExportFile.ts +80 -0
  132. package/src/utils.ts +45 -0
  133. package/tsconfig.esnext.json +7 -0
  134. package/tsconfig.json +14 -0
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@fluidframework/fluid-runner",
3
+ "version": "2.0.0-dev.1.3.0.96595",
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/fluid-runner"
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-dev.1.3.0.96595",
61
+ "@fluidframework/common-definitions": "^0.20.1",
62
+ "@fluidframework/container-definitions": "2.0.0-dev.1.3.0.96595",
63
+ "@fluidframework/container-loader": "2.0.0-dev.1.3.0.96595",
64
+ "@fluidframework/core-interfaces": "2.0.0-dev.1.3.0.96595",
65
+ "@fluidframework/driver-definitions": "2.0.0-dev.1.3.0.96595",
66
+ "@fluidframework/odsp-driver": "2.0.0-dev.1.3.0.96595",
67
+ "@fluidframework/odsp-driver-definitions": "2.0.0-dev.1.3.0.96595",
68
+ "@fluidframework/telemetry-utils": "2.0.0-dev.1.3.0.96595",
69
+ "json2csv": "^5.0.7",
70
+ "yargs": "13.2.2"
71
+ },
72
+ "devDependencies": {
73
+ "@fluidframework/build-common": "^1.0.0",
74
+ "@fluidframework/eslint-config-fluid": "^1.0.0",
75
+ "@fluidframework/mocha-test-setup": "2.0.0-dev.1.3.0.96595",
76
+ "@rushstack/eslint-config": "^2.5.1",
77
+ "@types/mocha": "^9.1.1",
78
+ "@types/node": "^14.18.0",
79
+ "concurrently": "^6.2.0",
80
+ "cross-env": "^7.0.2",
81
+ "eslint": "~8.6.0",
82
+ "mocha": "^10.0.0",
83
+ "nyc": "^15.0.0",
84
+ "rimraf": "^2.6.2",
85
+ "typescript": "~4.5.5"
86
+ }
87
+ }
@@ -0,0 +1,57 @@
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/sampleCodeLoaders/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
+ * Get code loader details to provide at Loader creation
27
+ * @param logger - created logger object to pass to code loader
28
+ */
29
+ getCodeLoader(logger: ITelemetryBaseLogger): Promise<ICodeDetailsLoader>;
30
+
31
+ /**
32
+ * Scope object to provide at Loader creation
33
+ */
34
+ scope?: FluidObject;
35
+
36
+ /**
37
+ * Executes code on container and returns the result
38
+ * @param container - container created by this application
39
+ * @param options - additional options
40
+ */
41
+ execute(container: IContainer, options?: string): Promise<string>;
42
+ }
43
+
44
+ /**
45
+ * Type cast to ensure necessary methods are present in the provided bundle
46
+ * @param bundle - bundle provided to this application
47
+ */
48
+ export function isCodeLoaderBundle(bundle: any): bundle is ICodeLoaderBundle {
49
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
50
+ return bundle?.fluidExport && typeof bundle.fluidExport === "object";
51
+ }
52
+
53
+ export function isFluidFileConverter(obj: any): obj is IFluidFileConverter {
54
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
55
+ return obj?.getCodeLoader && typeof obj.getCodeLoader === "function"
56
+ && obj.execute && typeof obj.execute === "function";
57
+ }
@@ -0,0 +1,104 @@
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 { ITelemetryLogger } from "@fluidframework/common-definitions";
8
+ import { LoaderHeader } from "@fluidframework/container-definitions";
9
+ import { Loader } from "@fluidframework/container-loader";
10
+ import { createLocalOdspDocumentServiceFactory } from "@fluidframework/odsp-driver";
11
+ import { PerformanceEvent } from "@fluidframework/telemetry-utils";
12
+ import { getArgsValidationError } from "./getArgsValidationError";
13
+ import { IFluidFileConverter } from "./codeLoaderBundle";
14
+ import { FakeUrlResolver } from "./fakeUrlResolver";
15
+ import { getSnapshotFileContent } from "./utils";
16
+ /* eslint-disable import/no-internal-modules */
17
+ import { ITelemetryOptions } from "./logger/fileLogger";
18
+ import { createLogger, getTelemetryFileValidationError } from "./logger/loggerUtils";
19
+ /* eslint-enable import/no-internal-modules */
20
+
21
+ export type IExportFileResponse = IExportFileResponseSuccess | IExportFileResponseFailure;
22
+
23
+ interface IExportFileResponseSuccess {
24
+ success: true;
25
+ }
26
+
27
+ interface IExportFileResponseFailure {
28
+ success: false;
29
+ eventName: string;
30
+ errorMessage: string;
31
+ error?: any;
32
+ }
33
+
34
+ const clientArgsValidationError = "Client_ArgsValidationError";
35
+
36
+ /**
37
+ * Execute code on Container based on ODSP snapshot and write result to file
38
+ */
39
+ export async function exportFile(
40
+ fluidFileConverter: IFluidFileConverter,
41
+ inputFile: string,
42
+ outputFile: string,
43
+ telemetryFile: string,
44
+ options?: string,
45
+ telemetryOptions?: ITelemetryOptions,
46
+ ): Promise<IExportFileResponse> {
47
+ const telemetryArgError = getTelemetryFileValidationError(telemetryFile);
48
+ if (telemetryArgError) {
49
+ const eventName = clientArgsValidationError;
50
+ return { success: false, eventName, errorMessage: telemetryArgError };
51
+ }
52
+ const { fileLogger, logger } = createLogger(telemetryFile, telemetryOptions);
53
+
54
+ try {
55
+ return await PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () => {
56
+ const argsValidationError = getArgsValidationError(inputFile, outputFile);
57
+ if (argsValidationError) {
58
+ const eventName = clientArgsValidationError;
59
+ logger.sendErrorEvent({ eventName, message: argsValidationError });
60
+ return { success: false, eventName, errorMessage: argsValidationError };
61
+ }
62
+
63
+ fs.writeFileSync(outputFile, await createContainerAndExecute(
64
+ getSnapshotFileContent(inputFile),
65
+ fluidFileConverter,
66
+ logger,
67
+ options,
68
+ ));
69
+
70
+ return { success: true };
71
+ });
72
+ } catch (error) {
73
+ const eventName = "Client_UnexpectedError";
74
+ logger.sendErrorEvent({ eventName }, error);
75
+ return { success: false, eventName, errorMessage: "Unexpected error", error };
76
+ } finally {
77
+ await fileLogger.close();
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Create the container based on an ODSP snapshot and execute code on it
83
+ * @returns result of execution
84
+ */
85
+ export async function createContainerAndExecute(
86
+ localOdspSnapshot: string | Uint8Array,
87
+ fluidFileConverter: IFluidFileConverter,
88
+ logger: ITelemetryLogger,
89
+ options?: string,
90
+ ): Promise<string> {
91
+ const loader = new Loader({
92
+ urlResolver: new FakeUrlResolver(),
93
+ documentServiceFactory: createLocalOdspDocumentServiceFactory(localOdspSnapshot),
94
+ codeLoader: await fluidFileConverter.getCodeLoader(logger),
95
+ scope: fluidFileConverter.scope,
96
+ logger,
97
+ });
98
+
99
+ const container = await loader.resolve({ url: "/fakeUrl/", headers: {
100
+ [LoaderHeader.loadMode]: { opsBeforeReturn: "cached" } } });
101
+
102
+ return PerformanceEvent.timedExecAsync(logger, { eventName: "ExportFile" }, async () =>
103
+ fluidFileConverter.execute(container, options));
104
+ }
@@ -0,0 +1,50 @@
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
+ * @internal
16
+ */
17
+ export class FakeUrlResolver implements IUrlResolver {
18
+ public async resolve(_request: IRequest): Promise<IResolvedUrl | undefined> {
19
+ const fakeOdspResolvedUrl: IOdspResolvedUrl = {
20
+ type: "fluid",
21
+ odspResolvedUrl: true,
22
+ id: fakeId,
23
+ siteUrl: fakeUrl,
24
+ driveId: fakeId,
25
+ itemId: fakeId,
26
+ url: fakeUrl,
27
+ hashedDocumentId: fakeId,
28
+ endpoints: {
29
+ snapshotStorageUrl: fakeUrl,
30
+ attachmentPOSTStorageUrl: fakeUrl,
31
+ attachmentGETStorageUrl: fakeUrl,
32
+ deltaStorageUrl: fakeUrl,
33
+ },
34
+ tokens: {},
35
+ fileName: fakeId,
36
+ summarizer: false,
37
+ fileVersion: fakeId,
38
+ };
39
+
40
+ return fakeOdspResolvedUrl;
41
+ }
42
+
43
+ public async getAbsoluteUrl(
44
+ _resolvedUrl: IResolvedUrl,
45
+ _relativeUrl: string,
46
+ _packageInfoSource?: IContainerPackageInfo,
47
+ ): Promise<string> {
48
+ return "";
49
+ }
50
+ }
@@ -0,0 +1,106 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /* eslint-disable max-len */
7
+ import * as yargs from "yargs";
8
+ import { exportFile } from "./exportFile";
9
+ import { IFluidFileConverter } from "./codeLoaderBundle";
10
+ import { parseBundleAndExportFile } from "./parseBundleAndExportFile";
11
+ // eslint-disable-next-line import/no-internal-modules
12
+ import { validateAndParseTelemetryOptions } from "./logger/loggerUtils";
13
+ import { validateCommandLineArgs } from "./utils";
14
+
15
+ /**
16
+ * @param fluidFileConverter - needs to be provided if "codeLoaderBundle" is not and vice versa
17
+ */
18
+ export function fluidRunner(fluidFileConverter?: IFluidFileConverter) {
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
20
+ yargs
21
+ .strict()
22
+ .version(false)
23
+ .command(
24
+ "exportFile",
25
+ "Generate an output for a local ODSP snapshot",
26
+ // eslint-disable-next-line @typescript-eslint/no-shadow
27
+ (yargs) =>
28
+ yargs
29
+ .option("codeLoader", {
30
+ describe: "Path to code loader bundle. Required if this application is being called without modification.\nSee \"README.md\" for more details.",
31
+ type: "string",
32
+ demandOption: false,
33
+ })
34
+ .option("inputFile", {
35
+ describe: "Path to local ODSP snapshot",
36
+ type: "string",
37
+ demandOption: true,
38
+ })
39
+ .option("outputFile", {
40
+ describe: "Path of output file (cannot already exist).\nExecution result will be written here",
41
+ type: "string",
42
+ demandOption: true,
43
+ })
44
+ .option("telemetryFile", {
45
+ describe: "Path of telemetry file for config and session data (cannot already exist)",
46
+ type: "string",
47
+ demandOption: true,
48
+ })
49
+ .option("options", {
50
+ describe: "Additional options passed to container on execution",
51
+ type: "string",
52
+ demandOption: false,
53
+ })
54
+ .option("telemetryFormat", {
55
+ describe: "Output format for telemetry. Current options are: [\"JSON\", \"CSV\"]",
56
+ type: "string",
57
+ demandOption: false,
58
+ default: "JSON",
59
+ })
60
+ .option("telemetryProp", {
61
+ describe: "Property to add to every telemetry entry. Formatted like \"--telemetryProp prop1 value1 --telemetryProp prop2 \\\"value 2\\\"\".",
62
+ type: "array",
63
+ demandOption: false,
64
+ }),
65
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
66
+ async (argv) => {
67
+ const argsError = validateCommandLineArgs(argv.codeLoader, fluidFileConverter);
68
+ if (argsError) {
69
+ console.error(argsError);
70
+ process.exit(1);
71
+ }
72
+ const telemetryOptionsResult = validateAndParseTelemetryOptions(argv.telemetryFormat, argv.telemetryProp);
73
+ if (!telemetryOptionsResult.success) {
74
+ console.error(telemetryOptionsResult.error);
75
+ process.exit(1);
76
+ }
77
+
78
+ const result = await (argv.codeLoader
79
+ ? parseBundleAndExportFile(
80
+ argv.codeLoader,
81
+ argv.inputFile,
82
+ argv.outputFile,
83
+ argv.telemetryFile,
84
+ argv.options,
85
+ telemetryOptionsResult.telemetryOptions,
86
+ ) : exportFile(
87
+ fluidFileConverter!,
88
+ argv.inputFile,
89
+ argv.outputFile,
90
+ argv.telemetryFile,
91
+ argv.options,
92
+ telemetryOptionsResult.telemetryOptions,
93
+ ));
94
+
95
+ if (!result.success) {
96
+ console.error(`${result.eventName}: ${result.errorMessage}`);
97
+ process.exit(1);
98
+ }
99
+ },
100
+ )
101
+ .help()
102
+ .demandCommand().argv;
103
+ }
104
+
105
+ fluidRunner();
106
+ /* eslint-enable max-len */
@@ -0,0 +1,27 @@
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
+ outputFile: string,
11
+ ): string | undefined {
12
+ // Validate input file
13
+ if (!inputFile) {
14
+ return "Input file name argument is missing.";
15
+ } else if (!fs.existsSync(inputFile)) {
16
+ return "Input file does not exist.";
17
+ }
18
+
19
+ // Validate output file
20
+ if (!outputFile) {
21
+ return "Output file argument is missing.";
22
+ } else if (fs.existsSync(outputFile)) {
23
+ return `Output file already exists [${outputFile}].`;
24
+ }
25
+
26
+ return undefined;
27
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /* eslint-disable import/no-internal-modules */
7
+ export { ICodeLoaderBundle, IFluidFileConverter } from "./codeLoaderBundle";
8
+ export * from "./exportFile";
9
+ export { fluidRunner } from "./fluidRunner";
10
+ export { OutputFormat } from "./logger/fileLogger";
11
+ export { createLogger, getTelemetryFileValidationError } from "./logger/loggerUtils";
12
+ export * from "./parseBundleAndExportFile";
13
+ export { getSnapshotFileContent } from "./utils";
14
+ /* eslint-enable import/no-internal-modules */
@@ -0,0 +1,58 @@
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 } from "@fluidframework/common-definitions";
8
+ import { IFileLogger } from "./fileLogger";
9
+
10
+ /**
11
+ * @internal
12
+ */
13
+ export abstract class BaseFileLogger implements IFileLogger {
14
+ public supportsTags?: true | undefined;
15
+
16
+ /** Hold events in memory until flushed */
17
+ protected events: any[] = [];
18
+ protected hasWrittenToFile = false;
19
+
20
+ /**
21
+ * @param filePath - file path to write logs to
22
+ * @param eventsPerFlush - number of events per flush
23
+ * @param defaultProps - default properties to add to every telemetry event
24
+ */
25
+ public constructor(
26
+ protected readonly filePath: string,
27
+ protected readonly eventsPerFlush: number = 50,
28
+ protected readonly defaultProps?: Record<string, string | number>,
29
+ ) { }
30
+
31
+ public send(event: ITelemetryBaseEvent): void {
32
+ // eslint-disable-next-line no-param-reassign
33
+ event = { ...event, ...this.defaultProps };
34
+ this.events.push(event);
35
+
36
+ if (this.events.length >= this.eventsPerFlush || event.category === "error") {
37
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
38
+ this.flush();
39
+ }
40
+ }
41
+
42
+ protected async flush(): Promise<void> {
43
+ if (this.events.length > 0) {
44
+ const contentToWrite = this.events.map((it) => JSON.stringify(it)).join(",");
45
+ if (this.hasWrittenToFile) {
46
+ fs.appendFileSync(this.filePath, `,${contentToWrite}`);
47
+ } else {
48
+ fs.appendFileSync(this.filePath, contentToWrite);
49
+ }
50
+ this.events = [];
51
+ this.hasWrittenToFile = true;
52
+ }
53
+ }
54
+
55
+ public async close(): Promise<void> {
56
+ await this.flush();
57
+ }
58
+ }
@@ -0,0 +1,40 @@
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 { parse } from "json2csv";
8
+ import { ITelemetryBaseEvent } from "@fluidframework/common-definitions";
9
+ import { BaseFileLogger } from "./baseFileLogger";
10
+
11
+ /**
12
+ * FileLogger that writes events into a defined CSV file
13
+ * @internal
14
+ */
15
+ export class CSVFileLogger extends BaseFileLogger {
16
+ /** Store the column names to write as the CSV header */
17
+ private readonly columns = new Set();
18
+
19
+ public async flush(): Promise<void> {
20
+ // Do nothing
21
+ }
22
+
23
+ public send(event: ITelemetryBaseEvent): void {
24
+ // eslint-disable-next-line guard-for-in, no-restricted-syntax
25
+ for (const prop in event) {
26
+ this.columns.add(prop);
27
+ }
28
+ super.send(event);
29
+ }
30
+
31
+ public async close(): Promise<void> {
32
+ await super.close();
33
+ // eslint-disable-next-line guard-for-in, no-restricted-syntax
34
+ for (const field in this.defaultProps) {
35
+ this.columns.add(field);
36
+ }
37
+
38
+ fs.writeFileSync(this.filePath, parse(this.events, Array.from(this.columns)));
39
+ }
40
+ }
@@ -0,0 +1,45 @@
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
+
8
+ /**
9
+ * Contract for logger that writes telemetry to a file
10
+ * @internal
11
+ */
12
+ export interface IFileLogger extends ITelemetryBaseLogger {
13
+ /**
14
+ * This method acts as a "dispose" and should be explicitly called at the end of execution
15
+ */
16
+ close(): Promise<void>;
17
+ }
18
+
19
+ /**
20
+ * Desired output format for the telemetry
21
+ */
22
+ export enum OutputFormat {
23
+ JSON,
24
+ CSV,
25
+ }
26
+
27
+ /* eslint-disable tsdoc/syntax */
28
+ /**
29
+ * Options to provide upon creation of IFileLogger
30
+ * @internal
31
+ */
32
+ export interface ITelemetryOptions {
33
+ /** Desired output format used to create a specific IFileLogger implementation */
34
+ outputFormat?: OutputFormat;
35
+
36
+ /**
37
+ * Properties that should be added to every telemetry event
38
+ * Example: { "prop1": "value1", "prop2": 10.0 }
39
+ */
40
+ defaultProps?: Record<string, string | number>;
41
+
42
+ /** Number of telemetry events per flush to telemetry file */
43
+ eventsPerFlush?: number;
44
+ }
45
+ /* eslint-enable tsdoc/syntax */
@@ -0,0 +1,27 @@
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 { BaseFileLogger } from "./baseFileLogger";
8
+
9
+ /**
10
+ * FileLogger that writes events into a defined CSV file
11
+ * @internal
12
+ */
13
+ export class JSONFileLogger extends BaseFileLogger {
14
+ constructor(
15
+ filePath: string,
16
+ eventsPerFlush: number = 50,
17
+ defaultProps?: Record<string, string | number>,
18
+ ) {
19
+ super(filePath, eventsPerFlush, defaultProps);
20
+ fs.appendFileSync(this.filePath, "[");
21
+ }
22
+
23
+ public async close(): Promise<void> {
24
+ await super.close();
25
+ fs.appendFileSync(this.filePath, "]");
26
+ }
27
+ }