@salesforce/vite-plugin-lwc-ui-bundle 11.13.2 → 11.15.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.
Files changed (39) hide show
  1. package/dist/index.d.ts +33 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +103 -6
  4. package/dist/index.js.map +1 -1
  5. package/dist/plugins/generate-meta-xml.d.ts +13 -0
  6. package/dist/plugins/generate-meta-xml.d.ts.map +1 -0
  7. package/dist/providers/access-check.d.ts.map +1 -1
  8. package/dist/providers/gate.d.ts.map +1 -1
  9. package/dist/providers/i18n.d.ts +16 -0
  10. package/dist/providers/i18n.d.ts.map +1 -1
  11. package/dist/providers/index.d.ts +2 -0
  12. package/dist/providers/index.d.ts.map +1 -1
  13. package/dist/providers/index.js +3 -178
  14. package/dist/providers/index.js.map +1 -1
  15. package/dist/providers/labels-graphql/index.d.ts.map +1 -1
  16. package/dist/providers/labels-graphql/index.js +12 -3
  17. package/dist/providers/labels-graphql/index.js.map +1 -1
  18. package/dist/providers/labels-graphql/runtime.d.ts +21 -14
  19. package/dist/providers/labels-graphql/runtime.d.ts.map +1 -1
  20. package/dist/providers/labels-graphql/runtime.js +39 -26
  21. package/dist/providers/labels-graphql/runtime.js.map +1 -1
  22. package/dist/providers/platform-graphql/constants.d.ts +15 -0
  23. package/dist/providers/platform-graphql/constants.d.ts.map +1 -0
  24. package/dist/providers/platform-graphql/i18n-static.d.ts +19 -0
  25. package/dist/providers/platform-graphql/i18n-static.d.ts.map +1 -0
  26. package/dist/providers/platform-graphql/index.d.ts +58 -0
  27. package/dist/providers/platform-graphql/index.d.ts.map +1 -0
  28. package/dist/providers/platform-graphql/index.js +312 -0
  29. package/dist/providers/platform-graphql/index.js.map +1 -0
  30. package/dist/providers/platform-graphql/runtime.d.ts +30 -0
  31. package/dist/providers/platform-graphql/runtime.d.ts.map +1 -0
  32. package/dist/providers/platform-graphql/runtime.js +189 -0
  33. package/dist/providers/platform-graphql/runtime.js.map +1 -0
  34. package/dist/types.d.ts +29 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/docs/consumer-guide.md +120 -4
  37. package/package.json +5 -5
  38. package/skills/setup-lwc-vite-plugin/SKILL.md +44 -1
  39. package/skills/setup-lwc-vite-plugin/references/known-pitfalls.md +57 -0
