diff --git a/.env b/.env deleted file mode 100644 index 277077fed..000000000 --- a/.env +++ /dev/null @@ -1 +0,0 @@ -CRYOSTAT_AUTHORITY=http://localhost:8181 diff --git a/.env.prod b/.env.prod deleted file mode 100644 index 38db24124..000000000 --- a/.env.prod +++ /dev/null @@ -1 +0,0 @@ -CRYOSTAT_AUTHORITY= diff --git a/package.json b/package.json index 5f20f5c80..be2ae9c55 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,10 @@ "scripts": { "build": "npm-run-all -l build:notests test", "build:notests": "webpack --config webpack.prod.js", + "build:preview:notests": "PREVIEW=true webpack --config webpack.prod.js", "clean": "rimraf dist", "start:dev": "webpack serve --hot --color --progress --config webpack.dev.js", + "start:dev:preview": "PREVIEW=true PORT=9091 webpack serve --hot --color --progress --config webpack.dev.js", "start:dev:lint": "ESLINT_ENABLE=true webpack serve --hot --color --progress --config webpack.dev.js", "test": "jest --maxWorkers=50% --coverage=true", "test:ci": "jest --maxWorkers=50%", @@ -45,7 +47,6 @@ "@typescript-eslint/parser": "^5.53.0", "css-loader": "^6.7.3", "css-minimizer-webpack-plugin": "^4.2.2", - "dotenv-webpack": "^8.0.1", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.7", "enzyme-to-json": "^3.6.2", @@ -64,6 +65,8 @@ "license-check-and-add": "^4.0.5", "lodash": "^4.17.21", "mini-css-extract-plugin": "^2.7.1", + "miragejs": "^0.1.47", + "mock-socket": "^9.2.1", "nanoid": "^3.3.4", "npm-run-all": "^4.1.5", "prettier": "^2.8.4", @@ -77,7 +80,7 @@ "rxjs": "^7.8.0", "style-loader": "^3.3.1", "svg-url-loader": "^8.0.0", - "terser-webpack-plugin": "^5.3.3", + "terser-webpack-plugin": "^5.3.9", "ts-jest": "^27.0.5", "ts-loader": "^9.4.2", "tsconfig-paths-webpack-plugin": "^4.0.0", diff --git a/src/mirage/factories.ts b/src/mirage/factories.ts new file mode 100644 index 000000000..a4c417b01 --- /dev/null +++ b/src/mirage/factories.ts @@ -0,0 +1,56 @@ +/* + * Copyright The Cryostat Authors + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import { Factory } from 'miragejs'; +import { FactoryDefinition } from 'miragejs/-types'; +import { Resource } from './typings'; + +export const targetFactory: FactoryDefinition = Factory.extend({ + alias: 'Fake Target', + connectUrl: 'http://fake-target.local:1234', + jvmId: '1234', + annotations: { + platform: { 'io.cryostat.demo': 'this-is-not-real' }, + cryostat: { hello: 'world' }, + }, +}); + +export const factories = { + [Resource.TARGET]: targetFactory, +}; + +export default factories; diff --git a/src/mirage/index.ts b/src/mirage/index.ts new file mode 100644 index 000000000..bd905c52f --- /dev/null +++ b/src/mirage/index.ts @@ -0,0 +1,646 @@ +/* + * Copyright The Cryostat Authors + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import build from '@app/build.json'; +import { createServer, Response } from 'miragejs'; +import { Server as WSServer, Client } from 'mock-socket'; +import factories from './factories'; +import models from './models'; +import { Resource } from './typings'; + +export const startMirage = ({ environment = 'development' } = {}) => { + const wsUrl = `ws://cryostat.local.preview:8181`; + const wsServer = new WSServer(wsUrl); + + // Create a mock server socket to send notifications + let websocket: Client; + wsServer.on('connection', (socket) => { + websocket = socket; + socket.on('message', (_) => { + socket.send( + JSON.stringify({ + meta: { + category: 'WsClientActivity', + type: { + type: 'application', + subtype: 'json', + }, + serverTime: +Date.now(), + }, + message: { + '127.0.0.1': 'accepted', + }, + }) + ); + }); + }); + + // Create a MirageJS Server to intercept network requests + return createServer({ + environment, + models, + factories, + + seeds(server) { + server.create(Resource.TARGET); + }, + + routes() { + this.timing = 0; + this.urlPrefix = process.env.CRYOSTAT_AUTHORITY || ''; + this.namespace = '/'; + this.logging = environment === 'development'; + + this.get('health', () => ({ + cryostatVersion: `${build.version.replace(/(-\w+)*$/g, '')}-0-preview`, + dashboardAvailable: false, + dashboardConfigured: false, + datasourceAvailable: false, + datasourceConfigured: false, + reportsAvailable: true, + reportsConfigured: false, + })); + this.get('api/v1/grafana_datasource_url', () => new Response(500)); + this.get('api/v1/grafana_dashboard_url', () => new Response(500)); + this.get('api/v1/notifications_url', () => ({ + notificationsUrl: wsUrl, + })); + this.post('api/v2.1/auth', () => { + return new Response( + 200, + { 'X-WWW-Authenticate': 'None' }, + { + meta: { + status: 'OK', + }, + data: { + result: { + username: environment, + }, + }, + } + ); + }); + this.post( + 'api/v2.1/auth/token', + () => new Response(400, {}, 'Resource downloads are not supported in this demo') + ); + this.post('api/v2/targets', (schema, request) => { + const attrs = JSON.parse(request.requestBody); + const target = schema.create(Resource.TARGET, { + jvmId: `${Math.floor(1000 * Math.random())}`, + alias: attrs.get('alias'), + connectUrl: attrs.get('connectUrl'), + annotations: { + platform: {}, + cryostat: {}, + }, + }); + websocket.send( + JSON.stringify({ + meta: { + category: 'TargetJvmDiscovery', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { event: { serviceRef: target, kind: 'FOUND' } }, + }) + ); + return { + data: { + result: target, + }, + }; + }); + this.get('api/v1/targets', (schema) => schema.all(Resource.TARGET).models); + this.get('api/v2.1/discovery', (schema) => ({ + meta: { + status: 'OK', + type: 'application/json', + }, + data: { + result: { + name: 'Universe', + nodeType: 'Universe', + labels: {}, + children: [ + { + name: 'KubernetesApi', + nodeType: 'Realm', + labels: {}, + children: schema.all(Resource.TARGET).models.map((t) => ({ + name: t.alias, + nodeType: 'JVM', + target: t, + })), + }, + ], + }, + }, + })); + this.get('api/v1/recordings', (schema) => schema.all(Resource.ARCHIVE).models); + this.get('api/beta/fs/recordings', (schema) => { + const target = schema.first(Resource.TARGET); + const archives = schema.all(Resource.ARCHIVE).models; + return target + ? [ + { + connectUrl: target.attrs.connectUrl, + jvmId: target.attrs.jvmId, + recordings: archives, + }, + ] + : []; + }); + this.delete('api/beta/recordings/:targetId/:recordingName', (schema, request) => { + const recordingName = request.params.recordingName; + const recording = schema.where(Resource.ARCHIVE, { name: recordingName }); + schema.findBy(Resource.ARCHIVE, { name: recordingName })?.destroy(); + const msg = { + meta: { + category: 'ArchivedRecordingDeleted', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + recording: { + ...recording.models[0].attrs, + }, + target: request.params['targetId'], + }, + }; + websocket.send(JSON.stringify(msg)); + return new Response(200); + }); + this.post('api/v1/targets/:targetId/recordings', (schema, request) => { + const attrs = JSON.parse(request.requestBody); + const recording = schema.create(Resource.RECORDING, { + // id will generated by Mirage (i.e. increment intergers) + downloadUrl: '', + reportUrl: `beta/reports/${encodeURIComponent(request.params.targetId)}/${encodeURIComponent( + attrs.get('recordingName') + )}`, + name: attrs.get('recordingName'), + state: 'RUNNING', + startTime: +Date.now(), + duration: attrs.get('duration') * 1000 || 0, + continuous: attrs.get('duration') == 0, + toDisk: attrs.get('toDisk') || false, + maxSize: attrs.get('maxSize') || 0, + maxAge: attrs.get('maxAge') || 0, + metadata: { + labels: { + ...(attrs.labels || {}), + 'template.type': 'TARGET', + 'template.name': 'Demo Template', + }, + }, + }); + websocket.send( + JSON.stringify({ + meta: { + category: 'ActiveRecordingCreated', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + target: request.params.targetId, + recording, + }, + }) + ); + return recording; + }); + this.get('api/v1/targets/:targetId/recordings', (schema) => schema.all(Resource.RECORDING).models); + this.delete('api/v1/targets/:targetId/recordings/:recordingName', (schema, request) => { + const recordingName = request.params.recordingName; + const recording = schema.where(Resource.RECORDING, { name: recordingName }); + schema.findBy(Resource.RECORDING, { name: recordingName })?.destroy(); + const msg = { + meta: { + category: 'ActiveRecordingDeleted', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + recording: { + ...recording.models[0].attrs, + }, + target: request.params.targetId, + }, + }; + websocket.send(JSON.stringify(msg)); + return new Response(200); + }); + this.patch('api/v1/targets/:targetId/recordings/:recordingName', (schema, request) => { + const body = JSON.parse(request.requestBody); + const recordingName = request.params.recordingName; + const target = schema.findBy(Resource.TARGET, { connectUrl: request.params.targetId }); + const recording = schema.findBy(Resource.RECORDING, { name: recordingName }); + + if (!recording || !target) { + return new Response(404); + } + let msg = {}; + switch (body) { + case 'STOP': { + recording.update({ state: 'STOPPED' }); + msg = { + meta: { + category: 'ActiveRecordingStopped', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + recording: { + ...recording.attrs, + }, + target: request.params.targetId, + }, + }; + websocket.send(JSON.stringify(msg)); + break; + } + case 'SAVE': { + const ts = +Date.now(); + const archived = schema.create(Resource.ARCHIVE, { + name: `${target.alias}-${recording.name}-${ts}`, + downloadUrl: recording.downloadUrl, + reportUrl: recording.reportUrl, + metadata: recording.metadata, + size: Math.ceil(Math.random() * 1000000), + archivedTime: ts, + }); + msg = { + meta: { + category: 'ActiveRecordingSaved', + type: { type: 'application', subType: 'json' }, + serverTime: ts, + }, + message: { + recording: archived, + target: request.params.targetId, + }, + }; + websocket.send(JSON.stringify(msg)); + break; + } + } + return new Response(200); + }); + this.get('api/beta/reports/:targetId/:recordingName', () => { + return new Response( + 200, + {}, + { + Demo: { + score: 100, + name: 'Fake Demo Result', + topic: 'demo', + description: 'Remember, all of this data is static, fake, and entirely within your browser.', + }, + VMOperations: { + score: 0.5943414499999999, + name: 'VMOperation Peak Duration', + topic: 'vm_operations', + description: + 'Summary:\nNo excessively long VM operations were found in this recording (the longest was 23.774 ms).', + }, + PasswordsInSystemProperties: { + score: 75.0, + name: 'Passwords in System Properties', + topic: 'system_properties', + description: + 'Summary:\nThe system properties in the recording may contain passwords.\n\nExplanation:\nThe following suspicious system properties were found in this recording: javax.net.ssl.keyStorePassword,javax.net.ssl.trustStorePassword,com.sun.management.jmxremote.password.file. The following regular expression was used to exclude strings from this rule: \u0027\u0027(passworld|passwise)\u0027\u0027.\n\nSolution:\nIf you wish to keep having passwords in your system properties, but want to be able to share recordings without also sharing the passwords, please disable the \u0027\u0027Initial System Property\u0027\u0027 event.', + }, + Options: { + score: 0.0, + name: 'Command Line Options Check', + topic: 'jvm_information', + description: 'Summary:\nNo undocumented, deprecated or non-recommended option flags were detected.', + }, + PasswordsInEnvironment: { + score: 75.0, + name: 'Passwords in Environment Variables', + topic: 'environment_variables', + description: + 'Summary:\nThe environment variables in the recording may contain passwords.\n\nExplanation:\nThe following suspicious environment variables were found in this recording: CRYOSTAT_JDBC_PASSWORD, CRYOSTAT_JMX_CREDENTIALS_DB_PASSWORD. The following regular expression was used to exclude strings from this rule: \u0027\u0027(passworld|passwise)\u0027\u0027.\n\nSolution:\nIf you wish to keep having passwords in your environment variables, but want to be able to share recordings without also sharing the passwords, please disable the \u0027\u0027Initial Environment Variable\u0027\u0027 event.', + }, + MethodProfiling: { + score: 0.6705776661956153, + name: 'Method Profiling', + topic: 'method_profiling', + description: 'Summary:\nNo methods where optimization would be particularly efficient could be detected.', + }, + ManyRunningProcesses: { + score: 0.20309488837692125, + name: 'Competing Processes', + topic: 'processes', + description: + 'Summary:\n1 processes were running while this Flight Recording was made.\n\nExplanation:\nAt 5/5/23, 5:17:27.180 PM, a total of 1 other processes were running on the host machine that this Flight Recording was made on.\n\nSolution:\nIf this is a server environment, it may be good to only run other critical processes on that machine.', + }, + StackdepthSetting: { + score: 25.0, + name: 'Stackdepth Setting', + topic: 'jvm_information', + description: + 'Summary:\nSome stack traces were truncated in this recording.\n\nExplanation:\nThe Flight Recorder is configured with a maximum captured stack depth of 64. 3.11 % of all traces were larger than this option, and were therefore truncated. If more detailed traces are required, increase the \u0027\u0027-XX:FlightRecorderOptions\u003dstackdepth\u003d\u003cvalue\u003e\u0027\u0027 value.\nEvents of the following types have truncated stack traces: org.openjdk.jmc.flightrecorder.rules.jdk.general.StackDepthSettingRule$StackDepthTruncationData@21e159e2,org.openjdk.jmc.flightrecorder.rules.jdk.general.StackDepthSettingRule$StackDepthTruncationData@174930bc,org.openjdk.jmc.flightrecorder.rules.jdk.general.StackDepthSettingRule$StackDepthTruncationData@4f5d6223', + }, + PasswordsInArguments: { + score: 0.0, + name: 'Passwords in Java Arguments', + topic: 'jvm_information', + description: 'Summary:\nThe recording does not seem to contain passwords in the application arguments.', + }, + } + ); + }); + this.get('api/v1/targets/:targetId/recordingOptions', () => []); + this.get('api/v1/targets/:targetId/events', () => [ + { + category: ['GC', 'Java Virtual Machine'], + name: 'GC Heap Configuration', + typeId: 'jdk.GCHeapConfiguration', + description: 'The configuration of the garbage collected heap', + }, + ]); + this.get('api/v1/targets/:targetId/templates', () => [ + { + name: 'Demo Template', + provider: 'Demo', + type: 'TARGET', + description: 'This is not a real event template, but it is here!', + }, + ]); + this.get('api/v2/probes', () => []); + this.post('api/v2/rules', (schema, request) => { + const attrs = JSON.parse(request.requestBody); + return { + data: { + result: schema.create(Resource.RULE, attrs), + }, + }; + }); + this.get('api/v2/rules', (schema) => ({ + data: { result: schema.all(Resource.RULE).models }, + })); + this.get('api/v2.2/credentials', () => ({ data: { result: [] } })); + this.post('api/v2.2/graphql', (schema, request) => { + const body = JSON.parse(request.requestBody); + const query = body.query.trim(); + const variables = body.variables; + const begin = query.substring(0, query.indexOf('{')); + let name = 'unknown'; + for (const n of begin.split(' ')) { + if (n == '{') { + break; + } + if (!n || n == 'query') { + continue; + } + name = n.substring(0, n.indexOf('(')); + break; + } + if (name === 'unknown' || !name) { + return new Response( + 400, + {}, + `${JSON.stringify(request.url)} (query: '${name}') currently unsupported in demo` + ); + } + let data = {}; + switch (name) { + case 'ArchivedRecordingsForTarget': + case 'UploadedRecordings': + data = { + archivedRecordings: { + data: schema.all(Resource.ARCHIVE).models, + }, + }; + break; + case 'ActiveRecordingsForTarget': + data = { + targetNodes: [ + { + recordings: { + archived: { + data: schema.all(Resource.ARCHIVE).models, + }, + }, + }, + ], + }; + break; + case 'ArchivedRecordingsForAutomatedAnalysis': + data = { + archivedRecordings: { + data: schema.all(Resource.ARCHIVE).models, + }, + }; + break; + case 'ActiveRecordingsForAutomatedAnalysis': + data = { + targetNodes: [ + { + recordings: { + active: { + data: schema.all(Resource.RECORDING).models, + }, + }, + }, + ], + }; + break; + case 'PostRecordingMetadata': { + const labels = {}; + for (const l of eval(variables.labels)) { + labels[l.key] = l.value; + } + schema.findBy(Resource.ARCHIVE, { name: variables.recordingName })?.update({ + metadata: { + labels, + }, + }); + data = { + targetNodes: [ + { + recordings: { + archived: { + data: [ + { + doPutMetadata: { + metadata: { + labels, + }, + }, + }, + ], + }, + }, + }, + ], + }; + websocket.send( + JSON.stringify({ + meta: { + category: 'RecordingMetadataUpdated', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + recordingName: variables.recordingName, + target: variables.connectUrl, + metadata: { + labels, + }, + }, + }) + ); + break; + } + case 'PostActiveRecordingMetadata': { + const labels = {}; + for (const l of eval(variables.labels)) { + labels[l.key] = l.value; + } + schema.findBy(Resource.RECORDING, { name: variables.recordingName })?.update({ + metadata: { + labels, + }, + }); + data = { + targetNodes: [ + { + recordings: { + active: { + data: [ + { + doPutMetadata: { + metadata: { + labels, + }, + }, + }, + ], + }, + }, + }, + ], + }; + websocket.send( + JSON.stringify({ + meta: { + category: 'RecordingMetadataUpdated', + type: { type: 'application', subType: 'json' }, + serverTime: +Date.now(), + }, + message: { + recordingName: variables.recordingName, + target: variables.connectUrl, + metadata: { + labels, + }, + }, + }) + ); + break; + } + case 'MBeanMXMetricsForTarget': + data = { + targetNodes: [ + { + mbeanMetrics: { + thread: { + threadCount: Math.ceil(Math.random() * 5), + daemonThreadCount: Math.ceil(Math.random() * 5), + }, + os: { + arch: 'x86_64', + availableProcessors: Math.ceil(Math.random() * 8), + version: '10.0.1', + systemCpuLoad: Math.random(), + systemLoadAverage: Math.random(), + processCpuLoad: Math.random(), + totalPhysicalMemorySize: Math.ceil(Math.random() * 64), + freePhysicalMemorySize: Math.ceil(Math.random() * 64), + }, + memory: { + heapMemoryUsage: { + init: Math.ceil(Math.random() * 64), + used: Math.ceil(Math.random() * 64), + committed: Math.ceil(Math.random() * 64), + max: Math.ceil(Math.random() * 64), + }, + nonHeapMemoryUsage: { + init: Math.ceil(Math.random() * 64), + used: Math.ceil(Math.random() * 64), + committed: Math.ceil(Math.random() * 64), + max: Math.ceil(Math.random() * 64), + }, + heapMemoryUsagePercent: Math.random(), + }, + runtime: { + bootClassPath: '/path/to/boot/classpath', + classPath: '/path/to/classpath', + inputArguments: ['-Xmx1g', '-Djava.security.policy=...'], + libraryPath: '/path/to/library/path', + managementSpecVersion: '1.0', + name: 'Java Virtual Machine', + specName: 'Java Virtual Machine Specification', + specVendor: 'Oracle Corporation', + startTime: Date.now(), + // systemProperties: {...} + uptime: Date.now(), + vmName: 'Java HotSpot(TM) 64-Bit Server VM', + vmVendor: 'Oracle Corporation', + vmVersion: '25.131-b11', + bootClassPathSupported: true, + }, + }, + }, + ], + }; + break; + } + return { data }; + }); + }, + }); +}; + +startMirage({ environment: process.env.NODE_ENV }); diff --git a/src/mirage/models.ts b/src/mirage/models.ts new file mode 100644 index 000000000..855dac995 --- /dev/null +++ b/src/mirage/models.ts @@ -0,0 +1,54 @@ +/* + * Copyright The Cryostat Authors + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +import { Model } from 'miragejs'; +import { ModelDefinition } from 'miragejs/-types'; +import { Resource } from './typings'; + +const TargetModel: ModelDefinition = Model.extend({}); +const RecordingModel: ModelDefinition = Model.extend({}); +const ArchiveModel: ModelDefinition = Model.extend({}); +const RuleModel: ModelDefinition = Model.extend({}); + +export const models = { + [Resource.TARGET]: TargetModel, + [Resource.RECORDING]: RecordingModel, + [Resource.ARCHIVE]: ArchiveModel, + [Resource.RULE]: RuleModel, +}; + +export default models; diff --git a/src/mirage/typings.ts b/src/mirage/typings.ts new file mode 100644 index 000000000..73ff7ab79 --- /dev/null +++ b/src/mirage/typings.ts @@ -0,0 +1,43 @@ +/* + * Copyright The Cryostat Authors + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or data + * (collectively the "Software"), free of charge and under any and all copyright + * rights in the Software, and any and all patent rights owned or freely + * licensable by each licensor hereunder covering either (i) the unmodified + * Software as contributed to or provided by such licensor, or (ii) the Larger + * Works (as defined below), to deal in both + * + * (a) the Software, and + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software (each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * The above copyright notice and either this complete permission notice or at + * a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +export enum Resource { + TARGET = 'target', + RECORDING = 'recording', + ARCHIVE = 'archive', + RULE = 'rule', +} diff --git a/webpack.common.js b/webpack.common.js index 672f47fad..37d95ac2c 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -9,7 +9,11 @@ module.exports = (env) => { return { context: __dirname, entry: { - app: path.resolve(__dirname, 'src', 'index.tsx') + app: { + import: path.resolve(__dirname, 'src', 'index.tsx'), + dependOn: process.env.PREVIEW? 'mirage': undefined + }, + ...(process.env.PREVIEW? {mirage: path.resolve(__dirname, 'src', 'mirage', 'index.ts')}: {}) }, plugins: [ new HtmlWebpackPlugin({ diff --git a/webpack.dev.js b/webpack.dev.js index 28f970d78..988571c02 100644 --- a/webpack.dev.js +++ b/webpack.dev.js @@ -1,8 +1,8 @@ const path = require('path'); const { merge } = require('webpack-merge'); const common = require('./webpack.common.js'); -const DotenvPlugin = require('dotenv-webpack'); const ESLintPlugin = require('eslint-webpack-plugin'); +const { EnvironmentPlugin } = require('webpack'); const HOST = process.env.HOST || "localhost"; const PORT = process.env.PORT || "9000"; @@ -19,7 +19,10 @@ module.exports = merge(common('development'), { port: PORT, }, plugins: [ - new DotenvPlugin(), + new EnvironmentPlugin({ + CRYOSTAT_AUTHORITY: 'http://localhost:8181', + PREVIEW: process.env.PREVIEW || 'false' + }) ], module: { rules: [ diff --git a/webpack.prod.js b/webpack.prod.js index b7fc47a3a..eb79f1229 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -4,11 +4,10 @@ const common = require('./webpack.common.js'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const TerserJSPlugin = require('terser-webpack-plugin'); -const DotenvPlugin = require('dotenv-webpack'); +const { EnvironmentPlugin } = require('webpack'); module.exports = merge(common('production'), { mode: 'production', - devtool: 'eval-source-map', cache: { type: 'filesystem', compression: 'gzip', @@ -16,10 +15,23 @@ module.exports = merge(common('production'), { }, optimization: { minimizer: [ - new TerserJSPlugin({}), + new TerserJSPlugin({ + terserOptions: { + format: { + comments: false, + }, + }, + extractComments: false, + }), new CssMinimizerPlugin({ minimizerOptions: { - preset: ['default', { mergeLonghand: false }] + preset: [ + 'default', + { + mergeLonghand: false, + discardComments: { removeAll: true } + } + ] }, }), ], @@ -29,9 +41,10 @@ module.exports = merge(common('production'), { filename: '[name].[contenthash].bundle.css', chunkFilename: '[name].[contenthash].bundle.css' // lazy-load css }), - new DotenvPlugin({ - path: './.env.prod', - }), + new EnvironmentPlugin({ + CRYOSTAT_AUTHORITY: '', + PREVIEW: process.env.PREVIEW || 'false' + }) ], module: { rules: [ diff --git a/yarn.lock b/yarn.lock index cfa42b88f..a5fa86537 100644 --- a/yarn.lock +++ b/yarn.lock @@ -957,6 +957,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:^0.3.17": + version: 0.3.18 + resolution: "@jridgewell/trace-mapping@npm:0.3.18" + dependencies: + "@jridgewell/resolve-uri": 3.1.0 + "@jridgewell/sourcemap-codec": 1.4.14 + checksum: 0572669f855260808c16fe8f78f5f1b4356463b11d3f2c7c0b5580c8ba1cbf4ae53efe9f627595830856e57dbac2325ac17eb0c3dd0ec42102e6f227cc289c02 + languageName: node + linkType: hard + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.4 resolution: "@leichtgewicht/ip-codec@npm:2.0.4" @@ -964,6 +974,13 @@ __metadata: languageName: node linkType: hard +"@miragejs/pretender-node-polyfill@npm:^0.1.0": + version: 0.1.2 + resolution: "@miragejs/pretender-node-polyfill@npm:0.1.2" + checksum: bb55983a253dc0d4162acac090b062500595c9844bfbd21fe25bc1f926808d0070d710e622aa6285fa16c673a65cf3e05f4b32e888c469f1bde8888a7934ee44 + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -3941,7 +3958,6 @@ __metadata: css-loader: ^6.7.3 css-minimizer-webpack-plugin: ^4.2.2 dayjs: ^1.11.7 - dotenv-webpack: ^8.0.1 enzyme: ^3.11.0 enzyme-adapter-react-16: ^1.15.7 enzyme-to-json: ^3.6.2 @@ -3963,6 +3979,8 @@ __metadata: license-check-and-add: ^4.0.5 lodash: ^4.17.21 mini-css-extract-plugin: ^2.7.1 + miragejs: ^0.1.47 + mock-socket: ^9.2.1 nanoid: ^3.3.4 npm-run-all: ^4.1.5 prettier: ^2.8.4 @@ -3983,7 +4001,7 @@ __metadata: showdown: ^2.1.0 style-loader: ^3.3.1 svg-url-loader: ^8.0.0 - terser-webpack-plugin: ^5.3.3 + terser-webpack-plugin: ^5.3.9 ts-jest: ^27.0.5 ts-loader: ^9.4.2 tsconfig-paths-webpack-plugin: ^4.0.0 @@ -5030,33 +5048,6 @@ __metadata: languageName: node linkType: hard -"dotenv-defaults@npm:^2.0.2": - version: 2.0.2 - resolution: "dotenv-defaults@npm:2.0.2" - dependencies: - dotenv: ^8.2.0 - checksum: c005960bd048e2189c4799e729c41f362e415e6e0b54313d3f31e853a84b049bf770b25cb21c112c2805bb13a8376edc9de451fb514abf8546392d327c27f6e5 - languageName: node - linkType: hard - -"dotenv-webpack@npm:^8.0.1": - version: 8.0.1 - resolution: "dotenv-webpack@npm:8.0.1" - dependencies: - dotenv-defaults: ^2.0.2 - peerDependencies: - webpack: ^4 || ^5 - checksum: ee73eda78df90bcf7446763dfe5e518a2054ed6222783856912d2c6a255fdfc041854e125e036ff2603b06fb44f5234713fad5feecb1e66a54fe78a35a98fb87 - languageName: node - linkType: hard - -"dotenv@npm:^8.2.0": - version: 8.6.0 - resolution: "dotenv@npm:8.6.0" - checksum: 38e902c80b0666ab59e9310a3d24ed237029a7ce34d976796349765ac96b8d769f6df19090f1f471b77a25ca391971efde8a1ea63bb83111bd8bec8e5cc9b2cd - languageName: node - linkType: hard - "duplexer@npm:^0.1.2": version: 0.1.2 resolution: "duplexer@npm:0.1.2" @@ -5991,6 +5982,13 @@ __metadata: languageName: node linkType: hard +"fake-xml-http-request@npm:^2.1.2": + version: 2.1.2 + resolution: "fake-xml-http-request@npm:2.1.2" + checksum: 7b1ebea1955c46cbda0e0c3308716cc82beca7bb6c549f1d1c2a8134203c9d6c5a21194e2c1e17650ff073ecbcd3e56cf1e21f248a86cbcb3567fd7a9ca4850e + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -7244,6 +7242,13 @@ __metadata: languageName: node linkType: hard +"inflected@npm:^2.0.4": + version: 2.1.0 + resolution: "inflected@npm:2.1.0" + checksum: 1e3fb2d24ec6c774977dd5ed884c6097b6525f66edecfdca0845ab4023d35f02af1057de50cd69cdc5829d0bd5ba69fd7fb41d4d8abdf85ee243bf2bbf65995d + languageName: node + linkType: hard + "inflight@npm:^1.0.4": version: 1.0.6 resolution: "inflight@npm:1.0.6" @@ -8760,6 +8765,34 @@ __metadata: languageName: node linkType: hard +"lodash.assign@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.assign@npm:4.2.0" + checksum: 75bbc6733c9f577c448031b4051f990f068802708891f94be9d4c2faffd6a9ec67a2c49671dafc908a068d35687765464853282842b4560b662e6c903d11cc90 + languageName: node + linkType: hard + +"lodash.camelcase@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.camelcase@npm:4.3.0" + checksum: cb9227612f71b83e42de93eccf1232feeb25e705bdb19ba26c04f91e885bfd3dd5c517c4a97137658190581d3493ea3973072ca010aab7e301046d90740393d1 + languageName: node + linkType: hard + +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 + languageName: node + linkType: hard + +"lodash.compact@npm:^3.0.1": + version: 3.0.1 + resolution: "lodash.compact@npm:3.0.1" + checksum: 75039eddfa5ef2ea0da1fc3d36515e92227241f94258b3dcf771196e741c878698ce5b79c0cb7fe758841c9dfd0e6fa222888985aadc0384fd79bbc9680dd829 + languageName: node + linkType: hard + "lodash.escape@npm:^4.0.1": version: 4.0.1 resolution: "lodash.escape@npm:4.0.1" @@ -8767,6 +8800,20 @@ __metadata: languageName: node linkType: hard +"lodash.find@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.find@npm:4.6.0" + checksum: b737f849a4fe36f5c3664ea636780dda2fde18335021faf80cdfdcb300ed75441da6f55cfd6de119092d8bb2ddbc4433f4a8de4b99c0b9c8640465b0901c717c + languageName: node + linkType: hard + +"lodash.flatten@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flatten@npm:4.4.0" + checksum: 0ac34a393d4b795d4b7421153d27c13ae67e08786c9cbb60ff5b732210d46f833598eee3fb3844bb10070e8488efe390ea53bb567377e0cb47e9e630bf0811cb + languageName: node + linkType: hard + "lodash.flattendeep@npm:^4.4.0": version: 4.4.0 resolution: "lodash.flattendeep@npm:4.4.0" @@ -8774,6 +8821,41 @@ __metadata: languageName: node linkType: hard +"lodash.forin@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.forin@npm:4.4.0" + checksum: c9c8e6c3204029fe0610efb8c74113b00a3d5a92b1d8defbf3f5cbc2d0f7663c680dff62e2950ed9406bb6d06e882fa39568c607bc44229d53fd93a9fd6c07b7 + languageName: node + linkType: hard + +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: e403047ddb03181c9d0e92df9556570e2b67e0f0a930fcbbbd779370972368f5568e914f913e93f3b08f6d492abc71e14d4e9b7a18916c31fa04bd2306efe545 + languageName: node + linkType: hard + +"lodash.has@npm:^4.5.2": + version: 4.5.2 + resolution: "lodash.has@npm:4.5.2" + checksum: b3ec829a86852331d48b3730ff06088a283d128a3965aa521ffd942bcf5c82e06bed3164ff7a7751d11e768d88f0d7bab316192091489caf20f452d42f7055d5 + languageName: node + linkType: hard + +"lodash.invokemap@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.invokemap@npm:4.6.0" + checksum: 646ceebbefbcb6da301f8c2868254680fd0bcdc6ada470495d9ae49c9c32938829c1b38a38c95d0258409a9655f85db404b16e648381c7450b7ed3d9c52d8808 + languageName: node + linkType: hard + +"lodash.isempty@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.isempty@npm:4.4.0" + checksum: a8118f23f7ed72a1dbd176bf27f297d1e71aa1926288449cb8f7cef99ba1bc7527eab52fe7899ab080fa1dc150aba6e4a6367bf49fa4e0b78da1ecc095f8d8c5 + languageName: node + linkType: hard + "lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" @@ -8781,6 +8863,48 @@ __metadata: languageName: node linkType: hard +"lodash.isfunction@npm:^3.0.9": + version: 3.0.9 + resolution: "lodash.isfunction@npm:3.0.9" + checksum: 99e54c34b1e8a9ba75c034deb39cedbd2aca7af685815e67a2a8ec4f73ec9748cda6ebee5a07d7de4b938e90d421fd280e9c385cc190f903ac217ac8aff30314 + languageName: node + linkType: hard + +"lodash.isinteger@npm:^4.0.4": + version: 4.0.4 + resolution: "lodash.isinteger@npm:4.0.4" + checksum: 6034821b3fc61a2ffc34e7d5644bb50c5fd8f1c0121c554c21ac271911ee0c0502274852845005f8651d51e199ee2e0cfebfe40aaa49c7fe617f603a8a0b1691 + languageName: node + linkType: hard + +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 + languageName: node + linkType: hard + +"lodash.lowerfirst@npm:^4.3.1": + version: 4.3.1 + resolution: "lodash.lowerfirst@npm:4.3.1" + checksum: e1688e18873777d394db4994d150dfc14cf01bf450169cf8296af4d84ecd7c3c4ae4dab3746f77f8719a093e4fff58bee3ae73ae7e23ef508b7d970b189d9952 + languageName: node + linkType: hard + +"lodash.map@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.map@npm:4.6.0" + checksum: 7369a41d7d24d15ce3bbd02a7faa3a90f6266c38184e64932571b9b21b758bd10c04ffd117d1859be1a44156f29b94df5045eff172bf8a97fddf68bf1002d12f + languageName: node + linkType: hard + +"lodash.mapvalues@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.mapvalues@npm:4.6.0" + checksum: 0ff1b252fda318fc36e47c296984925e98fbb0fc5a2ecc4ef458f3c739a9476d47e40c95ac653e8314d132aa59c746d4276527b99d6e271940555c6e12d2babd + languageName: node + linkType: hard + "lodash.memoize@npm:4.x, lodash.memoize@npm:^4.1.2": version: 4.1.2 resolution: "lodash.memoize@npm:4.1.2" @@ -8795,6 +8919,20 @@ __metadata: languageName: node linkType: hard +"lodash.pick@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.pick@npm:4.4.0" + checksum: 2c36cab7da6b999a20bd3373b40e31a3ef81fa264f34a6979c852c5bc8ac039379686b27380f0cb8e3781610844fafec6949c6fbbebc059c98f8fa8570e3675f + languageName: node + linkType: hard + +"lodash.snakecase@npm:^4.1.1": + version: 4.1.1 + resolution: "lodash.snakecase@npm:4.1.1" + checksum: 1685ed3e83dda6eae5a4dcaee161a51cd210aabb3e1c09c57150e7dd8feda19e4ca0d27d0631eabe8d0f4eaa51e376da64e8c018ae5415417c5890d42feb72a8 + languageName: node + linkType: hard + "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -8802,6 +8940,20 @@ __metadata: languageName: node linkType: hard +"lodash.uniqby@npm:^4.7.0": + version: 4.7.0 + resolution: "lodash.uniqby@npm:4.7.0" + checksum: 659264545a95726d1493123345aad8cbf56e17810fa9a0b029852c6d42bc80517696af09d99b23bef1845d10d95e01b8b4a1da578f22aeba7a30d3e0022a4938 + languageName: node + linkType: hard + +"lodash.values@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.values@npm:4.3.0" + checksum: 857e122ddf6edb50137887f0b834d471f96d66692ebb5de8b048ed277c8a3dd1bc8f85d82cf31b0f5c6dbc5b72c036a591205f76eec86bb11818a1a6f7e5a28c + languageName: node + linkType: hard + "lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -9194,6 +9346,40 @@ __metadata: languageName: node linkType: hard +"miragejs@npm:^0.1.47": + version: 0.1.47 + resolution: "miragejs@npm:0.1.47" + dependencies: + "@miragejs/pretender-node-polyfill": ^0.1.0 + inflected: ^2.0.4 + lodash.assign: ^4.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.compact: ^3.0.1 + lodash.find: ^4.6.0 + lodash.flatten: ^4.4.0 + lodash.forin: ^4.4.0 + lodash.get: ^4.4.2 + lodash.has: ^4.5.2 + lodash.invokemap: ^4.6.0 + lodash.isempty: ^4.4.0 + lodash.isequal: ^4.5.0 + lodash.isfunction: ^3.0.9 + lodash.isinteger: ^4.0.4 + lodash.isplainobject: ^4.0.6 + lodash.lowerfirst: ^4.3.1 + lodash.map: ^4.6.0 + lodash.mapvalues: ^4.6.0 + lodash.pick: ^4.4.0 + lodash.snakecase: ^4.1.1 + lodash.uniq: ^4.5.0 + lodash.uniqby: ^4.7.0 + lodash.values: ^4.3.0 + pretender: ^3.4.7 + checksum: b50cd640c07bdd2a05df20c5113685818c3c6c63a00af2b9ac59f7ba3bd1da4e2fbe5a9cf11ca7c635c4b2e66d5585704f1dae2d48d06096b9d58d0c57ebf087 + languageName: node + linkType: hard + "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -9244,6 +9430,13 @@ __metadata: languageName: node linkType: hard +"mock-socket@npm:^9.2.1": + version: 9.2.1 + resolution: "mock-socket@npm:9.2.1" + checksum: daf07689563163dbcefbefe23b2a9784a75d0af31706f23ad535c6ab2abbcdefa2e91acddeb50a3c39009139e47a8f909cbb38e8137452193ccb9331637fee3e + languageName: node + linkType: hard + "moo@npm:^0.5.0": version: 0.5.2 resolution: "moo@npm:0.5.2" @@ -10488,6 +10681,16 @@ __metadata: languageName: node linkType: hard +"pretender@npm:^3.4.7": + version: 3.4.7 + resolution: "pretender@npm:3.4.7" + dependencies: + fake-xml-http-request: ^2.1.2 + route-recognizer: ^0.3.3 + checksum: ca767dfa3fdb5e49cec8f272de1c7d489f53986a9f58dd0caf379b6365b1f1241bbb03bea6fe4421ce3e2aaa894d6c7349a5ef603283710be2babca49f1ad6fb + languageName: node + linkType: hard + "prettier@npm:^2.8.4": version: 2.8.4 resolution: "prettier@npm:2.8.4" @@ -11431,6 +11634,13 @@ __metadata: languageName: node linkType: hard +"route-recognizer@npm:^0.3.3": + version: 0.3.4 + resolution: "route-recognizer@npm:0.3.4" + checksum: 9586704491b26716eba1bdbb4a386893ecaab80c6dbf34e394957063b941ebf336ca09222f0892c9d118c609fa9b972d6e73e454abd4382acdc34dbb878e87f2 + languageName: node + linkType: hard + "rst-selector-parser@npm:^2.2.3": version: 2.2.3 resolution: "rst-selector-parser@npm:2.2.3" @@ -11644,7 +11854,7 @@ __metadata: languageName: node linkType: hard -"serialize-javascript@npm:^6.0.0": +"serialize-javascript@npm:^6.0.0, serialize-javascript@npm:^6.0.1": version: 6.0.1 resolution: "serialize-javascript@npm:6.0.1" dependencies: @@ -12415,7 +12625,7 @@ __metadata: languageName: node linkType: hard -"terser-webpack-plugin@npm:^5.1.3, terser-webpack-plugin@npm:^5.3.3": +"terser-webpack-plugin@npm:^5.1.3": version: 5.3.6 resolution: "terser-webpack-plugin@npm:5.3.6" dependencies: @@ -12437,6 +12647,28 @@ __metadata: languageName: node linkType: hard +"terser-webpack-plugin@npm:^5.3.9": + version: 5.3.9 + resolution: "terser-webpack-plugin@npm:5.3.9" + dependencies: + "@jridgewell/trace-mapping": ^0.3.17 + jest-worker: ^27.4.5 + schema-utils: ^3.1.1 + serialize-javascript: ^6.0.1 + terser: ^5.16.8 + peerDependencies: + webpack: ^5.1.0 + peerDependenciesMeta: + "@swc/core": + optional: true + esbuild: + optional: true + uglify-js: + optional: true + checksum: 41705713d6f9cb83287936b21e27c658891c78c4392159f5148b5623f0e8c48559869779619b058382a4c9758e7820ea034695e57dc7c474b4962b79f553bc5f + languageName: node + linkType: hard + "terser@npm:^5.10.0, terser@npm:^5.14.1": version: 5.16.3 resolution: "terser@npm:5.16.3" @@ -12451,6 +12683,20 @@ __metadata: languageName: node linkType: hard +"terser@npm:^5.16.8": + version: 5.17.6 + resolution: "terser@npm:5.17.6" + dependencies: + "@jridgewell/source-map": ^0.3.2 + acorn: ^8.5.0 + commander: ^2.20.0 + source-map-support: ~0.5.20 + bin: + terser: bin/terser + checksum: 9c0ab0261a99a61c5f53d05d4ecc7f68c552bae6af481464fdd596bc9d7e89ce8e21b1833cb3ce06ad5f658e2b226081d543e4fe6e324b2cdf03ee8b7eeec01a + languageName: node + linkType: hard + "test-exclude@npm:^6.0.0": version: 6.0.0 resolution: "test-exclude@npm:6.0.0"