@salesforce/angular-plugin-ui-bundle 11.59.1 → 11.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/serve.js CHANGED
@@ -4,16 +4,24 @@
4
4
  * All rights reserved.
5
5
  * For full license text, see the LICENSE.txt file
6
6
  */
7
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
8
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
9
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
10
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
11
+ });
12
+ }
13
+ return path;
14
+ };
7
15
  /**
8
16
  * sf-angular-serve — dev server wrapper for Angular CLI UI Bundles.
9
17
  *
10
- * Resolves API version from sf CLI session, then spawns ng serve with:
11
- * --define: substitutes __SF_API_VERSION__ in Vite's optimizeDeps prebundle
12
- * --port: pins to SF_UIBUNDLE_PORT (default 5173) for orchestrator discovery
18
+ * Runs ng serve with --define (substitutes __SF_API_VERSION__ in Vite's
19
+ * optimizeDeps prebundle the app-build esbuild plugin can't reach it) and
20
+ * --port (pins SF_UIBUNDLE_PORT for orchestrator discovery).
13
21
  *
14
- * Why this exists: Angular CLI's esbuild plugin (angular.json plugins[]) only
15
- * reaches the app build pass. Vite's optimizeDeps prebundle is a separate
16
- * esbuild invocation --define is the only way to reach it.
22
+ * The `--design` flag (consumed here, never forwarded to ng) selects the mode:
23
+ * default — spawns ng in a child process.
24
+ * --design — boots ng in-process so the compiler flag applies (see below).
17
25
  */
18
26
  import { spawn } from "node:child_process";
19
27
  import { createRequire } from "node:module";
@@ -21,27 +29,23 @@ import { join } from "node:path";
21
29
  import { pathToFileURL } from "node:url";
22
30
  import { createApiVersionPlugin } from "../plugins/api-version.js";
23
31
  import { getPort } from "../utils.js";
32
+ const require = createRequire(import.meta.url);
33
+ // `--design` is a wrapper-only flag: consumed here, never passed to ng.
34
+ const designMode = process.argv.includes("--design");
24
35
  const { version } = await createApiVersionPlugin();
25
36
  const port = getPort();
26
- // esbuild --define needs valid JS source for the value ("68.0", not 68.0), so
27
- // JSON.stringify once. No shell here (spawn without shell:true), so there is no
28
- // shell to strip a quoting layer — a second stringify would bake literal quotes
29
- // into the value and produce /services/data/v"68.0"/.
37
+ // --define needs valid JS source ("68.0", not 68.0); no shell, so stringify once
38
+ // (a second would bake literal quotes /services/data/v"68.0"/).
30
39
  const defineArg = `__SF_API_VERSION__=${JSON.stringify(version)}`;
31
- // Resolve Angular CLI bin without shell: spawn ng.js with the current Node executable.
32
- // This works cross-platform (Windows included) without shell: true.
33
- //
34
- // Resolve from the consuming app first (process.cwd() = where sf-angular-serve was
35
- // invoked). This is required in local dev where the plugin is symlinked via `file:`:
36
- // import.meta.url then points outside the app, so a module-relative resolve would miss
37
- // the app's @angular/cli. Fall back to module-relative for hoisted published installs.
40
+ // Resolve @angular/cli from the app (cwd) first the plugin sits outside the app
41
+ // (symlinked in dev, hoisted when published). Fall back to module-relative.
38
42
  function resolveNgCli() {
39
43
  try {
40
44
  const requireFromApp = createRequire(pathToFileURL(join(process.cwd(), "package.json")));
41
45
  return requireFromApp.resolve("@angular/cli/bin/ng.js");
42
46
  }
43
47
  catch {
44
- return createRequire(import.meta.url).resolve("@angular/cli/bin/ng.js");
48
+ return require.resolve("@angular/cli/bin/ng.js");
45
49
  }
46
50
  }
47
51
  let ngPath;
@@ -53,20 +57,60 @@ catch {
53
57
  "Please install it in your project: npm install --save-dev @angular/cli");
54
58
  process.exit(1);
55
59
  }
