@salesforce/vite-plugin-lwc-ui-bundle 9.20.0 → 9.20.1

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.
@@ -23,17 +23,51 @@ The plugin compiles your LWC components into a single self-contained `dist/index
23
23
 
24
24
  This section is for team leads and app developers deciding **how** to uplift LWCs into static bundles for agentic surfaces. It's not a setup guide — for that, see [Off-Platform Build](#off-platform-build) below.
25
25
 
26
- The plugin ships configuration for a default, foundational set of UI API adapters — including `lightning/graphql`. Most uplifted bundles can rely on these defaults without writing any adapter config. During development, consumers can supplement the defaults by passing additional config to `builtins.lds()` (see [Configuring `builtins.lds()`](#configuring-builtinslds) below). Once a configuration is stable and useful to others, open a PR against [`packages/vite-plugin-lwc-ui-bundle/src/providers/lds/index.ts`](../src/providers/lds/index.ts) to add it to `DEFAULT_ADAPTERS` so it ships in the next plugin release.
26
+ The plugin ships configuration for a default, foundational set of UI API adapters — including `lightning/graphql`. Most uplifted bundles can rely on these defaults without writing any adapter config. During development, consumers can supplement the defaults by passing additional config to `builtins.lds()` (see [Configuring `builtins.lds()`](#configuring-builtinslds) below). Once a configuration is stable and useful to others, open a PR against [`packages/vite-plugin-lwc-ui-bundle/src/providers/lds/index.ts`](../src/providers/lds/index.ts) to add it to `DEFAULT_REGISTRY` so it ships in the next plugin release.
27
27
 
28
28
  The Data Orchestration Service team is not responsible for the implementation of MCP tooling, nor for the adapter configuration of capabilities outside DOS ownership. As far as external teams are concerned, DOS provides the mechanism that applies the uplift per configuration; what each adapter resolves to at runtime — and which MCP tools back it — is owned by the team that owns the underlying capability.
29
29
 
30
30
  ### What ships in the default registry today
31
31
 
32
- (see `DEFAULT_ADAPTERS` in [`src/providers/lds/index.ts`](../src/providers/lds/index.ts)):
32
+ Every default entry is one of four **flavors**, keyed off its `type`. For the
33
+ exhaustive, always-current specifier/adapter list, read `DEFAULT_REGISTRY` in
34
+ [`src/providers/lds/index.ts`](../src/providers/lds/index.ts).
35
+
36
+ - **Wire** (`wire` / `graphql-wire`) — `@wire`-driven reactive reads, e.g.
37
+ `lightning/uiRecordApi`→`getRecord`, `lightning/graphql`→`graphql`.
38
+ - **Imperative** (`imperative-mutation` / `graphql-mutation` / `imperative-read` /
39
+ `graphql-imperative-read`) — promise-returning calls invoked directly, e.g.
40
+ `lightning/uiRecordApi`→`createRecord`/`updateRecord`,
41
+ `lightning/uiObjectInfoApi`→`getObjectInfo_imperative`.
42
+ - **State manager** (`state-manager`) — externalized LWC state managers composed
43
+ through an upstream per-state-manager factory, e.g.
44
+ `lightning/stateManagerRecord`→`createSmRecord`. Off-core they do not
45
+ auto-refire ([pitfall #15](../skills/setup-lwc-vite-plugin/references/known-pitfalls.md#15-state-managers-off-core-do-not-auto-refire-no-shared-luvio-store)).
46
+ - **State manager (graphql)** — the `lightning/stateManagerGraphQL`→`createSmGraphQL`
47
+ variant: same composition, but exposes `errors` (plural) and a `refresh()` that
48
+ off-core re-runs the query via a fresh MCP tool call.
49
+
50
+ State-manager entries use a different config shape from wire and
51
+ imperative entries — `LdsStateManagerConfig` carries
52
+ `factoryModule` + `factoryName` instead of being purely runtime-driven.
53
+ The load hook imports the named factory at build time and composes the
54
+ externalized state manager around an MCP-backed imperative adapter
55
+ synthesized from `mcp.toolName` + `configJsonSchema`:
56
+
57
+ ```ts
58
+ {
59
+ type: 'state-manager',
60
+ factoryModule: '@salesforce/state-managers-uiapi/factory',
61
+ factoryName: 'createSmRecord', // or 'createSmObjectInfo', 'createSmGraphQL'
62
+ mcp: { toolName: 'getRecordMcpTool' },
63
+ configJsonSchema: { /* same per-resource constant the wire entry uses */ },
64
+ }
65
+ ```
33
66
 
34
- - `lightning/uiRecordApi` — `getRecord` (wire), `createRecord` (imperative mutation), `updateRecord` (imperative mutation)
35
- - `lightning/uiObjectInfoApi` `getObjectInfo_imperative` (legacy-shape imperative read)
36
- - `lightning/graphql` `graphql` (wire) + `executeMutation`
67
+ All per-state-manager metadata (`definedBy`, `requiredKeys`,
68
+ `optionalKeys`, predicate) lives in the upstream factory and reaches
69
+ off-core verbatim through the externalized state manager — registry
70
+ entries do **not** redeclare those fields.
37
71
 
38
72
  ### Configuring `builtins.lds()`
39
73
 
@@ -73,7 +107,7 @@ lwcVitePlugin({
73
107
  **What developers should do today:**
74
108
 
75
109
  - **Consolidate / minimize calls within each component.** GraphQL or batch-enabled adapters are an alternative — one `@wire(graphql)` query can request fields from multiple records / object types in one round-trip, replacing N separate `@wire(getRecord)` / `@wire(getObjectInfo)` calls.
76
- - **Share data across components via an LWC state manager.** A state manager serves as a single source of truth so descendants subscribe rather than re-fetching. This recovers the cross-component sharing that on-platform LDS provides for free.
110
+ - **Share data across components via an LWC state manager.** A state manager serves as a single source of truth so descendants subscribe rather than re-fetching. The plugin's default registry now ships `lightning/stateManagerRecord`, `lightning/stateManagerObjectInfo`, and `lightning/stateManagerGraphQL` (see "What ships in the default registry today" above). This recovers most of the cross-component sharing that on-platform LDS provides for free, but because there's no shared Luvio store off-core, **state managers do not auto-refire** — a sibling component mutating the same record won't refresh other instances. See [Known pitfall #15](../skills/setup-lwc-vite-plugin/references/known-pitfalls.md#15-state-managers-off-core-do-not-auto-refire-no-shared-luvio-store) for the re-trigger pattern (call `setConfig` with the same args, or `refresh()`).
77
111
 
78
112
  **See also:** [Known pitfall #14 — Inefficient data access: latency and lack of caching](../skills/setup-lwc-vite-plugin/references/known-pitfalls.md#14-inefficient-data-access-latency-and-lack-of-caching).
79
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/vite-plugin-lwc-ui-bundle",
3
- "version": "9.20.0",
3
+ "version": "9.20.1",
4
4
  "description": "Vite plugin for compiling LWC components into static bundles for off-platform and MCP use",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "Salesforce",
@@ -59,9 +59,9 @@
59
59
  "peerDependencies": {
60
60
  "@lwc/rollup-plugin": "^9.0.0",
61
61
  "@salesforce/lds-adapters-onestore-graphql": "^1.440.0",
62
- "@salesforce/platform-sdk": "^9.20.0",
62
+ "@salesforce/platform-sdk": "^9.20.1",
63
63
  "@salesforce/state-managers-uiapi": "^0.30.0",
64
- "@salesforce/ui-bundle": "^9.20.0",
64
+ "@salesforce/ui-bundle": "^9.20.1",
65
65
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
66
66
  "zod": "^3.23.8"
67
67
  },
@@ -87,7 +87,7 @@
87
87
  "devDependencies": {
88
88
  "@conduit-client/bindings-utils": "3.19.6",
89
89
  "@conduit-client/command-base": "3.19.6",
90
- "@salesforce/platform-sdk": "^9.20.0",
90
+ "@salesforce/platform-sdk": "^9.20.1",
91
91
  "typescript": "^5.9.3",
92
92
  "vite": "^7.0.0",
93
93
  "vite-plugin-dts": "^4.5.4",
@@ -137,6 +137,21 @@ Starting from the root component, trace the dependency tree:
137
137
  support (provider + mock branch)
138
138
  - `lightning/uiRecordApi` imported AND `@wire(getRecord` / `@wire(get*`
139
139
  actually used → needs LDS support (provider + mock branch)
140
+ - `lightning/stateManagerRecord` imported (default `smRecord` import)
141
+ → needs `builtins.lds()` provider (default registry covers it) +
142
+ the `getRecordMcpTool` `callTool` mock branch (same tool the
143
+ `@wire(getRecord)` path uses — one branch covers both). See
144
+ `known-pitfalls.md` for the no-auto-refire (no shared Luvio store)
145
+ reactivity contract.
146
+ - `lightning/stateManagerObjectInfo` imported (default `smObjectInfo`
147
+ import) → needs `builtins.lds()` provider + the
148
+ `getObjectInfoMcpTool` `callTool` mock branch.
149
+ - `lightning/stateManagerGraphQL` imported (default `smGraphQL`
150
+ import) → needs `builtins.lds()` provider + the `graphqlQuery`
151
+ `callTool` mock branch (same tool the `@wire(graphql)` path uses).
152
+ Note `smGraphQL` exposes `errors` (plural) on its outer shape,
153
+ matching its analogous wire adapter — distinct from UIAPI managers
154
+ which expose `error` (singular).
140
155
  - `lightning/*` base components → needs `lightning-base-components`
141
156
  npm package, plus `gate()`, `accessCheck()`, and `primitiveUtils()`
142
157
  providers because base components use these modules internally even
@@ -150,17 +165,23 @@ Starting from the root component, trace the dependency tree:
150
165
 
151
166
  **Be precise with LDS detection.** The LDS provider/mock is only needed
152
167
  when a component's `@wire` actually consumes `getRecord`/`getRecordUi`/
153
- similar adapters from `lightning/uiRecordApi`. **Do not trigger the LDS
154
- provider just because:**
168
+ similar adapters from `lightning/uiRecordApi`, **or** when a component
169
+ imports a `lightning/stateManager*` default export and calls its
170
+ `setConfig` / setters. **Do not trigger the LDS provider just because:**
155
171
 
156
172
  - The app has a chat wrapper whose tool output happens to contain a
157
173
  record shape (`records[...]`) — that goes through
158
174
  `window.openai.toolOutput` + the mapper, not the LDS wire adapter.
159
175
  - A component imports `lightning/uiRecordApi` but doesn't `@wire` it.
160
-
161
- `builtins.lds()` wires up `@wire(getRecord)` specifically. If no
162
- component calls those wire adapters, omit both the provider and the
163
- `getRecordMcpTool` mock branch. A chat wrapper that maps
176
+ - A component imports `lightning/stateManager*` but never instantiates
177
+ the state manager.
178
+
179
+ `builtins.lds()` wires up `@wire(getRecord)`, `@wire(graphql)`, **and**
180
+ the three `lightning/stateManager{Record,ObjectInfo,GraphQL}` virtual
181
+ modules — they all live in the same default registry. If no component
182
+ calls those wire adapters or instantiates a state manager, omit both
183
+ the provider and the corresponding mock branch (`getRecordMcpTool` /
184
+ `getObjectInfoMcpTool` / `graphqlQuery`). A chat wrapper that maps
164
185
  `window.openai.toolOutput.records[...]` into a component's `@api`
165
186
  props is independent of LDS.
166
187
 
@@ -201,6 +222,9 @@ Check in this order:
201
222
  `builtins.lds()` must be present (graphql specifiers are in the default registry)
202
223
  - Step 4 found `@wire(getRecord)` from `lightning/uiRecordApi` →
203
224
  `builtins.lds()` must be present (one `builtins.lds()` covers both)
225
+ - Step 4 found any `lightning/stateManager{Record,ObjectInfo,GraphQL}`
226
+ import that's actually instantiated → `builtins.lds()` must be
227
+ present (the default registry covers all three specifiers)
204
228
  - Step 4 found any `@salesforce/i18n/*` → `builtins.i18n()` must be
205
229
  present
206
230
  - Step 4 found any `@salesforce/client/*` → `builtins.client()` must
@@ -252,14 +276,20 @@ Ask the user whether to generate mocks. Mocks serve two independent
252
276
  purposes — list each one **only if Step 4 / Step 2 produced something
253
277
  to mock**, and skip lines that don't apply:
254
278
 
255
- - **`callTool` branches for wire adapters** — one branch per wire
256
- provider actually detected in Step 4:
257
- - `graphqlQuery` only if Step 4 found `@wire(graphql)` in a
258
- component's `.js`. Not needed otherwise.
259
- - `getRecordMcpTool` — only if Step 4 found `@wire(getRecord)` in
260
- a component's `.js`. Not needed just because a chat wrapper
261
- passes record-shaped data; the wrapper reads `toolOutput`, not
262
- `callTool`.
279
+ - **`callTool` branches for wire adapters and state managers** — one
280
+ branch per tool actually detected in Step 4. State managers share
281
+ tool names with their sibling wire adapters, so one branch covers
282
+ both call paths:
283
+ - `graphqlQuery` — only if Step 4 found `@wire(graphql)` **or**
284
+ `lightning/stateManagerGraphQL` (default `smGraphQL`) usage. Not
285
+ needed otherwise.
286
+ - `getRecordMcpTool` — only if Step 4 found `@wire(getRecord)` **or**
287
+ `lightning/stateManagerRecord` (default `smRecord`) usage. Not
288
+ needed just because a chat wrapper passes record-shaped data;
289
+ the wrapper reads `toolOutput`, not `callTool`.
290
+ - `getObjectInfoMcpTool` — only if Step 4 found
291
+ `lightning/stateManagerObjectInfo` (default `smObjectInfo`) usage,
292
+ or a future `getObjectInfo` imperative-read consumer.
263
293
  - **Seed `window.openai.toolOutput`** — only if Step 2 generated a
264
294
  chat wrapper. This is what drives the wrapper's initial render; the
265
295
  mapper consumes it. It has nothing to do with `callTool`.
@@ -373,11 +403,15 @@ import lwcVitePlugin, { builtins } from "@salesforce/vite-plugin-lwc-ui-bundle";
373
403
  // inside plugins -> lwcVitePlugin({ providers: [...] })
374
404
  builtins.lds(), // default registry:
375
405
  // lightning/uiRecordApi, uiObjectInfoApi,
376
- // and lightning/graphql (tool: "graphqlQuery")
406
+ // lightning/graphql (tool: "graphqlQuery"),
407
+ // lightning/stateManagerRecord -> getRecordMcpTool
408
+ // lightning/stateManagerObjectInfo -> getObjectInfoMcpTool
409
+ // lightning/stateManagerGraphQL -> graphqlQuery
377
410
 
378
411
  // Add to / override the default registry. The argument is deep-merged onto
379
412
  // the defaults per specifier, so you only declare what you are changing —
380
- // unmentioned entries (e.g. `getRecord`, `createRecord`) are preserved.
413
+ // unmentioned entries (e.g. `getRecord`, `createRecord`, the three
414
+ // `lightning/stateManager*` defaults) are preserved.
381
415
  builtins.lds({
382
416
  "lightning/graphql": {
383
417
  graphql: { type: "graphql-wire", mcp: { toolName: "myGraphqlTool" } },
@@ -386,13 +420,55 @@ builtins.lds({
386
420
  }),
387
421
  ```
388
422
 
423
+ **Registering or overriding a state manager** — the
424
+ `LdsStateManagerConfig` discriminator (`type: 'state-manager'`)
425
+ points the load hook at an upstream per-state-manager **factory**.
426
+ Composition (the externalized state manager and all its per-manager
427
+ metadata — `definedBy`, `requiredKeys`, `optionalKeys`, predicate)
428
+ lives in the upstream factory; the registry entry only carries the
429
+ factory specifier (`factoryModule` + `factoryName`), the MCP tool name,
430
+ and the JSON schema for `setConfig` validation:
431
+
432
+ ```js
433
+ builtins.lds({
434
+ "lightning/stateManagerRecord": {
435
+ default: {
436
+ type: "state-manager",
437
+ factoryModule: "@salesforce/state-managers-uiapi/factory",
438
+ factoryName: "createSmRecord",
439
+ mcp: { toolName: "getRecordMcpTool" },
440
+ configJsonSchema: {
441
+ /* same RECORD_SCHEMA used by getRecord wire entry */
442
+ },
443
+ },
444
+ },
445
+ }),
446
+ ```
447
+
448
+ The three default entries are:
449
+
450
+ | Specifier | `factoryModule` | `factoryName` | Default `mcp.toolName` |
451
+ | ---------------------------------- | --------------------------------------------------- | -------------------- | ---------------------- |
452
+ | `lightning/stateManagerRecord` | `@salesforce/state-managers-uiapi/factory` | `createSmRecord` | `getRecordMcpTool` |
453
+ | `lightning/stateManagerObjectInfo` | `@salesforce/state-managers-uiapi/factory` | `createSmObjectInfo` | `getObjectInfoMcpTool` |
454
+ | `lightning/stateManagerGraphQL` | `@salesforce/lds-adapters-onestore-graphql/factory` | `createSmGraphQL` | `graphqlQuery` |
455
+
456
+ State-manager entries do **not** carry `definedBy`, `requiredKeys`,
457
+ `optionalKeys`, or any predicate — those live in the upstream factory
458
+ and reach off-core verbatim through the externalized state manager.
459
+ Within an adapter entry, the override is a **wholesale replacement,
460
+ not a deep merge**, so when you redeclare the `default` adapter you
461
+ must repeat every field you want to keep (including `factoryModule` /
462
+ `factoryName`).
463
+
389
464
  Adapt based on earlier findings:
390
465
 
391
466
  - Set `dirs` for the detected project structure (SFDX vs namespaced).
392
467
  - Populate `builtins.label({...})` with values from Step 6.
393
468
  - Include `builtins.primitiveUtils()` if using `lightning-base-components`.
394
469
  - Include `builtins.lds()` if any component uses `lightning/uiRecordApi`,
395
- `lightning/uiObjectInfoApi`, or `lightning/graphql`.
470
+ `lightning/uiObjectInfoApi`, `lightning/graphql`, or any of the
471
+ `lightning/stateManager{Record,ObjectInfo,GraphQL}` specifiers.
396
472
  - Include `viteSingleFile()` — without it, `vite build` emits multi-file
397
473
  output and the MCP host can't inline the bundle. See
398
474
  `known-pitfalls.md#6` for the full config block.
