diff --git a/.prettierignore b/.prettierignore index 0d4fd8a853..c8d16c5a01 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,6 +12,7 @@ yarn.lock /generated-docs /developer-extension/.output /developer-extension/.wxt +/test/apps/sf-lwc-app/* # Auto-generated by API Extractor — do not format /packages/js-core/api diff --git a/eslint.config.ts b/eslint.config.ts index 61566234f5..5c69a39b6e 100644 --- a/eslint.config.ts +++ b/eslint.config.ts @@ -52,6 +52,7 @@ export default defineConfig( 'test/apps/vue-router-v4-app', 'test/apps/nuxt-app', 'test/apps/nuxt-vue-router-v4-app', + 'test/apps/sf-lwc-app', 'sandbox', 'coverage', '.yarn', diff --git a/test/apps/sf-lwc-app/.gitignore b/test/apps/sf-lwc-app/.gitignore new file mode 100644 index 0000000000..a8488dce86 --- /dev/null +++ b/test/apps/sf-lwc-app/.gitignore @@ -0,0 +1,3 @@ +.sf +.sfdx +/force-app/main/default/staticresources/datadog_rum_slim.js \ No newline at end of file diff --git a/test/apps/sf-lwc-app/README.md b/test/apps/sf-lwc-app/README.md new file mode 100644 index 0000000000..dd8f3275f3 --- /dev/null +++ b/test/apps/sf-lwc-app/README.md @@ -0,0 +1,24 @@ +# SF LWC App + +Salesforce DX project used to exercise browser-sdk behavior in a real Lightning Web Components runtime. + +This app is Lightning-only. + +## What It Contains + +- A Lightning app named `SF LWC App` +- A trimmed Home page with Datadog test controls +- A `Product Explorer` app page with three hardcoded editable products +- `c:datadogInit` in the utility bar, backed by the `datadog_rum_slim` static resource + +## Deploy + +From this directory: + +```sh +yarn run setup -o engrumdev --ignore-conflicts +``` + +The setup script copies the local RUM slim bundle into the static resource, deploys the app, assigns the app permission set to the target user, and prints the app-specific Home URL. + +If prompted for a user. Log into 1Password and use `beltran.bulbarella@datadoghq.com.engrumdev` diff --git a/test/apps/sf-lwc-app/force-app/main/default/applications/SF_LWC_App.app-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/applications/SF_LWC_App.app-meta.xml new file mode 100644 index 0000000000..b98971f24f --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/applications/SF_LWC_App.app-meta.xml @@ -0,0 +1,17 @@ + + + + Tab + SF_LWC_App_Home + Large + Flexipage + standard-home + + Large + + Standard + standard-home + Product_Explorer + Lightning + SF_LWC_App_UtilityBar + diff --git a/test/apps/sf-lwc-app/force-app/main/default/cspTrustedSites/browser_intake_datadoghq_com.cspTrustedSite-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/cspTrustedSites/browser_intake_datadoghq_com.cspTrustedSite-meta.xml new file mode 100644 index 0000000000..adff07602d --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/cspTrustedSites/browser_intake_datadoghq_com.cspTrustedSite-meta.xml @@ -0,0 +1,10 @@ + + + false + false + All + Datadog browser RUM intake for US1 + https://browser-intake-datadoghq.com + true + true + diff --git a/test/apps/sf-lwc-app/force-app/main/default/flexipages/Product_Explorer.flexipage-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/flexipages/Product_Explorer.flexipage-meta.xml new file mode 100644 index 0000000000..142b4f2274 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/flexipages/Product_Explorer.flexipage-meta.xml @@ -0,0 +1,18 @@ + + + + + + productExplorer + productExplorer + + + main + Region + + Product Explorer + + AppPage + diff --git a/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_Home.flexipage-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_Home.flexipage-meta.xml new file mode 100644 index 0000000000..109a44e956 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_Home.flexipage-meta.xml @@ -0,0 +1,18 @@ + + + + + + customActionButtons + customActionButtons + + + top + Region + + SF LWC App Home + + HomePage + diff --git a/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_UtilityBar.flexipage-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_UtilityBar.flexipage-meta.xml new file mode 100644 index 0000000000..b059c88bc3 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/flexipages/SF_LWC_App_UtilityBar.flexipage-meta.xml @@ -0,0 +1,28 @@ + + + + + + + eager + decorator + true + + + label + decorator + Datadog Init + + datadogInit + datadogInit + + + utilityItems + Region + + SF LWC App UtilityBar + + UtilityBar + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.html b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.html new file mode 100644 index 0000000000..2cf2c89049 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.html @@ -0,0 +1,135 @@ + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js new file mode 100644 index 0000000000..e95c1db5bb --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js @@ -0,0 +1,123 @@ +import { LightningElement, track } from 'lwc' + +export default class CustomActionButtons extends LightningElement { + @track lastActionName + @track lastErrorName + @track lastResourceName + @track lastSelectorProbe + + handleActionClick(event) { + const actionName = event.currentTarget.dataset.actionName + this.lastActionName = actionName + + window.DD_RUM?.addAction(actionName, { + source: 'salesforce-home-button', + pathname: window.location?.pathname, + }) + } + + handleErrorClick(event) { + const errorName = event.currentTarget.dataset.errorName + this.lastErrorName = errorName + + window.DD_RUM?.addError(new Error(errorName), { + source: 'salesforce-home-button', + pathname: window.location?.pathname, + }) + } + + handleRuntimeErrorClick() { + throw new Error('salesforce direct runtime error test') + } + + handleLongTaskClick() { + window.setTimeout(() => { + const start = performance.now() + while (performance.now() - start < 750) { + /* spin */ + } + }, 0) + } + + // Vitals: report a completed duration vital directly. + handleAddDurationVitalClick() { + window.DD_RUM?.addDurationVital('salesforce.duration_vital_test', { + startTime: Date.now() - 250, + duration: 250, + description: 'salesforce duration vital test', + context: { + source: 'salesforce-home-button', + }, + }) + } + + // Vitals: measure a live 300ms window. + handleStartStopVitalClick() { + window.DD_RUM?.startDurationVital('salesforce.start_stop_vital_test', { + description: 'salesforce start-stop vital test', + context: { + source: 'salesforce-home-button', + }, + }) + + setTimeout(() => window.DD_RUM?.stopDurationVital('salesforce.start_stop_vital_test'), 300) + } + + async handleFetchResourceClick() { + const token = `dd-fetch-test-${Date.now()}` + const url = this.getResourceTestUrl(token) + this.lastResourceName = `fetch: ${token}` + + try { + const response = await window.fetch(url, { cache: 'no-store' }) + await response.text() + } catch (error) { + window.DD_RUM?.addError(error, { + source: 'salesforce-resource-button', + resourceType: 'fetch', + url, + }) + } + } + + handleXhrResourceClick() { + const token = `dd-xhr-test-${Date.now()}` + const url = this.getResourceTestUrl(token) + this.lastResourceName = `xhr: ${token}` + + const xhr = new window.XMLHttpRequest() + xhr.open('GET', url) + xhr.onerror = () => { + window.DD_RUM?.addError(new Error('salesforce xhr resource test failed'), { + source: 'salesforce-resource-button', + resourceType: 'xhr', + url, + }) + } + xhr.send() + } + + handleSelectorProbeClick(event) { + const innerActionName = event.currentTarget.getAttribute('data-dd-action-name') + this.lastSelectorProbe = innerActionName + + window.DD_RUM?.addAction('selector probe internal target', { + source: 'salesforce-selector-probe', + currentTargetActionName: innerActionName, + currentTargetTagName: event.currentTarget.tagName, + targetTagName: event.target.tagName, + composedPath: this.getComposedPathNames(event), + }) + } + + getResourceTestUrl(token) { + return `https://sample-json-api.com/products/1?${token}` + } + + getComposedPathNames(event) { + return event + .composedPath() + .slice(0, 6) + .map((target) => target.tagName || target.nodeName || String(target)) + } +} diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js-meta.xml new file mode 100644 index 0000000000..a183d52180 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/customActionButtons/customActionButtons.js-meta.xml @@ -0,0 +1,9 @@ + + + 65.0 + true + Custom Action Buttons + + lightning__HomePage + + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.html b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.html new file mode 100644 index 0000000000..cc340bc4c9 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.html @@ -0,0 +1 @@ + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js new file mode 100644 index 0000000000..d5945598df --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js @@ -0,0 +1,97 @@ +import { LightningElement, wire } from 'lwc' +import { NavigationMixin, CurrentPageReference } from 'lightning/navigation' +import datadogRumSlim from '@salesforce/resourceUrl/datadog_rum_slim' +import { loadScript } from 'lightning/platformResourceLoader' + +let datadogInitialization +let lastStartedUrl + +const DATADOG_PARAMS = ['c__applicationId', 'c__clientToken', 'c__env', 'c__service', 'c__site'] + +const cleanUrl = (url) => { + const cleanUrl = new URL(url, window.location.origin) + DATADOG_PARAMS.forEach((param) => cleanUrl.searchParams.delete(param)) + return cleanUrl.href +} + +const defaultDatadogRumConfig = { + trackViewsManually: true, + trackEarlyRequests: true, + trackLongTasks: true, + trackResources: true, + trackUserInteractions: true, + beforeSend: (event) => { + if (event.view) { + const sanitizedViewUrl = new URL(cleanUrl(event.view.url)) + event.view.url = sanitizedViewUrl.href + event.view.name = sanitizedViewUrl.pathname + sanitizedViewUrl.search + sanitizedViewUrl.hash + } + if (event.resource?.url) { + event.resource.url = cleanUrl(event.resource.url) + } + }, +} + +export default class DatadogInit extends NavigationMixin(LightningElement) { + connectedCallback() { + this.initialize() + } + + @wire(CurrentPageReference) + handleCurrentPageReference(pageReference) { + if (!pageReference) { + return + } + + this.initialize() + + if (window.DD_RUM) { + this.startViewForPageReference(pageReference) + } + } + + startViewForPageReference(pageReference) { + const urlPromise = this[NavigationMixin.GenerateUrl](pageReference) + urlPromise.then((url) => { + if (url === lastStartedUrl) { + return + } + lastStartedUrl = url + const absoluteUrl = new URL(url, window.location.origin).href + window.DD_RUM.startView({ name: url, url: absoluteUrl }) + }) + } + + initialize() { + if (!datadogInitialization) { + datadogInitialization = this.loadDatadogRum() + } + } + + loadDatadogRum() { + return loadScript(this, datadogRumSlim).then(() => { + const searchParams = new URLSearchParams(window.location.search) + const applicationId = searchParams.get('c__applicationId') + const clientToken = searchParams.get('c__clientToken') + + if (!applicationId || !clientToken) { + window.console.warn('Datadog RUM not initialized: missing c__applicationId or c__clientToken') + return + } + + window.DD_RUM.init({ + applicationId, + clientToken, + env: searchParams.get('c__env'), + service: searchParams.get('c__service'), + site: searchParams.get('c__site'), + ...defaultDatadogRumConfig, + }) + lastStartedUrl = window.location.pathname + window.location.search + window.location.hash + window.DD_RUM.startView({ + name: lastStartedUrl, + url: window.location.href, + }) + }) + } +} diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js-meta.xml new file mode 100644 index 0000000000..83b80e6493 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/datadogInit/datadogInit.js-meta.xml @@ -0,0 +1,9 @@ + + + 64.0 + true + Datadog Init + + lightning__UtilityBar + + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/jsconfig.json b/test/apps/sf-lwc-app/force-app/main/default/lwc/jsconfig.json new file mode 100644 index 0000000000..febe5fd90e --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/jsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "baseUrl": ".", + "paths": { + "c/*": ["*"] + } + }, + "include": ["**/*", "../../../../.sfdx/typings/lwc/**/*.d.ts"], + "typeAcquisition": { + "include": ["jest"] + } +} diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.html b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.html new file mode 100644 index 0000000000..22d68ac879 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.html @@ -0,0 +1,104 @@ + diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js new file mode 100644 index 0000000000..714b503296 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js @@ -0,0 +1,87 @@ +import { LightningElement } from 'lwc' + +const INITIAL_PRODUCTS = [ + { + id: 'dynamo-x2', + name: 'Dynamo X2', + category: 'Mountain', + level: 'Expert', + material: 'Carbon', + msrp: 7200, + motor: 'High torque 252 watt', + battery: '702Wh', + brakes: 'Hydraulic disc', + description: 'A high-output trail bike for technical climbs and fast descents.', + }, + { + id: 'electra-x2', + name: 'Electra X2', + category: 'Road', + level: 'Intermediate', + material: 'Aluminum', + msrp: 4300, + motor: 'Balanced 250 watt', + battery: '502Wh', + brakes: 'Dual-pivot caliper', + description: 'A lightweight commuter with fast handling and comfortable range.', + }, + { + id: 'fuse-x2', + name: 'Fuse X2', + category: 'Commuter', + level: 'Beginner', + material: 'Aluminum', + msrp: 2600, + motor: 'Efficient 250 watt', + battery: '402Wh', + brakes: 'Mechanical disc', + description: 'An approachable city bike for everyday testing workflows.', + }, +] + +export default class ProductExplorer extends LightningElement { + products = cloneProducts(INITIAL_PRODUCTS) + selectedProductId = INITIAL_PRODUCTS[0].id + + categoryOptions = toOptions(['Commuter', 'Mountain', 'Road']) + levelOptions = toOptions(['Beginner', 'Intermediate', 'Expert']) + materialOptions = toOptions(['Aluminum', 'Carbon', 'Steel']) + + get productsForView() { + return this.products + } + + get selectedProduct() { + return this.products.find((product) => product.id === this.selectedProductId) + } + + handleSelectProduct(event) { + this.selectedProductId = event.currentTarget.dataset.id + } + + handleFieldChange(event) { + const field = event.currentTarget.dataset.field + const value = field === 'msrp' ? Number(event.detail.value) : event.detail.value + + this.products = this.products.map((product) => + product.id === this.selectedProductId + ? { + ...product, + [field]: value, + } + : product + ) + } + + handleReset() { + this.products = cloneProducts(INITIAL_PRODUCTS) + } +} + +function cloneProducts(products) { + return products.map((product) => ({ ...product })) +} + +function toOptions(values) { + return values.map((value) => ({ label: value, value })) +} diff --git a/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js-meta.xml new file mode 100644 index 0000000000..27e42c077d --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/lwc/productExplorer/productExplorer.js-meta.xml @@ -0,0 +1,9 @@ + + + 65.0 + true + Product Explorer + + lightning__AppPage + + diff --git a/test/apps/sf-lwc-app/force-app/main/default/permissionsets/SF_LWC_App.permissionset-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/permissionsets/SF_LWC_App.permissionset-meta.xml new file mode 100644 index 0000000000..bbc61fa0f6 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/permissionsets/SF_LWC_App.permissionset-meta.xml @@ -0,0 +1,12 @@ + + + + SF_LWC_App + true + + + + Product_Explorer + Visible + + diff --git a/test/apps/sf-lwc-app/force-app/main/default/staticresources/datadog_rum_slim.resource-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/staticresources/datadog_rum_slim.resource-meta.xml new file mode 100644 index 0000000000..811f7d8899 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/staticresources/datadog_rum_slim.resource-meta.xml @@ -0,0 +1,5 @@ + + + Public + application/javascript + diff --git a/test/apps/sf-lwc-app/force-app/main/default/tabs/Product_Explorer.tab-meta.xml b/test/apps/sf-lwc-app/force-app/main/default/tabs/Product_Explorer.tab-meta.xml new file mode 100644 index 0000000000..e8104b8cc4 --- /dev/null +++ b/test/apps/sf-lwc-app/force-app/main/default/tabs/Product_Explorer.tab-meta.xml @@ -0,0 +1,7 @@ + + + Created by Lightning App Builder + Product_Explorer + + Custom3: Sun + diff --git a/test/apps/sf-lwc-app/package.json b/test/apps/sf-lwc-app/package.json new file mode 100644 index 0000000000..3e4c9820d2 --- /dev/null +++ b/test/apps/sf-lwc-app/package.json @@ -0,0 +1,8 @@ +{ + "name": "sf-lwc-app", + "private": true, + "description": "Salesforce Lightning app for browser-sdk Salesforce testing", + "scripts": { + "setup": "node scripts/setup.mjs" + } +} diff --git a/test/apps/sf-lwc-app/scripts/setup.mjs b/test/apps/sf-lwc-app/scripts/setup.mjs new file mode 100644 index 0000000000..4b5f5e0f7a --- /dev/null +++ b/test/apps/sf-lwc-app/scripts/setup.mjs @@ -0,0 +1,102 @@ +import { copyFileSync, existsSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { spawnSync } from 'node:child_process' +import { fileURLToPath } from 'node:url' + +const args = process.argv.slice(2).filter((arg) => arg !== '--') +const orgArgs = getOrgArgs(args) +const homePath = '/lightning/page/home' +const homeWithRumPath = + `${homePath}?c__applicationId=1397744d-34f4-4a6a-a735-801e31c18221` + + '&c__clientToken=pub2ad3fe2578f01b9f329bd0ea4a2f08c5' + + '&c__env=dev' + + '&c__service=browser-sdk-sandbox' + + '&c__site=datadoghq.com' + +syncDatadogBundle() +run('sf', ['project', 'deploy', 'start', ...args]) +assignPermissionSet() +printAppUrl() + +// Copies the Datadog RUM slim bundle to the app's staticresource +function syncDatadogBundle() { + const appDir = resolve(dirname(fileURLToPath(import.meta.url)), '..') + const browserSdkDir = resolve(appDir, '..', '..', '..') + const sourceBundle = resolve(browserSdkDir, 'packages/browser-rum-slim/bundle/datadog-rum-slim.js') + const targetBundle = resolve(appDir, 'force-app/main/default/staticresources/datadog_rum_slim.js') + + if (!existsSync(sourceBundle)) { + throw new Error( + `Missing Datadog RUM slim bundle at ${sourceBundle}. Run from a browser-sdk checkout with the bundle built.` + ) + } + + copyFileSync(sourceBundle, targetBundle) + console.log(`Synced ${targetBundle}`) +} + +function run(command, args) { + const result = spawnSync(command, args, { stdio: 'inherit' }) + if (result.status !== 0) { + process.exit(result.status ?? 1) + } +} + +function assignPermissionSet() { + const result = spawnSync('sf', ['org', 'assign', 'permset', '-n', 'SF_LWC_App', ...orgArgs], { + encoding: 'utf8', + }) + + if (result.status === 0) { + console.log('Assigned SF_LWC_App permission set') + return + } + + const output = `${result.stdout}\n${result.stderr}` + if (output.includes('Duplicate PermissionSetAssignment')) { + console.log('SF_LWC_App permission set already assigned') + return + } + + process.stdout.write(result.stdout) + process.stderr.write(result.stderr) + process.exit(result.status ?? 1) +} + +function printAppUrl() { + const result = spawnSync( + 'sf', + ['org', 'open', '-p', '/lightning/app/c__SF_LWC_App/page/home', '--url-only', '--json', ...orgArgs], + { + encoding: 'utf8', + } + ) + + if (result.status !== 0) { + process.stderr.write(result.stderr) + process.exit(result.status ?? 1) + } + + const { result: openResult } = JSON.parse(result.stdout) + const lightningUrl = new URL(openResult.url).origin.replace('.my.salesforce.com', '.lightning.force.com') + + console.log(`Home: ${openResult.url}`) + console.log(`Home with RUM: ${lightningUrl}${homeWithRumPath}`) +} + +function getOrgArgs(args) { + const orgArgs = [] + + for (let index = 0; index < args.length; index += 1) { + const arg = args[index] + + if ((arg === '-o' || arg === '--target-org') && args[index + 1]) { + orgArgs.push(arg, args[index + 1]) + index += 1 + } else if (arg.startsWith('--target-org=')) { + orgArgs.push(arg) + } + } + + return orgArgs +} diff --git a/test/apps/sf-lwc-app/sfdx-project.json b/test/apps/sf-lwc-app/sfdx-project.json new file mode 100644 index 0000000000..a68e6367f3 --- /dev/null +++ b/test/apps/sf-lwc-app/sfdx-project.json @@ -0,0 +1,10 @@ +{ + "packageDirectories": [ + { + "path": "force-app", + "default": true + } + ], + "namespace": "", + "sourceApiVersion": "65.0" +} diff --git a/test/apps/sf-lwc-app/yarn.lock b/test/apps/sf-lwc-app/yarn.lock new file mode 100644 index 0000000000..c36601d15f --- /dev/null +++ b/test/apps/sf-lwc-app/yarn.lock @@ -0,0 +1,12 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 10 + cacheKey: 10c0 + +"sf-lwc-app@workspace:.": + version: 0.0.0-use.local + resolution: "sf-lwc-app@workspace:." + languageName: unknown + linkType: soft