56
- const child = spawn(process.execPath, [ngPath, "serve", `--define=${defineArg}`, `--port=${port}`], {
57
- stdio: "inherit",
58
- env: { ...process.env, NODE_OPTIONS: "--no-deprecation" },
59
- });
60
- child.on("error", (err) => {
61
- console.error("[sf-angular-serve] Failed to start ng:", err.message);
62
- process.exit(1);
63
- });
64
- child.on("exit", (code) => {
65
- process.exit(code ?? 0);
66
- });
67
- process.on("SIGINT", () => {
68
- child.kill("SIGINT");
69
- });
70
- process.on("SIGTERM", () => {
71
- child.kill("SIGTERM");
72
- });
60
+ const ngArgs = ["serve", `--define=${defineArg}`, `--port=${port}`];
61
+ // Default mode: spawn ng in a child process (isolation + signal forwarding).
62
+ function spawnNgServe() {
63
+ const child = spawn(process.execPath, [ngPath, ...ngArgs], {
64
+ stdio: "inherit",
65
+ env: { ...process.env, NODE_OPTIONS: "--no-deprecation" },
66
+ });
67
+ child.on("error", (err) => {
68
+ console.error("[sf-angular-serve] Failed to start ng:", err.message);
69
+ process.exit(1);
70
+ });
71
+ child.on("exit", (code) => {
72
+ process.exit(code ?? 0);
73
+ });
74
+ process.on("SIGINT", () => {
75
+ child.kill("SIGINT");
76
+ });
77
+ process.on("SIGTERM", () => {
78
+ child.kill("SIGTERM");
79
+ });
80
+ }
81
+ // Design mode: boot ng in-process. The source-location flag is an in-memory global
82
+ // on @angular/compiler — a spawned child would get a fresh instance with it unset.
83
+ async function bootNgInProcessForDesign() {
84
+ process.env.SF_DESIGN_MODE = "true";
85
+ // Compile on the main thread so the flag below reaches the compiler's isolate.
86
+ process.env.NG_BUILD_PARALLEL_TS = "0";
87
+ // Bake `data-ng-source-location` onto every element (the hydrate script derives
88
+ // data-source-file / data-text-type from it). Resolve @angular/compiler from the
89
+ // app (cwd) to hit the same instance ng compiles with; the wrong copy = no-op.
90
+ try {
91
+ const appRequire = createRequire(pathToFileURL(join(process.cwd(), "package.json")));
92
+ const compiler = appRequire("@angular/compiler");
93
+ compiler.setEnableTemplateSourceLocations(true);
94
+ }
95
+ catch (err) {
96
+ console.error("[design-mode] failed to enable native source locations:", err);
97
+ }
98
+ // Rewrite argv to what plain `ng serve` would get, then boot. ng's exit code and
99
+ // signals are ours (same process); only the boot itself needs guarding.
100
+ const execPath = process.execPath;
101
+ const scriptPath = process.argv[1] ?? execPath;
102
+ process.argv = [execPath, scriptPath, ...ngArgs];
103
+ try {
104
+ await import(__rewriteRelativeImportExtension(pathToFileURL(ngPath).href));
105
+ }
106
+ catch (err) {
107
+ console.error("[sf-angular-serve] Failed to start ng:", err instanceof Error ? err.message : err);
108
+ process.exit(1);
109
+ }
110
+ }
111
+ if (designMode) {
112
+ await bootNgInProcessForDesign();
113
+ }
114
+ else {
115
+ spawnNgServe();
116
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ /** The function above, serialized to a self-invoking script served verbatim. */
7
+ export declare const DESIGN_MODE_HYDRATE_SCRIPT: string;
8
+ //# sourceMappingURL=design-mode-hydrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"design-mode-hydrate.d.ts","sourceRoot":"","sources":["../src/design-mode-hydrate.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAqFH,gFAAgF;AAChF,eAAO,MAAM,0BAA0B,QAAqC,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ /**
7
+ * Client-side hydration for design mode.
8
+ *
9
+ * Reads Angular's native `data-ng-source-location` and re-injects the attributes
10
+ * the design runtime consumes, so ui-design-mode is unchanged:
11
+ * - data-source-file="<path>:<line>:<col>"
12
+ * - data-text-type="none | mixed | element | static"
13
+ *
14
+ * A lone text node is labeled "static" (not "dynamic") so inline editing stays
15
+ * enabled: at runtime static text and `{{ interpolation }}` are indistinguishable.
16
+ *
17
+ * Authored as a normal function; `DESIGN_MODE_HYDRATE_SCRIPT` (below) serializes it
18
+ * to the string proxy.ts serves verbatim — no bundling step, no escaped regexes.
19
+ */
20
+ function hydrateClient() {
21
+ const NG_ATTR = "data-ng-source-location";
22
+ const SRC_ATTR = "data-source-file";
23
+ const TYPE_ATTR = "data-text-type";
24
+ // "<path>@o:<off>,l:<line>,c:<col>" -> "<path>:<line+1>:<col>" (native line 0-based).
25
+ // Keep the compiler's project-relative path verbatim — the host resolves and
26
+ // path-gates it, so a basename would break nested/same-named templates.
27
+ function toSourceFile(value) {
28
+ const m = /^(.*)@o:\d+,l:(\d+),c:(\d+)$/.exec(value);
29
+ if (!m || !m[1])
30
+ return null;
31
+ const path = m[1];
32
+ const line = parseInt(m[2], 10);
33
+ const col = parseInt(m[3], 10);
34
+ if (!isFinite(line) || !isFinite(col))
35
+ return null;
36
+ return path + ":" + (line + 1) + ":" + col;
37
+ }
38
+ // 0 children -> "none", >1 -> "mixed", 1 element -> "element", 1 text -> "static".
39
+ function classifyTextType(el) {
40
+ const relevant = [];
41
+ for (const n of el.childNodes) {
42
+ if (n.nodeType === 3 && !(n.textContent || "").trim())
43
+ continue;
44
+ relevant.push(n);
45
+ }
46
+ if (relevant.length === 0)
47
+ return "none";
48
+ if (relevant.length > 1)
49
+ return "mixed";
50
+ return relevant[0].nodeType === 1 ? "element" : "static";
51
+ }
52
+ function hydrateEl(el) {
53
+ const raw = el.getAttribute(NG_ATTR);
54
+ if (!raw)
55
+ return;
56
+ el.removeAttribute(NG_ATTR); // consumed; re-hydration passes skip it via the selector
57
+ const src = toSourceFile(raw);
58
+ if (!src)
59
+ return;
60
+ el.setAttribute(SRC_ATTR, src);
61
+ el.setAttribute(TYPE_ATTR, classifyTextType(el));
62
+ }
63
+ function hydrate(root) {
64
+ if (root.nodeType === 1 && root.hasAttribute(NG_ATTR)) {
65
+ hydrateEl(root);
66
+ }
67
+ for (const el of root.querySelectorAll("[" + NG_ATTR + "]")) {
68
+ hydrateEl(el);
69
+ }
70
+ }
71
+ function start() {
72
+ hydrate(document);
73
+ // Re-hydrate nodes Angular mounts later (@if / @for); the SRC_ATTR guard makes repeat passes cheap.
74
+ const observer = new MutationObserver((mutations) => {
75
+ for (const mutation of mutations) {
76
+ for (const node of mutation.addedNodes) {
77
+ if (node.nodeType === 1)
78
+ hydrate(node);
79
+ }
80
+ }
81
+ });
82
+ observer.observe(document.documentElement, { childList: true, subtree: true });
83
+ }
84
+ if (document.readyState === "loading") {
85
+ document.addEventListener("DOMContentLoaded", start);
86
+ }
87
+ else {
88
+ start();
89
+ }
90
+ }
91
+ /** The function above, serialized to a self-invoking script served verbatim. */
92
+ export const DESIGN_MODE_HYDRATE_SCRIPT = `(${hydrateClient.toString()})();`;
@@ -1 +1 @@
1
- {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/middleware/html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAmBH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA8DjE,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+E/F"}
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/middleware/html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA4EjE,wBAAsB,oBAAoB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CA+E/F"}
@@ -5,26 +5,24 @@
5
5
  */
6
6
  /**
7
7
  * HTML injection middleware for ng serve.
8
- * Intercepts HTML navigation responses (SPA routes) and injects the Live
9
- * Preview script, SFDC_ENV global, and base href. Skips static files and
10
- * /services/ paths.
8
+ * Intercepts SPA navigation responses and injects the Live Preview script,
9
+ * SFDC_ENV global, and base href. Skips static files and /services/ paths.
10
+ * In design mode (SF_DESIGN_MODE) it also injects the runtime <script> tag
11
+ * (proxy.ts serves that script).
11
12
  *
12
- * Execution order: BEFORE proxy middleware (wrapping must happen first).
13
+ * Runs BEFORE the proxy middleware (wrapping must happen first).
13
14
  *
14
- * The transform is split into two parts within this file: createIndexHtmlTransformer
15
- * is a pure string-in/string-out function (easy to unit-test on its own), and
16
- * createHtmlMiddleware is the res/req plumbing that buffers the response and feeds
17
- * it through that transform. The transformer is intentionally not part of the
18
- * package's public API — it is only ever consumed by this middleware.
15
+ * Split into a pure createIndexHtmlTransformer (string→string, unit-testable)
16
+ * and createHtmlMiddleware (req/res plumbing). The transformer is not public API.
19
17
  */
20
18
  import { getOrgInfo } from "@salesforce/ui-bundle/app";
21
19
  import { injectLivePreviewScript } from "@salesforce/ui-bundle/proxy";
20
+ import { DESIGN_MODE_HYDRATE_PATH, DESIGN_MODE_SCRIPT_PATH } from "./proxy.js";
22
21
  import { getCodeBuilderBasePath, getPort } from "../utils.js";
23
22
  /**
24
- * Factory: creates an HTML transform function bound to the current environment.
25
- * Resolves basePath once (from CODE_BUILDER_FRAMEWORK_PROXY_URI or "/") and
26
- * returns a pure transform. Only invoked by the ng serve middleware, so it
27
- * always injects — there is no build-time caller to guard against.
23
+ * Builds a pure HTML transform bound to the current env (basePath resolved once
24
+ * from CODE_BUILDER_FRAMEWORK_PROXY_URI or "/"). Only the ng serve middleware
25
+ * calls it, so it always injects.
28
26
  */
29
27
  async function createIndexHtmlTransformer(options = {}) {
30
28
  const port = getPort();
@@ -32,7 +30,8 @@ async function createIndexHtmlTransformer(options = {}) {
32
30
  const isCodeBuilder = !!codeBuilderProxyUrl;
33
31
  const basePath = isCodeBuilder ? getCodeBuilderBasePath(codeBuilderProxyUrl, port) : "/";
34
32
  const apiPath = basePath;
35
- // Resolve org URL for SFDC_ENV (used by Lightning Out, etc.)
33
+ const designMode = process.env.SF_DESIGN_MODE === "true";
34
+ // Org URL for SFDC_ENV (Lightning Out, etc.)
36
35
  let orgUrl;
37
36
  try {
38
37
  const orgInfo = await getOrgInfo(options.orgAlias);
@@ -47,10 +46,8 @@ async function createIndexHtmlTransformer(options = {}) {
47
46
  return (html) => {
48
47
  // 1. Live Preview script (enables VS Code extension communication)
49
48
  html = injectLivePreviewScript(html);
50
- // 2. Inject <base href> with computed basePath (Code Builder or "/")
51
- // If a base tag exists, replace it; otherwise inject after <head> (or skip if no <head>)
49
+ // 2. <base href>: replace an existing tag, else inject after <head>.
52
50
  const baseHref = basePath.endsWith("/") ? basePath : `${basePath}/`;
53
- // Match any <base ...> tag (single/double quotes, attributes in any order).
54
51
  const baseTagRegex = /<base\b[^>]*>/i;
55
52
  if (baseTagRegex.test(html)) {
56
53
  html = html.replace(baseTagRegex, `<base href="${baseHref}">`);
@@ -64,6 +61,20 @@ async function createIndexHtmlTransformer(options = {}) {
64
61
  if (html.includes("</head>")) {
65
62
  html = html.replace("</head>", ` ${sfdcEnvScript}\n</head>`);
66
63
  }
64
+ // 4. Design-mode scripts, before </body> (else </head>). Hydrate first so
65
+ // its attributes exist before the interactions runtime reads them.
66
+ if (designMode) {
67
+ const hydrateSrc = `${baseHref}${DESIGN_MODE_HYDRATE_PATH.replace(/^\//, "")}`;
68
+ const scriptSrc = `${baseHref}${DESIGN_MODE_SCRIPT_PATH.replace(/^\//, "")}`;
69
+ const designScripts = `<script type="module" src="${hydrateSrc}"></script>\n` +
70
+ ` <script type="module" src="${scriptSrc}"></script>`;
71
+ if (html.includes("</body>")) {
72
+ html = html.replace("</body>", ` ${designScripts}\n</body>`);
73
+ }
74
+ else if (html.includes("</head>")) {
75
+ html = html.replace("</head>", ` ${designScripts}\n</head>`);
76
+ }
77
+ }
67
78
  return html;
68
79
  };
69
80
  }
@@ -4,5 +4,7 @@
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
6
  import type { Middleware, SalesforceOptions } from "../types.ts";
7
+ export declare const DESIGN_MODE_SCRIPT_PATH = "/_sfdc/design-mode-interactions.js";
8
+ export declare const DESIGN_MODE_HYDRATE_PATH = "/_sfdc/design-mode-hydrate.js";
7
9
  export declare function createProxyMiddleware(options?: SalesforceOptions): Promise<Middleware>;
8
10
  //# sourceMappingURL=proxy.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/middleware/proxy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAuBjE,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAkEhG"}
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/middleware/proxy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAkBH,OAAO,KAAK,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIjE,eAAO,MAAM,uBAAuB,uCAAuC,CAAC;AAC5E,eAAO,MAAM,wBAAwB,kCAAkC,CAAC;AAsBxE,wBAAsB,qBAAqB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgGhG"}
@@ -5,16 +5,21 @@
5
5
  */
6
6
  /**
7
7
  * Proxy middleware factory.
8
- * Forwards /services/* requests to the connected Salesforce org with auth.
9
- * Watches ui-bundle.json for route changes and rebuilds handler automatically.
10
- *
11
- * Execution order: AFTER HTML middleware.
8
+ * Forwards /services/* to the connected org with auth; watches ui-bundle.json
9
+ * and rebuilds the handler on route changes. Ahead of proxying it serves a few
10
+ * local responses (health check, and in design mode the hydrate + interactions
11
+ * scripts). Runs AFTER the HTML middleware.
12
12
  */
13
13
  import { resolve } from "node:path";
14
14
  import { loadManifest, getOrgInfo } from "@salesforce/ui-bundle/app";
15
15
  import { createProxyHandler } from "@salesforce/ui-bundle/proxy";
16
+ import { getRuntimeScriptContent } from "@salesforce/ui-design-mode/runtime";
16
17
  import { watch } from "chokidar";
18
+ import { DESIGN_MODE_HYDRATE_SCRIPT } from "../design-mode-hydrate.js";
17
19
  import { getCodeBuilderBasePath, getPort } from "../utils.js";
20
+ // Served paths (html.ts imports these so the two files can't drift).
21
+ export const DESIGN_MODE_SCRIPT_PATH = "/_sfdc/design-mode-interactions.js";
22
+ export const DESIGN_MODE_HYDRATE_PATH = "/_sfdc/design-mode-hydrate.js";
18
23
  function buildHandler(manifest, orgInfo, options) {
19
24
  const port = getPort();
20
25
  const codeBuilderProxyUrl = process.env.CODE_BUILDER_FRAMEWORK_PROXY_URI;
@@ -35,6 +40,10 @@ export async function createProxyMiddleware(options = {}) {
35
40
  let cachedOrgInfo;
36
41
  let currentHandler;
37
42
  const manifestPath = resolve(process.cwd(), "ui-bundle.json");
43
+ // Read the pre-built runtime IIFE once; null = ui-design-mode not built
44
+ // (→ 404 below, server still boots). Inert unless in design mode.
45
+ const designMode = process.env.SF_DESIGN_MODE === "true";
46
+ const designScript = designMode ? getRuntimeScriptContent() : null;
38
47
  if (!cachedManifest) {
39
48
  cachedManifest = await loadManifest(manifestPath);
40
49
  }
@@ -71,6 +80,28 @@ export async function createProxyMiddleware(options = {}) {
71
80
  res.end();
72
81
  return;
73
82
  }
83
+ // Design-mode scripts — serve locally (Cache-Control: no-store so rebuilds are
84
+ // always picked up). endsWith tolerates a Code Builder base-path prefix.
85
+ if (designMode) {
86
+ const url = req.url?.split("?")[0] ?? "/";
87
+ if (url.endsWith(DESIGN_MODE_HYDRATE_PATH)) {
88
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
89
+ res.setHeader("Cache-Control", "no-store");
90
+ res.end(DESIGN_MODE_HYDRATE_SCRIPT);
91
+ return;
92
+ }
93
+ if (url.endsWith(DESIGN_MODE_SCRIPT_PATH)) {
94
+ if (designScript === null) {
95
+ res.writeHead(404, { "Content-Type": "text/plain" });
96
+ res.end("design-mode runtime not built");
97
+ return;
98
+ }
99
+ res.setHeader("Content-Type", "application/javascript; charset=utf-8");
100
+ res.setHeader("Cache-Control", "no-store");
101
+ res.end(designScript);
102
+ return;
103
+ }
104
+ }
74
105
  if (currentHandler) {
75
106
  try {
76
107
  await currentHandler(req, res, next);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/angular-plugin-ui-bundle",
3
3
  "description": "Angular CLI plugin for Salesforce UI Bundles",
4
- "version": "11.59.1",
4
+ "version": "11.60.0",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "type": "module",
7
7
  "main": "./dist/index.js",
@@ -29,7 +29,8 @@
29
29
  "test:coverage": "vitest run --coverage"
30
30
  },
31
31
  "dependencies": {
32
- "@salesforce/ui-bundle": "^11.59.1",
32
+ "@salesforce/ui-bundle": "^11.60.0",
33
+ "@salesforce/ui-design-mode": "^11.60.0",
33
34
  "chokidar": "^4.0.0"
34
35
  },
35
36
  "devDependencies": {