UNPKG

dt-app

Version:

The Dynatrace App Toolkit is a tool you can use from your command line to create, develop, and deploy apps on your Dynatrace environment.

1,636 lines (1,575 loc) 67.8 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // src/migrations/0.119.0/index.ts var import_fs14 = require("fs"); // src/utils/config/get-dt-app-file-config.ts var import_path9 = require("path"); // src/utils/config/extract-dt-app-config-from-ts.ts var import_path7 = require("path"); var import_url = require("url"); // src/core/modules/logger/logger.ts var import_chalk = require("chalk"); var import_lodash = require("lodash"); // src/core/modules/logger/logfile-stream.ts var import_fs = require("fs"); var import_promises = require("fs/promises"); var import_path = require("path"); var fileNamePattern = new RegExp(/[0-9]{4}-[0-9]{2}-[0-9]{2}_log.txt/); var pad = (num) => (num > 9 ? "" : "0") + num; var fileNameGenerator = (time) => { return `${time.getFullYear()}-${pad(time.getMonth() + 1)}-${pad( time.getDate() )}_log.txt`; }; var extractDateFromFileName = (filename) => { try { const parts = filename.split("-"); return new Date( Number(parts[0]), Number(parts[1]), Number(parts[2].split("_")[0]) ); } catch { return void 0; } }; var LogFileStreamStub = class { /** Stub for setup function */ async setup(_root) { return Promise.resolve(); } /** Stub that does not write to file */ write(_text) { } // eslint-disable-line @typescript-eslint/no-empty-function /** Stub that returns empty string */ getLogFile() { return ""; } }; var LogFileStreamImpl = class { /** Location where log files should be stored. */ logFolder; /** Log file path used by the current process. */ logFile; /** Actual stream that is used to write the logs into the file. */ stream; /** Initialize file stream for logging. */ async setup(root) { this.logFolder = (0, import_path.join)(root, ".dt-app/logs"); if (!(0, import_fs.existsSync)(this.logFolder)) { (0, import_fs.mkdirSync)(this.logFolder, { recursive: true }); } await this.rotate(); } /** Rotate file. */ async rotate() { this.logFile = (0, import_path.join)(this.logFolder, fileNameGenerator(/* @__PURE__ */ new Date())); if (!(0, import_fs.existsSync)(this.logFile)) { const files = (0, import_fs.readdirSync)(this.logFolder).map((f) => f.trim()).filter((f) => fileNamePattern.test(f)); if (files.length >= 10) { const oldestFile = files.map((f) => { return { path: f, date: extractDateFromFileName(f) }; }).filter((f) => f.date).sort((a, b) => a.date.getTime() - b.date.getTime())[0].path; (0, import_promises.unlink)((0, import_path.join)(this.logFolder, oldestFile)); } (0, import_fs.closeSync)((0, import_fs.openSync)(this.logFile, "w")); } await new Promise((resolve4) => { this.stream = (0, import_fs.createWriteStream)(this.logFile, { encoding: "utf-8", flags: "a" // Append to file }); this.stream.on("open", () => resolve4()); }); } /** Write log to file. */ write(text) { if (this.stream) { this.stream.write(text); } } /** Returns the path to the current log file. */ getLogFile() { return this.logFile; } }; async function initLogFileStream(root) { logFileStream = new LogFileStreamImpl(); return logFileStream.setup(root); } var logFileStream = new LogFileStreamStub(); // src/core/modules/logger/logger.ts var import_os = require("os"); // src/utils/terminal/spinner-service.ts var import_ora = __toESM(require("ora")); // src/utils/terminal/terminal-queue.ts var queue = []; var isProcessing = false; function enqueue(func) { return new Promise((resolve4, reject) => { queue.push(async () => { try { const result = await func(); resolve4(result); } catch (error) { reject(error); } }); processQueue(); }); } async function processQueue() { if (isProcessing) { return; } isProcessing = true; while (queue.length > 0) { const queueItem = queue.shift(); await queueItem(); } isProcessing = false; } async function terminalAction(promptFunction) { return enqueue(promptFunction); } // src/utils/terminal/spinner-service.ts var spinnerEnabled = process.env.DT_APP_DEACTIVATE_SPINNER !== "true"; var spinnerDefaultConfig = { // Frames: symbols, interval: 80 }; var concurrentSpinners = []; var spinnerState = "idle"; var multiSpinner = (0, import_ora.default)({ spinner: { frames: [""], interval: spinnerDefaultConfig.interval }, isSilent: !spinnerEnabled }); var getMultiSpinnerFrame = () => ` ${concurrentSpinners.map((s) => s.getFrame()).join("\n")}`; function updateMultiSpinner() { multiSpinner.text = getMultiSpinnerFrame(); } var multiSpinnerUpdateInterval = void 0; function startMultiSpinner() { if (spinnerEnabled && spinnerState !== "running" && !isProcessing) { if (concurrentSpinners.length >= 2) { if (multiSpinnerUpdateInterval === void 0) { multiSpinnerUpdateInterval = setInterval( updateMultiSpinner, spinnerDefaultConfig.interval ); } multiSpinner.start(); spinnerState = "running"; } else if (concurrentSpinners.length === 1) { concurrentSpinners[0].startOraSpinner(); spinnerState = "running"; } } } function pauseMultiSpinner() { if (spinnerState === "running") { spinnerState = "paused"; if (concurrentSpinners.length >= 2) { multiSpinner.stop(); } else if (concurrentSpinners.length === 1) { concurrentSpinners[0].stopOraSpinner(); } } } function resumeMultiSpinner() { if (spinnerState === "paused" && !isProcessing) { spinnerState = "running"; if (concurrentSpinners.length >= 2) { multiSpinner.start(); } else if (concurrentSpinners.length === 1) { concurrentSpinners[0].startOraSpinner(); } } } function stopMultiSpinner() { spinnerState = "idle"; if (concurrentSpinners.length >= 2) { multiSpinner.stop(); } else if (concurrentSpinners.length === 1) { concurrentSpinners[0].stopOraSpinner(); } concurrentSpinners.forEach((s) => s.abort()); if (multiSpinnerUpdateInterval !== void 0) { clearInterval(multiSpinnerUpdateInterval); multiSpinnerUpdateInterval = void 0; } } var SpinnerImpl = class { spinner; /** * @param options {@link ora.Options} */ constructor(options) { this.spinner = (0, import_ora.default)({ ...options, isSilent: !spinnerEnabled }); } // Documented in interface get text() { return this.spinner.text; } // Documented in interface set text(text) { this.spinner.text = text; } /** * Stops the spinner and removes it from the concurrent spinner Array */ cleanup() { const index = concurrentSpinners.indexOf(this); if (index > -1) { if (this.spinner.isSpinning) { this.spinner.stop(); } concurrentSpinners.splice(index, 1); } if (concurrentSpinners.length === 0) { stopMultiSpinner(); } } /** * Starts the internal ora spinner directly (used when only one spinner is active) */ startOraSpinner() { if (!this.spinner.isSpinning) { this.spinner.start(); } } /** * Stops the internal ora spinner directly (used when only one spinner is active) */ stopOraSpinner() { if (this.spinner.isSpinning) { this.spinner.stop(); } } /** * @returns the next frame of the spinner */ getFrame() { return this.spinner.frame(); } // Documented in interface start(text) { if (text) { this.text = text; } const hadOnlyOne = concurrentSpinners.length === 1; if (!concurrentSpinners.includes(this)) { concurrentSpinners.push(this); } if (hadOnlyOne && concurrentSpinners.length === 2 && spinnerState === "running") { concurrentSpinners[0].stopOraSpinner(); if (multiSpinnerUpdateInterval !== void 0) { clearInterval(multiSpinnerUpdateInterval); multiSpinnerUpdateInterval = void 0; } spinnerState = "idle"; } startMultiSpinner(); return this; } /** * Stops the spinner and executes cleanup of this instance. * @param text stop text for the spinner * @param stopType either {@link SpinnerStopType} or {@link ora.PersistOptions} * @returns The spinner instance. */ _stop(text, stopType = "abort") { this.text = text ?? this.text; pauseMultiSpinner(); switch (stopType) { case "success": this.spinner.succeed(this.text); break; case "info": this.spinner.info(this.text); break; case "fail": this.spinner.fail(this.text); break; case "warn": this.spinner.warn(this.text); break; case "abort": this.spinner.stop(); break; default: this.spinner.stopAndPersist(stopType); } this.cleanup(); resumeMultiSpinner(); return this; } // Documented in interface stop(text, stopOptions = {}) { return this._stop(text, stopOptions); } // Documented in interface abort() { return this._stop(void 0, "abort"); } // Documented in interface info(text) { return this._stop(text, "info"); } // Documented in interface succeed(text) { return this._stop(text, "success"); } // Documented in interface fail(text) { return this._stop(text, "fail"); } // Documented in interface warn(text) { return this._stop(text, "warn"); } }; var SpinnerService = { /** * Creates an inactive spinner. * @param options - Optional {@link Options} for the spinner. * @returns A new Spinner instance. */ create: (options = {}) => new SpinnerImpl({ ...options, interval: spinnerDefaultConfig.interval }), /** * Creates and starts a new spinner. * @param text - Optional text to be displayed. * @param options - Optional {@link Options} for the spinner. * @returns A new Spinner instance. */ start: (text, options = {}) => new SpinnerImpl({ ...options, interval: spinnerDefaultConfig.interval }).start(text), /** * Pauses all running spinners. */ pause: pauseMultiSpinner, /** * Resumes all paused spinners. */ resume: resumeMultiSpinner, /** * Resets all running or idle spinners. */ reset: stopMultiSpinner, /** * Marks all spinners as succeeded with optional text. * @param text - Optional text to be displayed. */ successAll: (text) => concurrentSpinners.forEach((s) => s.succeed(text)), /** * Marks all spinners as failed with optional text. * @param text - Optional text to be displayed. */ failAll: (text) => concurrentSpinners.forEach((s) => s.fail(text)), disable: () => { spinnerEnabled = false; stopMultiSpinner(); }, enable: () => { spinnerEnabled = true; }, getInterval: () => spinnerDefaultConfig.interval, _getMockedSpinner: function(text) { const mockSpinner = { abort: (..._) => mockSpinner, fail: (..._) => mockSpinner, info: (..._) => mockSpinner, start: (..._) => mockSpinner, stop: (..._) => mockSpinner, succeed: (..._) => mockSpinner, text: text ?? "", warn: (..._) => mockSpinner }; return mockSpinner; } }; // src/core/modules/logger/logger.ts var WaveLogger = class { boxSize = 80; warningBoxSize = 90; /** Disables logging */ disabled = false; /** Loading spinner instance */ spinner = { create: () => SpinnerService.create(), disable: SpinnerService.disable, start: (text) => SpinnerService.start(text), pause: SpinnerService.pause, resume: SpinnerService.resume, abort: SpinnerService.reset }; /** Toggle if the logger should log */ disable() { this.disabled = true; SpinnerService.disable(); } /** Log something to the cli */ log(text) { this._log(`${this._padString(text, 1)}`, "INFO"); } /** Prints a debug message */ debug(text, context) { if (global.VERBOSE_MODE && context.startsWith(global.VERBOSE_MODE.replace("%", ""))) { this.spinner.pause(); console.log( `${(0, import_chalk.yellow)("DEBUG")}${this._padString(`[${context}] ${text}`, 1)}` ); this.debounceTimeout = setTimeout(() => { this.spinner.resume(); }, SpinnerService.getInterval()); } this._printToFile(`[${context}] ${text}`, "DEBUG"); } /** Log level information (verbose output) */ info(text, pad2) { this._log( pad2 === void 0 || pad2 ? this._padString(text, 0) : text, "INFO" ); } /** Log level for successful tasks */ success(text) { this._log((0, import_chalk.green)(this._padString(text, 0)), "INFO"); } /** Log level information (verbose output) */ error(text) { this._log((0, import_chalk.red)(this._padString(text, 0)), "ERROR"); } /** Log level for warnings */ warn(text) { const boxedText = ` ${(0, import_chalk.yellow)( "Warning " + (0, import_lodash.repeat)("─", this.warningBoxSize - 8) )} ${(0, import_chalk.yellow)(this._padString(text))} ${(0, import_chalk.yellow)( (0, import_lodash.repeat)("─", this.warningBoxSize) )} `; this._log(boxedText, "WARNING"); } /** Logs the text in a box */ logBox(text) { if (this.disabled) { return; } const boxedText = ` ${(0, import_chalk.magenta)((0, import_lodash.repeat)("─", this.boxSize))} ${(0, import_chalk.magenta)( this._padString(text) )} ${(0, import_chalk.magenta)((0, import_lodash.repeat)("─", this.boxSize))} `; this._log(boxedText, "INFO"); } /** Cleanup logger. Used before system.exit() */ clean() { this.spinner.abort(); } /** Initialize log file stream */ async initLogFile(root) { if (!this.disabled) { await initLogFileStream(root); } } debounceTimeout; /** Logs the text and pauses a loading spinner in between if there is one */ _log(text, scope) { if (this.disabled) { return; } if (this.debounceTimeout) { clearTimeout(this.debounceTimeout); } this.spinner.pause(); if (scope === "ERROR") { console.log(text); } else { terminalAction(() => Promise.resolve(console.log(text))); } this.debounceTimeout = setTimeout(() => { this.spinner.resume(); }, SpinnerService.getInterval()); this._printToFile(text, scope); } /** Logs the text with timestamp prefix to file */ _printToFile(text, scope) { logFileStream.write( `${getOffsetTime().toISOString()} ${scope} ${stripAnsi(text)} ` ); } /** Cleans a string and adds the necessary space */ // eslint-disable-next-line @typescript-eslint/typedef _padString(text, start = 2) { return `${text}`.split(/[\n\r]/).map((line) => `${(0, import_lodash.repeat)(" ", start)}${line}`).join(import_os.EOL); } }; function stripAnsi(text) { const pattern = [ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))" ].join("|"); return text.replace(new RegExp(pattern, "g"), ""); } function getOffsetTime() { const now = /* @__PURE__ */ new Date(); const timezoneOffset = now.getTimezoneOffset() * 6e4; now.setTime(now.getTime() - timezoneOffset); return now; } var logger = new WaveLogger(); // src/utils/node-resolve-package.ts function nodeResolvePackage(pck, cwd) { return require.resolve(pck, { paths: [cwd] }); } // src/utils/error-handler/errors.ts var import_chalk2 = require("chalk"); var CREDENTIALS_ERROR = `Authentication failed! Please make sure that: * The provided client ID and the secret are correct. * The provided client ID has needed scopes. Deploy scopes: * app-engine:apps:run * app-engine:apps:install * app-engine:apps:delete Uninstall scopes: * app-engine:apps:delete Telemetry scopes: * app-engine:apps:run Development server needs all the scopes specified in the app.config. For more details on how you can configure authentication, see ${"https://dt-url.net/qw024j8" /* APP_TOOLKIT_CONFIGURATION */} `; function toError(e) { return e instanceof Error ? e : new Error(String(e)); } function throwWithDynatraceErrorCode(error, errorCode) { throw new Error(`ERROR [${(0, import_chalk2.green)(errorCode)}]: ${error.message}`); } // src/plugin/require-plugins.ts var DEBUG_CONTEXT = "PLUGIN"; function requirePlugins(options) { return options.plugins.map((p) => requirePlugin(p, options.root)).filter((p) => !!p); } function requirePlugin(plugin, root) { try { return require(nodeResolvePackage(plugin, root)).default; } catch (e) { logger.warn(`Could not require plugin ${plugin}. It will be ignored.`); logger.debug(toError(e).toString(), DEBUG_CONTEXT); } return void 0; } // src/build/diagnostic.ts var import_path2 = require("path"); // src/build/utils/import-typescript.ts var typescript; function resolveTypescript(root) { if (!typescript) { typescript = require(nodeResolvePackage("typescript", root)); } return typescript; } // src/build/diagnostic.ts function fromEsbuildMessage(message, category) { return { category, text: message.text, filePath: message.location?.file ? (0, import_path2.resolve)(message.location.file) : void 0, line: message.location?.line ?? 1, column: (message.location?.column ?? 0) + 1 }; } function fromTypeScriptDiagnostic(tsDiagnostic, root) { const ts = resolveTypescript(root); const code = `TS${tsDiagnostic.code}`; const text = ts.flattenDiagnosticMessageText(tsDiagnostic.messageText, "\n"); const category = tsDiagnostic.category === ts.DiagnosticCategory.Error ? "Error" /* error */ : "Warning" /* warning */; if (tsDiagnostic.file) { const { line, character } = ts.getLineAndCharacterOfPosition( tsDiagnostic.file, tsDiagnostic.start ); return { code, category, text, filePath: tsDiagnostic.file.fileName, column: character + 1, line: line + 1 }; } return { code, category, text, filePath: void 0, line: 1, column: 1 }; } // src/build/compile.ts var import_path5 = require("path"); // src/build/utils/run-esbuild.ts var import_path4 = require("path"); // src/build/utils/exclude-vendor-from-source-map.ts var import_fs2 = require("fs"); var EMPTY_SOURCEMAP = "//# sourceMappingURL=data:application/json;base64,ewogICJtYXBwaW5ncyI6ICJBQUFBQSIsCiAgInNvdXJjZXMiOiBbIiJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiIl0sCiAgIm5hbWVzIjogWyIiXSwKICAidmVyc2lvbiI6IDMsCiAgImZpbGUiOiAiIgp9"; var JS_EXTENSIONS = [".js", ".mjs", ".cjs"]; var excludeVendorFromSourceMapPlugin = () => ({ name: "excludeVendorFromSourceMap", setup(build) { build.onLoad({ filter: /node_modules/ }, (args) => { if (JS_EXTENSIONS.some((ext) => args.path.endsWith(ext))) { return { contents: `${(0, import_fs2.readFileSync)(args.path, "utf8")} ${EMPTY_SOURCEMAP}`, loader: "default" }; } }); } }); // src/utils/get-lib-src-dir.ts var import_path3 = require("path"); function getLibSrcDir() { if (__filename.endsWith("bin.js")) { return (0, import_path3.resolve)(__dirname); } if (__filename.endsWith("index.js")) { return (0, import_path3.resolve)(__dirname, "src"); } if (__filename.endsWith(".ts")) { return (0, import_path3.resolve)(__dirname, ".."); } throw new Error("Could not retrieve lib source folder!", { cause: 257 /* INTERNAL */ }); } // src/build/utils.ts var import_fast_glob = __toESM(require("fast-glob")); // src/utils/terminal/layout.ts var import_chalk3 = __toESM(require("chalk")); // src/build/build-watchers/utils.ts function getDefaultEsbuildOptions() { return { sourcemap: true, color: true, loader: { // Esbuild should not bundle .node files but also not throw an error if .node files are required ".node": "empty" }, bundle: true, logLevel: "silent", charset: "utf8", write: false }; } // src/build/utils/run-esbuild.ts async function runEsbuild(outdir, options, abortSignal) { const esbuild = require(nodeResolvePackage("esbuild", options.cwd)); if (!esbuild.context) { throw new Error( `esbuild context function not found. This is likely happening because you may have dependencies in your project that are installing an older version of esbuild. Please fix your dependencies and try again. ${esbuild.version ? `Your current esbuild version being used is ${esbuild.version}.` : ""}`, { cause: 258 /* USER */ } ); } const { context } = esbuild; const sourcemap = options.sourcemapOptions?.sourcemap ?? false; if (sourcemap && !options.sourcemapOptions?.includeVendorSourceMaps) { options.plugins = [ ...options.plugins ?? [], excludeVendorFromSourceMapPlugin() ]; } const ctx = await context({ ...getDefaultEsbuildOptions(), entryPoints: options.entryPoints.map( (entryPoint) => (0, import_path4.isAbsolute)(entryPoint) ? entryPoint : (0, import_path4.join)(options.cwd, entryPoint) ), outbase: options.outbase, target: options.target, minify: options.minify ?? false, sourcemap, platform: options.platform || "browser", plugins: options.plugins || [], external: options.external || [], sourceRoot: options.sourceRoot, tsconfig: (0, import_path4.isAbsolute)(options.tsconfig) ? options.tsconfig : (0, import_path4.join)(options.cwd, options.tsconfig), outdir, format: options.format, globalName: options.globalName, metafile: options.metafile, write: false, // Necessary for type safety. define: options.define }); abortSignal?.addEventListener("abort", () => ctx.cancel()); const resultPromise = ctx.rebuild(); try { return await resultPromise; } finally { ctx.dispose(); } } // src/build/compile.ts async function compile(options, abortSignal) { const timerStart = Date.now(); try { const tmpOutDir = (0, import_path5.join)(options.cwd, "dist"); const buildResult = await runEsbuild(tmpOutDir, options, abortSignal); const timerEnd = Date.now(); const diagnostics = buildResult.warnings.map((message) => fromEsbuildMessage(message, "Warning" /* warning */)).filter((diagnostic) => !diagnostic.filePath?.includes("node_modules/")); const outDir = options.outDir ?? "dist"; const fileMap = buildResult.outputFiles.reduce((prev, file) => { const filepath = `${import_path5.sep}${(0, import_path5.join)( outDir, file.path.replace(tmpOutDir, "") )}`; return { ...prev, [filepath]: { content: Buffer.from(file.contents) } }; }, {}); return { fileMap, diagnostics, duration: timerEnd - timerStart }; } catch (e) { const timerEnd = Date.now(); if (isBuildFailure(e)) { const diagnostics = [ // Convert esbuild errors ...e.errors.map( (message) => fromEsbuildMessage(message, "Error" /* error */) ), // Convert esbuild warnings, but ignore warnings in node_modules ...e.warnings.map( (message) => fromEsbuildMessage(message, "Warning" /* warning */) ).filter( (diagnostic) => !diagnostic.filePath?.includes("node_modules/") ) ]; return { fileMap: {}, diagnostics, duration: timerEnd - timerStart }; } const error = e instanceof Error ? e : new Error(String(e), { cause: 261 /* COMPILATION */ }); throw error; } } function isBuildFailure(error) { return typeof error === "object" && typeof error?.errors !== "undefined"; } // src/dev/pretty-error.ts var import_code_frame = require("@babel/code-frame"); var import_chalk4 = require("chalk"); var import_fs3 = require("fs"); var import_ansi_to_html = __toESM(require("ansi-to-html")); var ansiToHtmlConverter = new import_ansi_to_html.default({}); function logDiagnostics(diagnostics) { for (const diagnostic of diagnostics) { logger.info(prettyConsoleError(diagnostic)); } } var prettyConsoleError = (diagnostic) => { const file = diagnostic.filePath ? `${(0, import_chalk4.cyan)(diagnostic.filePath)}:${(0, import_chalk4.yellow)(diagnostic.line)}:${(0, import_chalk4.yellow)( diagnostic.column )}` : "<no file>"; const category = diagnostic.category === "Error" /* error */ ? (0, import_chalk4.red)("Error:") : (0, import_chalk4.yellow)("Warning:"); const code = diagnostic.code ? (0, import_chalk4.gray)(` ${diagnostic.code}`) : ""; const header = `> ${file} - ${category}${code} ${diagnostic.text}`; const codeFrame = (0, import_chalk4.bold)(`${createCodeFrame(diagnostic)}`); return `${header} ${codeFrame}`; }; var createCodeFrame = (error) => { try { if (!error.filePath) { return ""; } const fileString = (0, import_fs3.readFileSync)(error.filePath, "utf-8"); const isFileEmpty = fileString === ""; const location = { start: { line: error.line, column: isFileEmpty ? 1 : error.column } }; return (0, import_code_frame.codeFrameColumns)(isFileEmpty ? " " : fileString, location, { forceColor: true }); } catch (error2) { console.error(error2); } return ""; }; // src/utils/config/extract-dt-app-config-from-ts.ts var import_fs5 = require("fs"); // src/utils/config/native-node-modules-plugin.ts var nativeNodeModulesPlugin = { name: "native-node-modules", setup(build) { build.onResolve({ filter: /\.node$/, namespace: "file" }, (args) => ({ path: require.resolve(args.path, { paths: [args.resolveDir] }), namespace: "node-file" })); build.onLoad({ filter: /.*/, namespace: "node-file" }, (args) => ({ contents: ` import path from ${JSON.stringify(args.path)} try { module.exports = require(path) } catch {} ` })); build.onResolve({ filter: /\.node$/, namespace: "node-file" }, (args) => ({ path: args.path, namespace: "file" })); const opts = build.initialOptions; opts.loader = opts.loader || {}; opts.loader[".node"] = "file"; } }; // src/utils/config/extract-dt-app-config-from-ts.ts var import_crypto = __toESM(require("crypto")); // src/build/type-checker.ts var import_lodash2 = require("lodash"); var import_micromatch = require("micromatch"); var import_fs4 = require("fs"); var import_path6 = require("path"); // src/build/fixed-build-type-options.ts var fixedGeneralOptions = { noEmit: true, skipLibCheck: true }; function getFixedBuildTypeOptions(root) { const { ScriptTarget } = resolveTypescript(root); return { ["ui" /* UI */]: { target: ScriptTarget.ES2022, ...fixedGeneralOptions }, ["functions" /* FUNCTIONS */]: { target: ScriptTarget.ESNext, ...fixedGeneralOptions }, ["actions" /* ACTIONS */]: { target: ScriptTarget.ESNext, ...fixedGeneralOptions }, ["widgets" /* WIDGETS */]: { target: ScriptTarget.ESNext, ...fixedGeneralOptions } }; } // src/build/type-checker.ts function check(options) { const { createCompilerHost, createProgram } = resolveTypescript( options.srcRoot ); const { tsConfigContents, tsConfigFileDiagnostics: fileDiagnostics } = getTsConfig(options.srcRoot, options.tsConfigFullPath); const isSolutionConfig = tsConfigContents.projectReferences && tsConfigContents.projectReferences.length > 0 && tsConfigContents.fileNames.length === 0; if (isSolutionConfig) { return handleSolutionConfigWithBuilder(options); } const compilerOptions = { ...tsConfigContents.options, ...getFixedBuildTypeOptions(options.srcRoot)[options.buildType], incremental: false }; const host = createCompilerHost(tsConfigContents.options); let entryFiles = []; const purgedEntryFiles = (0, import_micromatch.not)( options.entrypoints, tsConfigContents.raw.exclude ).map( (filepath) => (0, import_path6.isAbsolute)(filepath) ? filepath : (0, import_path6.join)(options.srcRoot, filepath) ); if (tsConfigContents.options.composite) { entryFiles = [ .../* @__PURE__ */ new Set([...purgedEntryFiles, ...tsConfigContents.fileNames]) ]; } else { const typeDefinitions = tsConfigContents.fileNames.filter( (fileName) => fileName.endsWith(".d.ts") ); const purgedTypeDefs = (0, import_micromatch.not)( typeDefinitions, tsConfigContents.raw.exclude ).map( (filepath) => (0, import_path6.isAbsolute)(filepath) ? filepath : (0, import_path6.join)(options.srcRoot, filepath) ); entryFiles = [.../* @__PURE__ */ new Set([...purgedEntryFiles, ...purgedTypeDefs])]; } const program2 = createProgram({ rootNames: entryFiles, options: compilerOptions, host, configFileParsingDiagnostics: fileDiagnostics, projectReferences: tsConfigContents.projectReferences }); const results = program2.emit(); const allDiagnostics = [ ...program2.getSyntacticDiagnostics(), ...program2.getSemanticDiagnostics(), ...program2.getConfigFileParsingDiagnostics(), ...program2.getGlobalDiagnostics(), ...program2.getDeclarationDiagnostics(), ...results.diagnostics ]; return allDiagnostics.map( (d) => fromTypeScriptDiagnostic(d, options.appRoot) ); } function getTsConfig(cwd, tsConfigPath) { const { sys, readConfigFile, parseJsonConfigFileContent } = resolveTypescript(cwd); const configFile = readConfigFile( tsConfigPath, (path) => (0, import_fs4.readFileSync)(path).toString() ); let tsConfigFileDiagnostics = []; if (configFile.error && (0, import_lodash2.has)(configFile.error.file, "parseDiagnostics")) { tsConfigFileDiagnostics = (0, import_lodash2.get)( configFile.error?.file, "parseDiagnostics" ); } const tsConfigContents = parseJsonConfigFileContent( configFile.config, sys, (0, import_path6.dirname)(tsConfigPath) ); tsConfigFileDiagnostics.push(...tsConfigContents.errors); return { tsConfigContents, tsConfigFileDiagnostics }; } function handleSolutionConfigWithBuilder(options) { const { sys, createSolutionBuilderHost, createSolutionBuilder } = resolveTypescript(options.srcRoot); const allDiagnostics = []; const { noEmit, ...solutionBuildTypeOptions } = getFixedBuildTypeOptions( options.appRoot )[options.buildType]; const host = createSolutionBuilderHost( sys, void 0, (diagnostic) => { if (diagnostic.code !== 6357) { allDiagnostics.push(diagnostic); } }, (diagnostic) => { if (diagnostic.code !== 6357) { allDiagnostics.push(diagnostic); } } ); const originalWriteFile = host.writeFile; host.writeFile = (path, data, writeByteOrderMark) => { if (path.endsWith(".d.ts") || path.endsWith(".d.ts.map") || path.endsWith(".tsbuildinfo")) { originalWriteFile?.(path, data, writeByteOrderMark); } }; const solutionBuilder = createSolutionBuilder( host, [options.tsConfigFullPath], // Root config files { dry: false, force: false, verbose: false, ...solutionBuildTypeOptions } ); solutionBuilder.build(); return allDiagnostics.map( (d) => fromTypeScriptDiagnostic(d, options.appRoot) ); } // src/utils/config/extract-dt-app-config-from-ts.ts async function extractDtAppConfigFromTs(root, configFilePath, tsconfigPath, skipTypeCheck, format = "cjs") { logger.debug( "Perform build and type-check on configuration file", DEBUG_CONTEXT ); const externalPackages = getProjectDependencies(format, root); const buildPromise = compile({ cwd: (0, import_path7.dirname)(configFilePath), tsconfig: tsconfigPath, entryPoints: [configFilePath], external: ["esbuild", "vite", ...externalPackages], platform: "node", target: "node16", format, outDir: "dist", plugins: [nativeNodeModulesPlugin], define: {} // Overwrite define to not break esbuild plugins defined in app.config }); let typeCheckDiagnostics = []; if (!skipTypeCheck) { typeCheckDiagnostics = check({ appRoot: root, srcRoot: root, entrypoints: [configFilePath], tsConfigFullPath: tsconfigPath, buildType: "ui" /* UI */ }); } const buildResult = await buildPromise; const diagnostics = [...buildResult.diagnostics, ...typeCheckDiagnostics]; logDiagnostics(diagnostics); if (diagnostics.some( (diagnostic) => diagnostic.category === "Error" /* error */ )) { throw new Error("Reading your configuration file failed.", { cause: 258 /* USER */ }); } logger.debug("Successfully prepared configuration to be read", DEBUG_CONTEXT); const randomTmpDir = generateRandomDirectoryName(configFilePath, ".dt-app"); const dtAppConfigJsPath = (0, import_path7.join)( randomTmpDir, format === "cjs" ? "app.config.js" : "app.config.mjs" ); if (!(0, import_fs5.existsSync)(randomTmpDir)) { (0, import_fs5.mkdirSync)(randomTmpDir, { recursive: true }); } (0, import_fs5.writeFileSync)( dtAppConfigJsPath, buildResult.fileMap[(0, import_path7.join)(import_path7.sep, "dist", "app.config.js")].content.toString( "utf-8" ) ); try { const result = format === "cjs" ? ( // eslint-disable-next-line @typescript-eslint/no-require-imports await require(dtAppConfigJsPath) ) : ( // For ESM we need to adjust path to be able to import it on Windows (await import((0, import_url.pathToFileURL)(dtAppConfigJsPath).href)).default ); return result; } catch (error) { throw new Error(`Error at resolving of app.config: ${error}`, { cause: 258 /* USER */ }); } finally { try { (0, import_fs5.rmSync)(randomTmpDir, { recursive: true }); } catch (e) { logger.debug( `A temporary file can't be cleaned up: ${dtAppConfigJsPath}. Error: ${toError(e).message}`, DEBUG_CONTEXT ); } } } function getProjectDependencies(format, root) { if (format !== "esm") { return []; } const externalPackages = []; try { const packageJsonPath = (0, import_path7.join)(root, "package.json"); const packageJson = JSON.parse((0, import_fs5.readFileSync)(packageJsonPath, "utf-8")); const dependencies = Object.keys(packageJson?.dependencies || {}); const devDependencies = Object.keys(packageJson?.devDependencies || {}); externalPackages.push(...dependencies, ...devDependencies); } catch { logger.debug( "Failed to read package.json for external packages", DEBUG_CONTEXT ); } return externalPackages; } function generateRandomDirectoryName(filePath, dir) { const randomString = import_crypto.default.randomBytes(4).toString("hex"); return (0, import_path7.join)((0, import_path7.dirname)(filePath), `${dir}/${randomString}`); } // src/shared/utils/read-json.ts var import_fs6 = require("fs"); function readJson(file) { if (!(0, import_fs6.existsSync)(file) && !(0, import_fs6.lstatSync)(file, { throwIfNoEntry: false })?.isFile()) { throw new Error(`[read-json.ts] File does not exist: ${file}`, { cause: 257 /* INTERNAL */ }); } const content = (0, import_fs6.readFileSync)(file, "utf-8"); return JSON.parse(content); } // src/utils/validation/file-existence.ts var import_node_path = require("node:path"); var import_fs7 = require("fs"); var import_inquirer = require("inquirer"); // src/utils/formatters.ts var import_path8 = require("path"); var import_lodash3 = require("lodash"); function appendFileExtension({ name, suffix, allowNested = true }) { const { name: nameWithoutExtension, dir, base } = (0, import_path8.parse)(name); const parsedName = allowNested ? nameWithoutExtension : base; const fileName = `${parsedName}.${suffix}`; if (allowNested) { return (0, import_path8.join)(dir, fileName); } return fileName; } // src/utils/validation/file-existence.ts function checkIfFileExists(fileSearch) { const { filePath } = getFileNameAndPath(fileSearch); return (0, import_fs7.existsSync)(filePath); } function getFileNameAndPath({ allowNested = true, ...options }) { let fileName = options.suffix ? appendFileExtension({ name: options.name, suffix: options.suffix, allowNested }) : options.name; let filePath = ""; if (options.folder) { filePath = (0, import_node_path.join)(options.cwd, options.folder, fileName); } else { filePath = (0, import_node_path.join)(options.cwd, fileName); } if (!options.suffix) { fileName = (0, import_node_path.basename)(filePath); } return { fileName, filePath }; } // src/utils/config/get-dt-app-file-config.ts var import_fs9 = require("fs"); // src/shared/utils/filesystem.ts var import_fs8 = require("fs"); function mkdirp(path) { if (!(0, import_fs8.existsSync)(path)) { (0, import_fs8.mkdirSync)(path); } } // src/utils/config/get-dt-app-file-config.ts async function getDtAppFileConfig(root, skipTypeCheck = false) { logger.debug("Searching for configuration file", DEBUG_CONTEXT); const configFileName = await getConfigFileName(root); if (configFileName) { const fullPath = (0, import_path9.join)(root, configFileName); logger.debug(`Found ${fullPath}`, DEBUG_CONTEXT); const fileExtension = (0, import_path9.extname)(configFileName); switch (fileExtension) { case ".js": { return require(fullPath); } case ".json": { try { return readJson(fullPath); } catch (e) { if (e instanceof Error) { throw new Error(e.message, { cause: 258 /* USER */ }); } else { throw new Error(String(e), { cause: 258 /* USER */ }); } } } case ".ts": case ".cts": { const tsconfigPath = findTsConfigPath(root); return await extractDtAppConfigFromTs( root, fullPath, tsconfigPath, skipTypeCheck, fileExtension === ".ts" ? "esm" : "cjs" ); } } } throw Error( "The current directory does not appear to contain a Dynatrace App. Please verify that you're in the correct application directory and re-run this command inside your Dynatrace App folder." ); } function findTsConfigPath(root) { const isFilePresent = checkIfFileExists({ cwd: root, name: "tsconfig", suffix: "json" }); if (isFilePresent) { return (0, import_path9.join)(root, "tsconfig.json"); } mkdirp((0, import_path9.join)(root, ".dt-app")); const tempTsConfigPath = (0, import_path9.join)(root, ".dt-app", "tmp-app-tsconfig.json"); const tempTsConfig = { compilerOptions: { target: "ESNext", module: "ESNext", moduleResolution: "node", allowSyntheticDefaultImports: true, esModuleInterop: true, strict: true, skipLibCheck: true, resolveJsonModule: true, noImplicitAny: false, types: ["node"], typeRoots: ["../node_modules/@types", "../node_modules/@dynatrace"] }, include: ["../app.config.ts", "../app.config.cts"], exclude: ["../node_modules", "../dist"] }; try { (0, import_fs9.writeFileSync)(tempTsConfigPath, JSON.stringify(tempTsConfig, null, 2)); } catch { throw new Error( `Could not write temporary tsconfig at ${tempTsConfigPath}`, { cause: 258 /* USER */ } ); } return tempTsConfigPath; } async function getConfigFileName(cwd) { const configFileExtensionList = ["cts", "ts", "js", "json"]; const configFiles = configFileExtensionList.filter( (ext) => checkIfFileExists({ name: "app", suffix: `config.${ext}`, cwd }) ).map((ext) => `app.config.${ext}`); if (configFiles.length > 1) { logger.warn(`Multiple app.config files have been detected. (${configFiles.join( ", " )}) ${configFiles[0]} will be used.`); } return configFiles[0]; } // src/utils/config/cli-options-utils.ts var import_path15 = require("path"); var import_fs13 = require("fs"); var import_lodash5 = require("lodash"); var import_chalk7 = require("chalk"); // src/utils/reporting/reporting.ts var import_openkit_js = require("@dynatrace/openkit-js"); var import_lodash4 = require("lodash"); var import_os3 = require("os"); var import_path13 = require("path"); // src/utils/terminal/execute-command.ts var import_commander = require("commander"); // src/utils/clouddev/codespaces.ts var codespacesEnv = process.env.CODESPACE_NAME && process.env.CODESPACE_NAME.trim() !== "" ? process.env.CODESPACE_NAME : void 0; // src/utils/clouddev/gitpod.ts var gitpodEnv = process.env.GITPOD_WORKSPACE_URL && process.env.GITPOD_WORKSPACE_URL.trim() !== "" ? process.env.GITPOD_WORKSPACE_URL : void 0; // src/utils/clouddev/cloud-dev.ts var customEnv = process.env.DT_APP_DEV_ENVIRONMENT_URL && process.env.DT_APP_DEV_ENVIRONMENT_URL.trim() !== "" ? process.env.DT_APP_DEV_ENVIRONMENT_URL : void 0; // src/utils/request.ts var import_undici = require("undici"); async function dtFetch(url, options) { return (0, import_undici.fetch)(url, options); } // src/utils/config/validate-wave-config.ts var import_ajv_errors = __toESM(require("ajv-errors")); var import__ = __toESM(require("ajv/dist/2019")); var import_chalk5 = require("chalk"); var import_path10 = require("path"); var import_fs10 = require("fs"); var import_micromatch2 = require("micromatch"); function initSchema(schemaPath) { const ajv = new import__.default({ allErrors: true, formats: { "relative-path": { type: "string", validate: (val) => !(0, import_path10.isAbsolute)(val) }, "valid-glob": { type: "string", validate: (val) => { if (!val || val.length === 0) { return false; } try { (0, import_micromatch2.parse)(val); return true; } catch { return false; } } }, url: { type: "string", validate: (val) => { try { const url = new URL(val); return url.protocol === "http:" || url.protocol === "https:"; } catch { return false; } } }, // ESBuild's mangleProps and reserveProps are typed as RegExp, // which ts-json-schema-generator converts to format: "regex" // Accept any string as a valid regex pattern regex: true }, strict: false }); (0, import_ajv_errors.default)(ajv); const schemaContent = (0, import_fs10.readFileSync)(schemaPath, "utf-8"); const schema = JSON.parse(schemaContent); return { appConfigSchema: schema, validateAppConfigSchema: ajv.compile(schema) }; } async function validateConfig(schemaPath, options, validationLevel = "default") { if (validationLevel === "none" || process.env.DT_APP_SKIP_CONFIG_VALIDATION === "true") { return options; } const { validateAppConfigSchema } = initSchema(schemaPath); if (validateAppConfigSchema(options)) { return options; } else { for (const err of validateAppConfigSchema.errors) { logger.info(prettyConsoleError2({ error: err })); } throw new Error("App config validation failed", { cause: 264 /* VALIDATION */ }); } } function prettyConsoleError2({ title = "App config", error }) { const type = import_chalk5.red`error:`; let instancePath = error.instancePath ?? ""; if (error.schemaPath && error.schemaPath.indexOf("additionalProperties") >= 0 && instancePath.indexOf("app") < 0) { instancePath = instancePath?.substr(0, error.instancePath.lastIndexOf("/")); } const prop = instancePath ? instancePath.split("/").reduce((acc, cur) => { if (!acc) { return cur; } if (!isNaN(+cur)) { return `${acc}[${cur}]`; } return `${acc}.${cur}`; }) : "CliOptions"; const message = error.message ? error.message.replace(/"/gi, "'") : ""; return `> ${(0, import_chalk5.bold)(title)}: ${type} '${prop}' ${message}.`; } function validateLegacyConfigOptions(fileConfig) { if (fileConfig.distDir) { throw new Error( 'The "distDir" option is no longer supported. Please remove "distDir" from the app.config.' ); } if (fileConfig?.server?.open) { throw new Error( 'The "server.open" option is no longer supported. Please remove "server.open" from the app.config.' ); } } // src/plugin/plugin-provider.ts var registeredHooks = { "before-build": [], "before-serve": [] }; async function invoke(hook, args) { for (const h of get2(hook)) { logger.debug( `Running '${hook}' hook of plugin '${h.name}'...`, DEBUG_CONTEXT ); try { await h.callBack(...args); } catch (e) { logger.error( `An error occurred during the '${hook}' hook of plugin '${h.name}': ${toError(e).toString()}` ); continue; } logger.debug( `Finished '${hook}' hook of plugin '${h.name}'.`, DEBUG_CONTEXT ); } } function get2(hook) { return registeredHooks[hook]; } function register(plugin) { const pluginHooks = { "before-build": [], "before-serve": [] }; const stub = { beforeBuild(callBack) { pluginHooks["before-build"].push({ name: plugin.name, callBack }); }, beforeServe(callBack) { pluginHooks["before-serve"].push({ name: plugin.name, callBack }); } }; try { plugin.setup(stub); } catch (e) { logger.error( `An error occurred during the setup of plugin '${plugin.name}': ${toError(e).toString()}` ); return; } Object.keys(pluginHooks).forEach((key) => { if (isHook(key)) { registeredHooks[key].push(...pluginHooks[key]); } }); } function isHook(value) { const validHooks = ["before-build", "before-serve"]; return validHooks.includes(value); } function registerAll(pluginArray) { for (const plugin of pluginArray) { register(plugin); } } var pluginProvider = { registeredHooks, register, registerAll, invoke }; // src/utils/config/copy-json-config-schema.ts var import_path11 = require("path"); var import_fs11 = require("fs"); function getAppConfigSchemaPath(root, projectName) { try { const dtAppPath = findDtAppPath(root, projectName ?? ""); const schemaPath = (0, import_path11.join)(dtAppPath, "lib", "src", "app.config.schema.json"); if ((0, import_fs11.existsSync)(schemaPath)) { return schemaPath; } } catch { } return (0, import_path11.join)(getLibSrcDir(), "app.config.schema.json"); } function findDtAppPath(root, projectName) { return (0, import_path11.resolve)( require.resolve("dt-app", { paths: [(0, import_path11.join)(root, projectName)] }), "../.." ); } // src/core/modules/config/global-config-module.ts function createGlobalConfigModule() { let config = null; return { /** * Initialize the config module: validate inputs, merge options, run post-merge * enrichment, and freeze the result. * Can only be called once. Subsequent calls will throw an error. * After calling, retrieve the config via {@link getConfig}. * @throws Error if already initialized */ async init(argsConfig, fileConfig, envConfig = process.env, validationLevel = "default", isDev = false, beforeConfigFreeze) { if (config) { throw new Error( "Config module has already been initialized. Cannot initialize twice." ); } printPathConfigurationsWarnings( fileConfig, argsConfig ); validateLegacyConfigOptions(fileConfig); const root = argsConfig.root ?? process.cwd(); const environmentUrl = argsConfig.environmentUrl ?? envConfig.DT_APP_ENVIRONMENT_URL?.trim() ?? fileConfig.environmentUrl; await validateConfig( getAppConfigSchemaPath(root), { ...fileConfig, environmentUrl }, validationLevel ); let options = mergeOptions(argsConfig, fileConfig, envConfig, isDev); applyUiPathConventions(options); await validateAndPruneActions(options); await validateSelfMonAgent(options); pluginProvider.registerAll( requirePlugins({ root: options.root, plugins: options.plugins }) ); if (beforeConfigFreeze) { options = await beforeConfigFreeze(options); } config = deepFreeze(options); }, /** * Get the stored config. * @returns The immutable config object * @throws Error if not yet initialized */ getConfig() { if (!config) { throw new Error( "Config module has not been initialized. Call init() first." ); } return config; }, /** * Check if the config module has been initialized.