@@ -406,9 +482,19 @@ Adapt based on earlier findings:
406
482
  - `builtins.lds()` default for `lightning/graphql` (both `graphql` wire
407
483
  and `executeMutation`): `graphqlQuery`. Override by registering the
408
484
  specifier explicitly — see example above.
485
+ - `builtins.lds()` default for `lightning/stateManagerRecord`:
486
+ `getRecordMcpTool` (shared with the `getRecord` wire entry — one
487
+ mock branch covers both).
488
+ - `builtins.lds()` default for `lightning/stateManagerObjectInfo`:
489
+ `getObjectInfoMcpTool`.
490
+ - `builtins.lds()` default for `lightning/stateManagerGraphQL`:
491
+ `graphqlQuery` (shared with the `graphql` wire entry).
409
492
 
410
493
  Whatever tool names you settle on here **must match** the branches in
411
- `bootstrap.js` mocks (Step 8).
494
+ `bootstrap.js` mocks (Step 8). For state-manager consumers, also note
495
+ that `smGraphQL`'s outer shape exposes `errors` (plural), matching its
496
+ sibling wire adapter — UIAPI managers (`smRecord`, `smObjectInfo`)
497
+ expose `error` (singular).
412
498
 
413
499
  #### `index.html`
414
500
 
@@ -466,6 +552,10 @@ Run this check **only** if Step 4 found actual usage of any of:
466
552
  - `lightning/uiRecordApi` with real `@wire` usage of `getRecord` /