@@ -0,0 +1,189 @@
1
+ const permState = {
2
+ cache: /* @__PURE__ */ new Map(),
3
+ pending: /* @__PURE__ */ new Set(),
4
+ scheduled: false,
5
+ subscribers: /* @__PURE__ */ new Map()
6
+ };
7
+ const customPermState = {
8
+ cache: /* @__PURE__ */ new Map(),
9
+ pending: /* @__PURE__ */ new Set(),
10
+ scheduled: false,
11
+ subscribers: /* @__PURE__ */ new Map()
12
+ };
13
+ const i18nState = {
14
+ cache: /* @__PURE__ */ new Map(),
15
+ pending: /* @__PURE__ */ new Set(),
16
+ scheduled: false,
17
+ subscribers: /* @__PURE__ */ new Map()
18
+ };
19
+ let probePromise = null;
20
+ const I18N_GRAPHQL_KEYS = [
21
+ "lang",
22
+ "dir",
23
+ "locale",
24
+ "currency",
25
+ "timeZone",
26
+ "firstDayOfWeek",
27
+ "defaultCalendar",
28
+ "defaultNumberingSystem"
29
+ ];
30
+ const I18N_NUMERIC_KEYS = /* @__PURE__ */ new Set(["firstDayOfWeek"]);
31
+ const PERM_QUERY = `query UserPermissions($names: [String!]!) {
32
+ uiapi { platform { userPermissions(names: $names) { name hasAccess } } }
33
+ }`;
34
+ const CUSTOM_PERM_QUERY = `query CustomPermissions($names: [String!]!) {
35
+ uiapi { platform { customPermissions(names: $names) { name hasAccess } } }
36
+ }`;
37
+ const I18N_QUERY = `query I18n {
38
+ uiapi { platform { i18n { ${I18N_GRAPHQL_KEYS.join(" ")} } } }
39
+ }`;
40
+ async function detectPlatformPath() {
41
+ try {
42
+ const response = await graphqlFetch(`{ uiapi { platform { __typename } } }`, {});
43
+ return !!response?.data?.uiapi?.platform;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ function getApiShape() {
49
+ if (!probePromise) {
50
+ probePromise = detectPlatformPath();
51
+ }
52
+ return probePromise;
53
+ }
54
+ function notify(state, key, value) {
55
+ if (state.cache.get(key) === value) return;
56
+ state.cache.set(key, value);
57
+ const set = state.subscribers.get(key);
58
+ if (set) for (const cb of set) cb(value);
59
+ }
60
+ function subscribeTo(state, key, current, callback) {
61
+ let set = state.subscribers.get(key);
62
+ if (!set) {
63
+ set = /* @__PURE__ */ new Set();
64
+ state.subscribers.set(key, set);
65
+ }
66
+ set.add(callback);
67
+ callback(current());
68
+ return () => {
69
+ state.subscribers.get(key)?.delete(callback);
70
+ };
71
+ }
72
+ function getUserPermission(name, fallback) {
73
+ if (permState.cache.has(name)) return permState.cache.get(name);
74
+ permState.cache.set(name, fallback);
75
+ permState.pending.add(name);
76
+ schedulePermFetch(permState, PERM_QUERY, "userPermissions");
77
+ return fallback;
78
+ }
79
+ function subscribeUserPermission(name, fallback, cb) {
80
+ return subscribeTo(permState, name, () => getUserPermission(name, fallback), cb);
81
+ }
82
+ function getCustomPermission(name, fallback) {
83
+ if (customPermState.cache.has(name)) return customPermState.cache.get(name);
84
+ customPermState.cache.set(name, fallback);
85
+ customPermState.pending.add(name);
86
+ schedulePermFetch(customPermState, CUSTOM_PERM_QUERY, "customPermissions");
87
+ return fallback;
88
+ }
89
+ function subscribeCustomPermission(name, fallback, cb) {
90
+ return subscribeTo(customPermState, name, () => getCustomPermission(name, fallback), cb);
91
+ }
92
+ function schedulePermFetch(state, query, field) {
93
+ if (state.scheduled) return;
94
+ state.scheduled = true;
95
+ Promise.resolve().then(() => {
96
+ state.scheduled = false;
97
+ if (state.pending.size === 0) return;
98
+ const names = [...state.pending];
99
+ state.pending.clear();
100
+ fetchPermissions(state, query, field, names);
101
+ });
102
+ }
103
+ async function fetchPermissions(state, query, field, names) {
104
+ if (!await getApiShape()) return;
105
+ for (let i = 0; i < names.length; i += 100) {
106
+ const batch = names.slice(i, i + 100);
107
+ try {
108
+ const response = await graphqlFetch(query, { names: batch });
109
+ const records = response?.data?.uiapi?.platform?.[field] ?? [];
110
+ for (const rec of records) {
111
+ if (!rec) continue;
112
+ notify(state, rec.name, !!rec.hasAccess);
113
+ }
114
+ } catch (e) {
115
+ console.warn(`[platform-graphql] ${field} fetch failed:`, e);
116
+ }
117
+ }
118
+ }
119
+ function getI18n(key, fallback) {
120
+ if (i18nState.cache.has(key)) return i18nState.cache.get(key);
121
+ i18nState.cache.set(key, fallback);
122
+ scheduleI18nFetch();
123
+ return fallback;
124
+ }
125
+ function subscribeI18n(key, fallback, cb) {
126
+ return subscribeTo(i18nState, key, () => getI18n(key, fallback), cb);
127
+ }
128
+ function scheduleI18nFetch() {
129
+ if (i18nState.scheduled) return;
130
+ i18nState.scheduled = true;
131
+ Promise.resolve().then(() => {
132
+ i18nState.scheduled = false;
133
+ fetchI18n();
134
+ });
135
+ }
136
+ async function fetchI18n() {
137
+ if (!await getApiShape()) return;
138
+ try {
139
+ const response = await graphqlFetch(I18N_QUERY, {});
140
+ const payload = response?.data?.uiapi?.platform?.i18n;
141
+ if (!payload) return;
142
+ for (const k of I18N_GRAPHQL_KEYS) {
143
+ if (payload[k] == null) continue;
144
+ const value = I18N_NUMERIC_KEYS.has(k) ? Number(payload[k]) : String(payload[k]);
145
+ notify(i18nState, k, value);
146
+ }
147
+ } catch (e) {
148
+ console.warn("[platform-graphql] i18n fetch failed:", e);
149
+ }
150
+ }
151
+ async function graphqlFetch(query, variables) {
152
+ let createDataSDK;
153
+ try {
154
+ ({ createDataSDK } = await import("@salesforce/platform-sdk"));
155
+ } catch {
156
+ console.warn(
157
+ "[platform-graphql] @salesforce/platform-sdk is not available. Using fallback values."
158
+ );
159
+ return {};
160
+ }
161
+ if (typeof createDataSDK !== "function") return {};
162
+ const sdk = await createDataSDK();
163
+ if (!sdk.graphql) return {};
164
+ const result = await sdk.graphql.query({ query, variables });
165
+ return { data: result.data, errors: result.errors };
166
+ }
167
+ function __resetForTest() {
168
+ for (const s of [permState, customPermState, i18nState]) {
169
+ s.cache.clear();
170
+ s.pending.clear();
171
+ s.scheduled = false;
172
+ s.subscribers.clear();
173
+ }
174
+ probePromise = null;
175
+ }
176
+ export {
177
+ I18N_GRAPHQL_KEYS,
178
+ I18N_NUMERIC_KEYS,
179
+ __resetForTest,
180
+ getApiShape,
181
+ getCustomPermission,
182
+ getI18n,
183
+ getUserPermission,
184
+ graphqlFetch,
185
+ subscribeCustomPermission,
186
+ subscribeI18n,
187
+ subscribeUserPermission
188
+ };
189
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sources":["../../../src/providers/platform-graphql/runtime.ts"],"sourcesContent":["/**\n * Copyright (c) 2026, Salesforce, Inc.,\n * All rights reserved.\n * For full license text, see the LICENSE.txt file\n */\n\n/**\n * Shared runtime for scoped modules that resolve via `uiapi.platform.*` GraphQL\n * off-core through the Platform Data SDK:\n *\n * - `@salesforce/userPermission/<Name>` → `userPermissions(names:)` → boolean\n * - `@salesforce/accessCheck/<Name>` → `userPermissions(names:)` → boolean\n * - `@salesforce/customPermission/<Name>` → `customPermissions(names:)` → boolean\n * - `@salesforce/i18n/<key>` → `i18n { ... }` (locale identity)\n *\n * Like the labels runtime, each accessor returns a synchronous value (the\n * build-time fallback) immediately and schedules a batched GraphQL fetch; the\n * resolved org value lands in the cache and notifies subscribers. Components opt\n * into reactivity via the generated module's `subscribe(cb)` export.\n */\n\ninterface DomainState<V> {\n\tcache: Map<string, V>;\n\tpending: Set<string>;\n\tscheduled: boolean;\n\tsubscribers: Map<string, Set<(value: V) => void>>;\n}\n\nconst permState: DomainState<boolean> = {\n\tcache: new Map(),\n\tpending: new Set(),\n\tscheduled: false,\n\tsubscribers: new Map(),\n};\nconst customPermState: DomainState<boolean> = {\n\tcache: new Map(),\n\tpending: new Set(),\n\tscheduled: false,\n\tsubscribers: new Map(),\n};\n// i18n values are mostly strings, but a few keys are contractually numeric\n// (see I18N_NUMERIC_KEYS) so the domain is `string | number`.\nconst i18nState: DomainState<string | number> = {\n\tcache: new Map(),\n\tpending: new Set(),\n\tscheduled: false,\n\tsubscribers: new Map(),\n};\n\nlet probePromise: Promise<boolean> | null = null;\n\n/** i18n scalar fields exposed by `uiapi.platform.i18n` (locale identity). */\nexport const I18N_GRAPHQL_KEYS = [\n\t\"lang\",\n\t\"dir\",\n\t\"locale\",\n\t\"currency\",\n\t\"timeZone\",\n\t\"firstDayOfWeek\",\n\t\"defaultCalendar\",\n\t\"defaultNumberingSystem\",\n] as const;\n\n/**\n * i18n identity keys whose `@salesforce/i18n/*` contract is a NUMBER, not a\n * string (e.g. `firstDayOfWeek` is `1`=Mon … `7`=Sun). These are coerced with\n * `Number()` rather than `String()` so consumers that do arithmetic or strict\n * equality on them keep working. All other identity keys are strings.\n */\nexport const I18N_NUMERIC_KEYS = new Set<string>([\"firstDayOfWeek\"]);\n\nconst PERM_QUERY = `query UserPermissions($names: [String!]!) {\n uiapi { platform { userPermissions(names: $names) { name hasAccess } } }\n}`;\n\nconst CUSTOM_PERM_QUERY = `query CustomPermissions($names: [String!]!) {\n uiapi { platform { customPermissions(names: $names) { name hasAccess } } }\n}`;\n\nconst I18N_QUERY = `query I18n {\n uiapi { platform { i18n { ${I18N_GRAPHQL_KEYS.join(\" \")} } } }\n}`;\n\nasync function detectPlatformPath(): Promise<boolean> {\n\ttry {\n\t\tconst response = (await graphqlFetch(`{ uiapi { platform { __typename } } }`, {})) as {\n\t\t\tdata?: { uiapi?: { platform?: unknown } };\n\t\t};\n\t\treturn !!response?.data?.uiapi?.platform;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport function getApiShape(): Promise<boolean> {\n\tif (!probePromise) {\n\t\tprobePromise = detectPlatformPath();\n\t}\n\treturn probePromise;\n}\n\nfunction notify<V>(state: DomainState<V>, key: string, value: V): void {\n\tif (state.cache.get(key) === value) return;\n\tstate.cache.set(key, value);\n\tconst set = state.subscribers.get(key);\n\tif (set) for (const cb of set) cb(value);\n}\n\nfunction subscribeTo<V>(\n\tstate: DomainState<V>,\n\tkey: string,\n\tcurrent: () => V,\n\tcallback: (value: V) => void,\n): () => void {\n\tlet set = state.subscribers.get(key);\n\tif (!set) {\n\t\tset = new Set();\n\t\tstate.subscribers.set(key, set);\n\t}\n\tset.add(callback);\n\tcallback(current());\n\treturn () => {\n\t\tstate.subscribers.get(key)?.delete(callback);\n\t};\n}\n\n// ── Permissions (userPermissions / accessCheck) ──────────────────────────────\n\nexport function getUserPermission(name: string, fallback: boolean): boolean {\n\tif (permState.cache.has(name)) return permState.cache.get(name)!;\n\tpermState.cache.set(name, fallback);\n\tpermState.pending.add(name);\n\tschedulePermFetch(permState, PERM_QUERY, \"userPermissions\");\n\treturn fallback;\n}\n\nexport function subscribeUserPermission(\n\tname: string,\n\tfallback: boolean,\n\tcb: (v: boolean) => void,\n): () => void {\n\treturn subscribeTo(permState, name, () => getUserPermission(name, fallback), cb);\n}\n\nexport function getCustomPermission(name: string, fallback: boolean): boolean {\n\tif (customPermState.cache.has(name)) return customPermState.cache.get(name)!;\n\tcustomPermState.cache.set(name, fallback);\n\tcustomPermState.pending.add(name);\n\tschedulePermFetch(customPermState, CUSTOM_PERM_QUERY, \"customPermissions\");\n\treturn fallback;\n}\n\nexport function subscribeCustomPermission(\n\tname: string,\n\tfallback: boolean,\n\tcb: (v: boolean) => void,\n): () => void {\n\treturn subscribeTo(customPermState, name, () => getCustomPermission(name, fallback), cb);\n}\n\ninterface PermissionRecord {\n\tname: string;\n\thasAccess: boolean;\n}\n\nfunction schedulePermFetch(\n\tstate: DomainState<boolean>,\n\tquery: string,\n\tfield: \"userPermissions\" | \"customPermissions\",\n): void {\n\tif (state.scheduled) return;\n\tstate.scheduled = true;\n\tPromise.resolve().then(() => {\n\t\tstate.scheduled = false;\n\t\tif (state.pending.size === 0) return;\n\t\tconst names = [...state.pending];\n\t\tstate.pending.clear();\n\t\tfetchPermissions(state, query, field, names);\n\t});\n}\n\nasync function fetchPermissions(\n\tstate: DomainState<boolean>,\n\tquery: string,\n\tfield: \"userPermissions\" | \"customPermissions\",\n\tnames: string[],\n): Promise<void> {\n\t// platform.* path is required for these fields; if absent, keep fallbacks.\n\tif (!(await getApiShape())) return;\n\tfor (let i = 0; i < names.length; i += 100) {\n\t\tconst batch = names.slice(i, i + 100);\n\t\ttry {\n\t\t\tconst response = (await graphqlFetch(query, { names: batch })) as {\n\t\t\t\tdata?: { uiapi?: { platform?: Record<string, PermissionRecord[]> } };\n\t\t\t};\n\t\t\tconst records = response?.data?.uiapi?.platform?.[field] ?? [];\n\t\t\tfor (const rec of records) {\n\t\t\t\t// An unknown/invalid name yields a `null` array entry (with a\n\t\t\t\t// matching ValidationError in `errors`), positionally aligned with\n\t\t\t\t// the requested name. Skip it so one bad name in a mixed batch does\n\t\t\t\t// not abort resolution for the valid names alongside it — their\n\t\t\t\t// fallbacks would otherwise silently stick.\n\t\t\t\tif (!rec) continue;\n\t\t\t\tnotify(state, rec.name, !!rec.hasAccess);\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconsole.warn(`[platform-graphql] ${field} fetch failed:`, e);\n\t\t}\n\t}\n}\n\n// ── i18n (locale identity) ───────────────────────────────────────────────────\n\nexport function getI18n(key: string, fallback: string | number): string | number {\n\tif (i18nState.cache.has(key)) return i18nState.cache.get(key)!;\n\ti18nState.cache.set(key, fallback);\n\t// Any i18n key triggers a single fetch that resolves ALL identity fields at\n\t// once, so there is no per-key pending set to track (unlike permissions).\n\tscheduleI18nFetch();\n\treturn fallback;\n}\n\nexport function subscribeI18n(\n\tkey: string,\n\tfallback: string | number,\n\tcb: (v: string | number) => void,\n): () => void {\n\treturn subscribeTo(i18nState, key, () => getI18n(key, fallback), cb);\n}\n\nfunction scheduleI18nFetch(): void {\n\tif (i18nState.scheduled) return;\n\ti18nState.scheduled = true;\n\tPromise.resolve().then(() => {\n\t\ti18nState.scheduled = false;\n\t\tfetchI18n();\n\t});\n}\n\nasync function fetchI18n(): Promise<void> {\n\tif (!(await getApiShape())) return;\n\ttry {\n\t\tconst response = (await graphqlFetch(I18N_QUERY, {})) as {\n\t\t\tdata?: { uiapi?: { platform?: { i18n?: Record<string, unknown> } } };\n\t\t};\n\t\tconst payload = response?.data?.uiapi?.platform?.i18n;\n\t\tif (!payload) return;\n\t\tfor (const k of I18N_GRAPHQL_KEYS) {\n\t\t\tif (payload[k] == null) continue;\n\t\t\t// firstDayOfWeek (and any other numeric-contract key) stays a number;\n\t\t\t// everything else is a string. See I18N_NUMERIC_KEYS.\n\t\t\tconst value = I18N_NUMERIC_KEYS.has(k) ? Number(payload[k]) : String(payload[k]);\n\t\t\tnotify(i18nState, k, value);\n\t\t}\n\t} catch (e) {\n\t\tconsole.warn(\"[platform-graphql] i18n fetch failed:\", e);\n\t}\n}\n\n// ── Shared SDK transport ─────────────────────────────────────────────────────\n\n/**\n * Fetch through the Platform Data SDK — surface-aware (direct session GraphQL on\n * WebApp/MFE, window.openai bridge on OpenAI/MCP). Returns `{}` when no data\n * surface is available, so callers keep their build-time fallbacks.\n */\nexport async function graphqlFetch(\n\tquery: string,\n\tvariables: Record<string, unknown>,\n): Promise<unknown> {\n\tlet createDataSDK:\n\t\t| ((options?: unknown) => Promise<{\n\t\t\t\tgraphql?: { query: (opts: unknown) => Promise<{ data?: unknown; errors?: unknown }> };\n\t\t }>)\n\t\t| undefined;\n\ttry {\n\t\t({ createDataSDK } = (await import(\"@salesforce/platform-sdk\")) as unknown as {\n\t\t\tcreateDataSDK: typeof createDataSDK;\n\t\t});\n\t} catch {\n\t\tconsole.warn(\n\t\t\t\"[platform-graphql] @salesforce/platform-sdk is not available. Using fallback values.\",\n\t\t);\n\t\treturn {};\n\t}\n\tif (typeof createDataSDK !== \"function\") return {};\n\tconst sdk = await createDataSDK();\n\tif (!sdk.graphql) return {};\n\tconst result = await sdk.graphql.query({ query, variables });\n\treturn { data: result.data, errors: result.errors };\n}\n\n/** Reset internal state — exposed for tests only. */\nexport function __resetForTest(): void {\n\tfor (const s of [permState, customPermState, i18nState]) {\n\t\ts.cache.clear();\n\t\ts.pending.clear();\n\t\ts.scheduled = false;\n\t\ts.subscribers.clear();\n\t}\n\tprobePromise = null;\n}\n"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { UIBundleTarget } from '@salesforce/ui-bundle/app';
1
2
  import { Plugin } from 'vite';
2
3
  export interface Provider {
3
4
  prefix?: string;
@@ -27,6 +28,34 @@ export interface LwcVitePluginOptions {
27
28
  ignorePatterns?: string[];
28
29
  passthroughRules?: PassthroughRule[];
29
30
  bundle?: boolean;
31
+ /**
32
+ * Write the bundle's `*.uibundle-meta.xml` at build time, deriving its
33
+ * `<target>` element from {@link GenerateMetaXmlOptions.target}. The target
34
+ * is configured here, NOT in `ui-bundle.json` — the platform's manifest
35
+ * schema rejects a `target` field. `ui-bundle.json` (at the project root) is
36
+ * read only for the master-label fallback (`manifest.name`).
37
+ *
38
+ * Generation runs only when a `target` is supplied; without one there is
39
+ * nothing to contribute over a hand-authored meta XML. Enabled by default
40
+ * but inert until you pass a `target`. Set to `false` to disable outright,
41
+ * or pass an object to configure the target, paths, and metadata.
42
+ */
43
+ generateMetaXml?: boolean | GenerateMetaXmlOptions;
44
+ }
45
+ export interface GenerateMetaXmlOptions {
46
+ /**
47
+ * Deployment target for the bundle: "Experience", "CustomApplication", or
48
+ * "AppLauncher" (an alias for CustomApplication). Drives the generated
49
+ * `*.uibundle-meta.xml` `<target>`. Supplied here rather than in
50
+ * `ui-bundle.json` because the platform's manifest schema rejects a `target`
51
+ * field. When omitted, no `<target>` is emitted (and nothing is generated
52
+ * unless a description/label is otherwise needed).
53
+ */
54
+ target?: UIBundleTarget;
55
+ manifestPath?: string;
56
+ bundleName?: string;
57
+ description?: string;
58
+ overwriteExisting?: boolean;
30
59
  }
31
60
  export interface DiscoveredModule {
32
61
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,QAAQ;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAChC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAEnC,MAAM,WAAW,QAAQ;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAChC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,CAAC,EAAE,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,CAAC;IAC9B,GAAG,CAAC,EAAE,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,eAAe;IAC/B,GAAG,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,eAAe;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,oBAAoB;IACpC,OAAO,CAAC,EAAE,YAAY,CAAC;IACvB,SAAS,CAAC,EAAE,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;OAWG;IACH,eAAe,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;CACnD;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,cAAc,CAAC;IAExB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACtC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC"}
@@ -259,6 +259,21 @@ export default defineConfig({
259
259
  });
260
260
  ```
261
261
 
262
+ **What is the `providers` array?** It is the list of resolvers for `@salesforce/*`
263
+ scoped-module imports (`@salesforce/label/*`, `@salesforce/i18n/*`,
264
+ `@salesforce/userPermission/*`, `@salesforce/gate/*`, …) that don't exist as
265
+ real npm packages. Each `builtins.*()` returns a Vite plugin that intercepts one
266
+ family of specifiers and generates the module the platform LWC compiler would
267
+ otherwise provide.
268
+
269
+ **You usually don't need it.** When you **omit** `providers` entirely, the plugin
270
+ installs a default set that resolves labels, i18n, permissions, and access checks
271
+ at runtime via GraphQL (see [GraphQL-backed scoped modules](#graphql-backed-scoped-modules-default)
272
+ below). Pass `providers` only to opt a family **out** of the runtime fetch (e.g.
273
+ `builtins.i18n()` for browser/`Intl`-derived locale identity, `builtins.label()`
274
+ for static build-time labels) or to add extra config — the explicit list above
275
+ shows the opt-out shape, not a required one.
276
+
262
277
  #### Component Directory Configuration
263
278
 
264
279
  The plugin supports two directory structures:
@@ -298,8 +313,22 @@ Components are importable as `myNamespace/myComponent`.
298
313
 
299
314
  #### Configuring Labels
300
315
 
301
- The `builtins.label()` provider handles `@salesforce/label/*` imports. How you
302
- configure it depends on your project:
316
+ There are two providers for `@salesforce/label/*`:
317
+
318
+ - **`builtins.labelsGraphql()` — the default.** Resolves labels at runtime via UI
319
+ API GraphQL through the Platform Data SDK (`createDataSDK()`), so labels reflect
320
+ the current user's translation. The SDK picks the transport per surface: a
321
+ direct session-authenticated GraphQL request in a full-page web app, or the
322
+ `window.openai` bridge in an MCP/ChatGPT host. Build-time values (or the
323
+ key-derived fallback) render until the runtime fetch resolves. Included
324
+ automatically when you omit the `providers` array.
325
+ - **`builtins.label()` — static.** Resolves to a fixed build-time string. Use it
326
+ to opt out of runtime fetching (e.g. a pure off-core demo with no org).
327
+
328
+ Both accept the same overrides object; `labelsGraphql` uses the overrides as the
329
+ static fallback shown before/instead of a successful GraphQL fetch.
330
+
331
+ How you configure labels depends on your project:
303
332
 
304
333
  **SFDX project with `CustomLabels.labels-meta.xml`:**
305
334
 
@@ -340,8 +369,95 @@ you'll get runtime errors like:
340
369
  Uncaught TypeError: Cannot read properties of undefined (reading 'isOpen')
341
370
  ```
342
371
 
343
- Always include `builtins.gate()` and `builtins.accessCheck()` when using
344
- `lightning-base-components`.
372
+ Include `builtins.gate()` when using `lightning-base-components`. (When you omit
373
+ the `providers` array entirely, the default set already covers these.)
374
+
375
+ #### GraphQL-backed scoped modules (default)
376
+
377
+ When you **omit** the `providers` array, these scoped modules resolve at runtime
378
+ via UI API GraphQL through the Platform Data SDK (with build-time first-paint
379
+ values):
380
+
381
+ | Scoped module | GraphQL source (`uiapi.platform.*`) | Provider | First-paint value |
382
+ | -------------------------------------- | ----------------------------------- | ------------------- | ------------------------------------ |
383
+ | `@salesforce/label/*` | `labels` | `labelsGraphql()` | override or key-derived text |
384
+ | `@salesforce/i18n/*` (locale identity) | `i18n` | `platformGraphql()` | CLDR placeholder |
385
+ | `@salesforce/userPermission/*` | `userPermissions` | `platformGraphql()` | **required — you must configure it** |
386
+ | `@salesforce/accessCheck/*` | `userPermissions` | `platformGraphql()` | `false` (deny-by-default) |
387
+ | `@salesforce/customPermission/*` | `customPermissions` | `platformGraphql()` | **required — you must configure it** |
388
+
389
+ The generated modules export the first-paint value as `default` and an opt-in
390
+ `subscribe(callback)` that fires with the resolved org value.
391
+
392
+ ##### Named permissions must be configured (no silent guess)
393
+
394
+ A `@salesforce/userPermission/<Name>` or `@salesforce/customPermission/<Name>`
395
+ import is an assertion about **per-user runtime state**. A plain default import
396
+ binds the module's value **once** at module-eval time and never updates on its
397
+ own — so if the plugin silently defaulted an unconfigured permission to `false`,
398
+ your UI would render a permanent, wrong answer about what the current user can do
399
+ (and it would be wrong even on the GraphQL **success** path, because a default
400
+ import never re-reads the resolved value).
401
+
402
+ Rather than guess, **the build fails** if you import a named permission without
403
+ declaring the value it should show before the org responds:
404
+
405
+ ```
406
+ [platform-graphql] @salesforce/userPermission/ApiEnabled was imported but has no
407
+ first-paint value configured. …
408
+ ```
409
+
410
+ Declare it with the `defaultProviders()` helper — which lets you configure this
411
+ one provider while keeping every other default in place:
412
+
413
+ ```js
414
+ import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
415
+
416
+ lwcVitePlugin({
417
+ modules: { dirs: [{ path: "force-app/main/default/lwc", namespace: "c" }] },
418
+ providers: defaultProviders({
419
+ platformGraphql: {
420
+ userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
421
+ customPermissionDefaults: { My_Custom_Perm: false },
422
+ },
423
+ }),
424
+ });
425
+ ```
426
+
427
+ Use `false` unless you have a specific reason to render `true` before the fetch
428
+ resolves. `@salesforce/accessCheck/*` is exempt: it's a feature **gate**, so an
429
+ unconfigured check safely deny-defaults to `false` (base components rely on this),
430
+ though you can still override it.
431
+
432
+ ##### Reflecting the resolved value reactively (`subscribe`)
433
+
434
+ The configured value is only the **first paint**. To show the real org value once
435
+ the GraphQL fetch lands, import the module's `subscribe(callback)` export and
436
+ assign to a reactive field — the callback fires immediately with the current value
437
+ and again when the resolved value differs:
438
+
439
+ ```js
440
+ import apiEnabled, { subscribe } from "@salesforce/userPermission/ApiEnabled";
441
+
442
+ export default class extends LightningElement {
443
+ apiEnabled = apiEnabled; // first paint (your configured value)
444
+ connectedCallback() {
445
+ subscribe((v) => (this.apiEnabled = v)); // corrects in the DOM when the org responds
446
+ }
447
+ }
448
+ ```
449
+
450
+ > **Dual-deploy note:** the `subscribe` export exists only when the module is
451
+ > served by this plugin. If the **same** component source must also deploy as
452
+ > on-platform LWC metadata (where `@salesforce/userPermission/*` has no `subscribe`
453
+ > export), keep a plain default import — it will render the configured first-paint
454
+ > value off-core and the platform-resolved value on-core.
455
+
456
+ i18n number/date **format** patterns and calendar data stay static (CLDR reference
457
+ data, no org source). `@salesforce/gate/*` also stays static — its GraphQL field is
458
+ UiTier-context-only and not reachable off-core. The static `builtins.i18n()` /
459
+ `builtins.accessCheck()` / `builtins.label()` providers remain available for
460
+ explicit opt-out.
345
461
 
346
462
  ### Step 3: Create `index.html`
347
463
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/vite-plugin-lwc-ui-bundle",
3
- "version": "11.13.2",
3
+ "version": "11.15.0",
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",
@@ -49,7 +49,7 @@
49
49
  "skills"
50
50
  ],
51
51
  "scripts": {
52
- "build": "vite build && vite build --mode runtime-lds && vite build --mode runtime-labels-graphql",
52
+ "build": "vite build && vite build --mode runtime-lds && vite build --mode runtime-labels-graphql && vite build --mode runtime-platform-graphql",
53
53
  "clean": "rm -rf dist",
54
54
  "dev": "vite build --watch",
55
55
  "test": "vitest run",
@@ -74,9 +74,9 @@
74
74
  },
75
75
  "peerDependencies": {
76
76
  "@lwc/rollup-plugin": "^9.0.0",
77
- "@salesforce/platform-sdk": "^11.13.2",
77
+ "@salesforce/platform-sdk": "^11.15.0",
78
78
  "@salesforce/state-managers-uiapi": "^0.31.0",
79
- "@salesforce/ui-bundle": "^11.13.2",
79
+ "@salesforce/ui-bundle": "^11.15.0",
80
80
  "lwc": "^9.0.0",
81
81
  "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
82
82
  "zod": "^3.23.8"
@@ -100,7 +100,7 @@
100
100
  "devDependencies": {
101
101
  "@conduit-client/bindings-utils": "3.19.6",
102
102
  "@conduit-client/command-base": "3.19.6",
103
- "@salesforce/platform-sdk": "^11.13.2",
103
+ "@salesforce/platform-sdk": "^11.15.0",
104
104
  "@types/ws": "^8.5.12",
105
105
  "typescript": "^5.9.3",
106
106
  "vite": "^7.0.0",
@@ -157,7 +157,15 @@ Starting from the root component, trace the dependency tree:
157
157
  providers because base components use these modules internally even
158
158
  if user code doesn't
159
159
  - `@salesforce/gate/*`, `@salesforce/accessCheck/*` → handled by
160
- those providers
160
+ those providers (accessCheck deny-defaults to `false`, no per-name
161
+ config required)
162
+ - `@salesforce/userPermission/<Name>`, `@salesforce/customPermission/<Name>`
163
+ → **collect every `<Name>`.** These are named per-user permissions:
164
+ the default `platformGraphql()` provider **fails the build** unless
165
+ each imported name has a first-paint value declared in
166
+ `userPermissionDefaults` / `customPermissionDefaults`. Record the
167
+ exact names so Step 7 can generate them (default them to `false`
168
+ unless the user says otherwise). See `known-pitfalls.md#17`.
161
169
  - `@salesforce/i18n/*` → `i18n` provider
162
170
  - `@salesforce/client/*` → `client` provider (provides `formFactor`
163
171
  based on viewport width)
@@ -227,6 +235,13 @@ Check in this order:
227
235
  present (the default registry covers all three specifiers)
228
236
  - Step 4 found any `@salesforce/i18n/*` → `builtins.i18n()` must be
229
237
  present
238
+ - Step 4 found any `@salesforce/userPermission/<Name>` or
239
+ `@salesforce/customPermission/<Name>` → the default
240
+ `platformGraphql()` provider must declare a first-paint value for
241
+ **each** name in `userPermissionDefaults` / `customPermissionDefaults`,
242
+ or the build throws. Use the `defaultProviders({ platformGraphql: {…} })`
243
+ helper so the rest of the registry stays default. See
244
+ `references/known-pitfalls.md#17`.
230
245
  - Step 4 found any `@salesforce/client/*` → `builtins.client()` must
231
246
  be present
232
247
  - `modules.npm` includes `lightning-base-components` (or
@@ -465,6 +480,34 @@ Adapt based on earlier findings:
465
480
 
466
481
  - Set `dirs` for the detected project structure (SFDX vs namespaced).
467
482
  - Populate `builtins.label({...})` with values from Step 6.
483
+ - **Declare a first-paint value for every named permission Step 4 found.**
484
+ Each `@salesforce/userPermission/<Name>` / `@salesforce/customPermission/<Name>`
485
+ import needs an entry, or the build throws (`known-pitfalls.md#17`).
486
+ Prefer the `defaultProviders()` helper so you configure only this while
487
+ keeping the rest of the default registry:
488
+
489
+ ```js
490
+ import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
491
+
492
+ lwcVitePlugin({
493
+ modules: {
494
+ /* ... */
495
+ },
496
+ providers: defaultProviders({
497
+ platformGraphql: {
498
+ userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
499
+ customPermissionDefaults: { My_Custom_Perm: false },
500
+ },
501
+ }),
502
+ });
503
+ ```
504
+
505
+ Default each to `false` unless the user wants a `true` first paint; note
506
+ the resolved org value only reaches the DOM if the component imports the
507
+ module's `subscribe()` export (keep a plain default import if the same
508
+ source also deploys on-platform). `@salesforce/accessCheck/*` needs no
509
+ such config — it deny-defaults to `false`.
510
+
468
511
  - Include `builtins.primitiveUtils()` if using `lightning-base-components`.
469
512
  - Include `builtins.lds()` if any component uses `lightning/uiRecordApi`,
470
513
  `lightning/uiObjectInfoApi`, `lightning/graphql`, or any of the
@@ -440,3 +440,60 @@ the plugin with `livePreview({ debug: true })` (or run with plugin debug
440
440
  logging) — it logs the underlying import error. Note the bridge is a
441
441
  local-dev convenience only; its absence never affects the compiled
442
442
  bundle.
443
+
444
+ ## 17. Build fails: named permission imported with no first-paint value
445
+
446
+ **Symptom:** `vite build` (or `npm run dev`) fails with:
447
+
448
+ ```
449
+ [platform-graphql] @salesforce/userPermission/ApiEnabled was imported but
450
+ has no first-paint value configured. A permission is per-user runtime
451
+ state, so the plugin refuses to silently render a fabricated `false`. …
452
+ ```
453
+
454
+ **Cause:** A component imports `@salesforce/userPermission/<Name>` or
455
+ `@salesforce/customPermission/<Name>`, but no value was declared for that
456
+ name. A named permission is an assertion about **per-user runtime state**;
457
+ a plain default import binds the value **once** at module-eval time and
458
+ never updates on its own (only the module's `subscribe()` export does).
459
+ So a silently-defaulted `false` would render a permanent, wrong answer —
460
+ even when the GraphQL fetch **succeeds**. Rather than guess, the plugin
461
+ requires you to state the value shown before the org responds. (This is
462
+ **not** the same as `@salesforce/accessCheck/*`, which is a feature gate
463
+ and safely deny-defaults to `false` — see pitfall #8.)
464
+
465
+ **Fix:** Declare each imported permission's first-paint value via the
466
+ `defaultProviders()` helper (keeps every other default in place):
467
+
468
+ ```js
469
+ import lwcVitePlugin, { defaultProviders } from "@salesforce/vite-plugin-lwc-ui-bundle";
470
+
471
+ lwcVitePlugin({
472
+ modules: {
473
+ /* ... */
474
+ },
475
+ providers: defaultProviders({
476
+ platformGraphql: {
477
+ userPermissionDefaults: { ApiEnabled: false, CustomizeApplication: false },
478
+ customPermissionDefaults: { My_Custom_Perm: false },
479
+ },
480
+ }),
481
+ });
482
+ ```
483
+
484
+ Use `false` unless you have a specific reason to render `true` before the
485
+ fetch resolves. To reflect the **resolved** org value in the DOM, import
486
+ the module's `subscribe(callback)` export and assign to a reactive field:
487
+
488
+ ```js
489
+ import apiEnabled, { subscribe } from "@salesforce/userPermission/ApiEnabled";
490
+ // this.apiEnabled = apiEnabled; // first paint (configured)
491
+ // connectedCallback() { subscribe(v => this.apiEnabled = v); } // corrects in DOM
492
+ ```
493
+
494
+ If the same component must also deploy on-platform (where the scoped
495
+ module has no `subscribe` export), keep the plain default import.
496
+
497
+ **Prevention:** SKILL.md Step 4 flags every `@salesforce/userPermission/*`
498
+ and `@salesforce/customPermission/*` import so the generated config
499
+ declares its first-paint value up front.