467
553
  `createRecord` / `updateRecord`
468
554
  - `lightning/uiObjectInfoApi` with real usage
555
+ - `lightning/stateManagerRecord`, `lightning/stateManagerObjectInfo`,
556
+ or `lightning/stateManagerGraphQL` with real instantiation (default
557
+ `smRecord` / `smObjectInfo` / `smGraphQL` import that's actually
558
+ invoked) — these all flow through the same LDS runtime
469
559
 
470
560
  If none of the above are in the component tree, skip this sub-step
471
561
  entirely — the LDS runtime is never loaded and the peers don't matter.
@@ -365,3 +365,49 @@ inherently a problem — only chains and overlapping fetches are. The
365
365
  strategy section in
366
366
  [`docs/consumer-guide.md`](../../../docs/consumer-guide.md#data-waterfall-awareness)
367
367
  walks through the recommendation.
368
+
369
+ ## 15. State managers off-core do not auto-refire (no shared Luvio store)
370
+
371
+ **Symptom:** A `lightning/stateManager*` instance fetches once and then
372
+ never updates on its own, even when sibling code in the same page
373
+ mutates the same record. The `data` / `status` atoms stay frozen at
374
+ their first-loaded values; no second `callTool` fires unless the
375
+ consumer triggers it.
376
+
377
+ **Cause:** On core, every `lightning/stateManager*` instance shares a
378
+ single Luvio store. When any other code path mutates a record (a
379
+ `@wire(getRecord)` re-fetch, an `updateRecord` call, another state
380
+ manager observing the same key), Luvio re-publishes to every
381
+ subscriber on the same key — so siblings re-fire automatically.
382
+ Off-core through `vite-plugin-lwc-ui-bundle` there is no shared store:
383
+ each state manager wraps its own MCP-backed adapter, and `subscribe` is
384
+ a deliberate no-op (nothing fans changes out). Mutations elsewhere in
385
+ the page do not propagate automatically.
386
+
387
+ **Fix:** Re-trigger off-core state managers explicitly when the
388
+ consumer knows the underlying data may have changed. Call the matching
389
+ setter (`setRecordId`, `setFields`, …) or `setConfig(currentConfig)`
390
+ with the same arguments to re-run the adapter; `smGraphQL` also exposes
391
+ `refresh()` (upstream PR `lds-lightning-platform#7916`), which off-core
392
+ re-issues the query as a fresh MCP tool call and resolves with the new
393
+ result (rejecting only if that re-execute itself fails):
394
+
395
+ ```js
396
+ import smRecord from "lightning/stateManagerRecord";
397
+
398
+ const sm = smRecord();
399
+ sm.setConfig({ recordId: "001…", fields: ["Account.Name"] }); // initial load
400
+ // …later, after a sibling component mutates the same record:
401
+ sm.setConfig({ recordId: "001…", fields: ["Account.Name"] }); // re-fires the call
402
+ ```
403
+
404
+ **On-core behavior is unchanged** — the same upstream factories power
405
+ both; on core the shared store means there's nothing to work around.
406
+
407
+ **Prevention:** treat state managers off-core as imperative-with-atoms
408
+ rather than reactive subscriptions. Audit every place where a data
409
+ mutation in one component should be visible in another, and add an
410
+ explicit re-trigger (`setConfig` / `refresh()`) at the mutation site,
411
+ or a broadcast/event the dependent state managers listen for. The
412
+ plugin cannot synthesize a Luvio store — that's a §11 Risk 1 contract
413
+ baked into the off-core uplift.