@salesforce/ui-bundle-template-app-react-template-b2e 11.55.1 → 11.56.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 (42) hide show
  1. package/dist/CHANGELOG.md +8 -0
  2. package/dist/force-app/main/default/uiBundles/reactinternalapp/package.json +4 -4
  3. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/README.md +235 -0
  4. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/api/apiUtils.ts +56 -0
  5. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/api/orgApiVersionService.ts +26 -0
  6. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/api/searchableContentTypesService.ts +116 -0
  7. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/channelResolver.ts +40 -0
  8. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/cmsQueryFragment.ts +97 -0
  9. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/contentTypeSessionCache.ts +134 -0
  10. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/contentTypeUtils.ts +38 -0
  11. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/hooks/useSearchableContentTypes.ts +128 -0
  12. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/index.ts +54 -0
  13. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/parseResponse.ts +65 -0
  14. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/searchChannel.ts +15 -0
  15. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/cms/types.ts +66 -0
  16. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/registry.ts +38 -0
  17. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/sobject/index.ts +19 -0
  18. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/sobject/parseResponse.ts +56 -0
  19. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/sobject/queryFragment.ts +141 -0
  20. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/adapters/types.ts +101 -0
  21. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/api/searchService.ts +110 -45
  22. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/GlobalSearchBox.tsx +98 -0
  23. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/MergedSearchResults.tsx +34 -17
  24. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/Search.tsx +31 -13
  25. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/SearchResults.tsx +17 -11
  26. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/SourceSection.tsx +9 -4
  27. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/controls/ScopeSelector.tsx +50 -6
  28. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/results/CmsResultRow.tsx +101 -0
  29. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/components/results/resolveResultRenderer.ts +49 -0
  30. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/config.json +7 -2
  31. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/constants.ts +8 -0
  32. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/hooks/useSearch.ts +313 -50
  33. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/index.ts +26 -1
  34. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/queryBuilder.ts +62 -118
  35. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/features/search/types.ts +74 -5
  36. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/pages/Home.tsx +3 -42
  37. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/routes.tsx +12 -5
  38. package/dist/force-app/main/default/uiBundles/reactinternalapp/tsconfig.tsbuildinfo +1 -1
  39. package/dist/package-lock.json +2 -2
  40. package/dist/package.json +1 -1
  41. package/package.json +4 -2
  42. package/dist/force-app/main/default/uiBundles/reactinternalapp/src/pages/AccountSearch.tsx +0 -25
@@ -1,151 +1,95 @@
1
1
  /**
2
- * Builds a single multi-aliased GraphQL document for search.
2
+ * Builds a single combined GraphQL document for search across every source.
3
3
  *
4
- * Each SObject source becomes one aliased child of `uiapi.query`, with its own
5
- * `first / after / where / orderBy` variables. Variable names are
6
- * `${sourceKey}_first` etc. so two sources with similar names never collide.
4
+ * The builder is kind-agnostic: for each source request it asks the registered
5
+ * adapter (`getAdapter(kind).buildRequest`) for a `QueryFragmentContribution`
6
+ * and assembles them:
7
+ * - `placement: "uiapi.query"` fragments (e.g. SObject sources) go inside the
8
+ * shared `uiapi { query { … } }` block.
9
+ * - `placement: "root"` fragments (e.g. the CMS `managed_content` block) go in
10
+ * as siblings of `uiapi` at the document root.
11
+ * - every contribution's `variableDeclarations` are concatenated into the
12
+ * `query Search(...)` header and every contribution's `variables` merged
13
+ * into one flat map.
7
14
  *
8
- * Selection sets are generated from `displayFields`:
9
- * - string `"Name"` → `Name @optional { value displayValue }`
10
- * - `{ name, raw: true }` → `Name`
11
- * - `{ name, subfields }` → nested `Name @optional { ... }`
15
+ * A contribution may declare zero extra variables (the CMS bootstrap call omits
16
+ * `$cmsContentTypeFQNs` entirely), so the merge tolerates declaration lists of
17
+ * differing lengths and never emits a declared-but-unused variable.
12
18
  *
13
- * The `idField` (default "Id") is always emitted; it is **not** wrapped
14
- * because uiapi's Id is a scalar.
19
+ * When only SObject sources participate, the emitted document is identical to
20
+ * the pre-adapter builder — the `uiapi` block is unchanged and no root siblings
21
+ * are added.
15
22
  */
16
23
 
17
- import { buildFilter, buildGlobalQueryClause, type ActiveFilterValue } from "./utils/filterUtils";
18
- import { buildOrderBy, type SortState } from "./utils/sortUtils";
19
- import type { DisplayField, SObjectSourceConfig } from "./types";
24
+ import { getAdapter } from "./adapters/registry";
25
+ import type { SourceRequest } from "./adapters/types";
20
26
 
21
- const KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
22
-
23
- function assertValidKey(key: string): void {
24
- if (!KEY_PATTERN.test(key)) {
25
- throw new Error(
26
- `Invalid source key "${key}". Must match /^[A-Za-z_][A-Za-z0-9_]*$/ to be a valid GraphQL alias and variable prefix.`,
27
- );
28
- }
29
- }
27
+ // `SourceRequest` is now defined canonically in `adapters/types.ts`; re-export
28
+ // it here for backward compatibility (the barrel and existing consumers import
29
+ // `SourceRequest` from `./queryBuilder`).
30
+ export type { SourceRequest } from "./adapters/types";
30
31
 
31
32
  export interface SearchQueryPayload {
32
33
  document: string;
33
34
  variables: Record<string, unknown>;
34
35
  }
35
36
 
36
- export interface SourceRequest {
37
- source: SObjectSourceConfig;
38
- q: string;
39
- filters: ActiveFilterValue[];
40
- sort: SortState | null;
41
- pageSize: number;
42
- afterCursor: string | undefined;
43
- }
44
-
45
37
  /**
46
- * Combines a per-source structured-filter clause with the global-q `or` clause.
47
- * Returns `undefined` when neither side has any constraints.
38
+ * Builds the combined query document and a flat variables map by dispatching
39
+ * each source through its adapter and partitioning fragments by placement.
48
40
  */
49
- function buildSourceWhere(
50
- source: SObjectSourceConfig,
51
- q: string,
52
- filters: ActiveFilterValue[],
53
- ): unknown {
54
- const clauses: unknown[] = [];
55
- const globalClause = buildGlobalQueryClause(q, source.searchableFields);
56
- if (globalClause) clauses.push(globalClause);
57
- const structured = buildFilter(filters, source.filterBy ?? []);
58
- if (structured) clauses.push(structured);
59
- if (clauses.length === 0) return undefined;
60
- if (clauses.length === 1) return clauses[0];
61
- return { and: clauses };
62
- }
63
-
64
- /** Builds the multi-aliased query document and a flat variables map. */
65
41
  export function buildSearchQuery(requests: SourceRequest[]): SearchQueryPayload {
66
42
  if (requests.length === 0) {
67
43
  throw new Error("buildSearchQuery requires at least one source request");
68
44
  }
69
45
 
70
46
  const variableDeclarations: string[] = [];
71
- const querySelections: string[] = [];
47
+ const uiapiFragments: string[] = [];
48
+ const rootFragments: string[] = [];
72
49
  const variables: Record<string, unknown> = {};
73
50
 
74
51
  for (const request of requests) {
75
- const { source } = request;
76
- assertValidKey(source.key);
77
-
78
- const firstVar = `${source.key}_first`;
79
- const afterVar = `${source.key}_after`;
80
- const whereVar = `${source.key}_where`;
81
- const orderByVar = `${source.key}_orderBy`;
82
- const whereType = source.whereTypeName ?? `${source.objectName}_Filter`;
83
- const orderByType = source.orderByTypeName ?? `${source.objectName}_OrderBy`;
84
-
85
- variableDeclarations.push(
86
- `$${firstVar}: Int`,
87
- `$${afterVar}: String`,
88
- `$${whereVar}: ${whereType}`,
89
- `$${orderByVar}: ${orderByType}`,
90
- );
91
-
92
- querySelections.push(
93
- `${source.key}: ${source.objectName}(` +
94
- `first: $${firstVar}, ` +
95
- `after: $${afterVar}, ` +
96
- `where: $${whereVar}, ` +
97
- `orderBy: $${orderByVar}` +
98
- `) {\n` +
99
- buildSelectionBody(source) +
100
- `\n}`,
101
- );
52
+ const contribution = getAdapter(request.source.kind).buildRequest(request);
53
+
54
+ // Concatenate declarations (may be empty for a given source) and merge
55
+ // values — an omitted declaration means the variable is never declared,
56
+ // so it is likewise never supplied.
57
+ variableDeclarations.push(...contribution.variableDeclarations);
58
+ Object.assign(variables, contribution.variables);
59
+
60
+ if (contribution.placement === "root") {
61
+ rootFragments.push(contribution.fragment);
62
+ } else {
63
+ uiapiFragments.push(contribution.fragment);
64
+ }
65
+ }
102
66
 
103
- variables[firstVar] = request.pageSize;
104
- variables[afterVar] = request.afterCursor ?? null;
105
- variables[whereVar] = buildSourceWhere(source, request.q, request.filters) ?? null;
106
- variables[orderByVar] = buildOrderBy(request.sort) ?? null;
67
+ const parts: string[] = [`query Search(${variableDeclarations.join(", ")}) {`];
68
+
69
+ // The `uiapi { query { … } }` block holds every `uiapi.query`-placed fragment.
70
+ // Emit it only when at least one such fragment exists — a CMS-only request
71
+ // (all SObject sources dropped) must not produce an empty, invalid selection
72
+ // set. When SObject sources are present its shape is identical to the
73
+ // pre-adapter builder.
74
+ if (uiapiFragments.length > 0) {
75
+ parts.push(` uiapi {`);
76
+ parts.push(` query {`);
77
+ parts.push(uiapiFragments.map((s) => indent(s, " ")).join("\n"));
78
+ parts.push(` }`);
79
+ parts.push(` }`);
107
80
  }
108
81
 
109
- const document =
110
- `query Search(${variableDeclarations.join(", ")}) {\n` +
111
- ` uiapi {\n` +
112
- ` query {\n` +
113
- querySelections.map((s) => indent(s, " ")).join("\n") +
114
- `\n }\n` +
115
- ` }\n` +
116
- `}\n`;
82
+ // Root-placement fragments (e.g. CMS `managed_content`) are siblings of uiapi.
83
+ for (const fragment of rootFragments) {
84
+ parts.push(indent(fragment, " "));
85
+ }
117
86
 
118
- return { document, variables };
119
- }
87
+ parts.push(`}`);
120
88
 
121
- function buildSelectionBody(source: SObjectSourceConfig): string {
122
- const idField = source.idField ?? "Id";
123
- const fieldLines = [` ${idField}`];
124
- for (const field of source.displayFields) {
125
- fieldLines.push(...renderField(field, " "));
126
- }
127
- return [
128
- ` edges { node {`,
129
- ...fieldLines,
130
- ` } }`,
131
- ` pageInfo { hasNextPage hasPreviousPage startCursor endCursor }`,
132
- ` totalCount`,
133
- ].join("\n");
134
- }
89
+ // Trailing newline preserved from the original builder.
90
+ const document = parts.join("\n") + "\n";
135
91
 
136
- function renderField(field: DisplayField, indentStr: string): string[] {
137
- if (typeof field === "string") {
138
- return [`${indentStr}${field} @optional { value displayValue }`];
139
- }
140
- if ("raw" in field) {
141
- return [`${indentStr}${field.name}`];
142
- }
143
- const lines = [`${indentStr}${field.name} @optional {`];
144
- for (const sub of field.subfields) {
145
- lines.push(...renderField(sub, indentStr + " "));
146
- }
147
- lines.push(`${indentStr}}`);
148
- return lines;
92
+ return { document, variables };
149
93
  }
150
94
 
151
95
  function indent(s: string, prefix: string): string {
@@ -12,6 +12,12 @@
12
12
 
13
13
  import type { FilterFieldConfig, ActiveFilterValue } from "./utils/filterUtils";
14
14
  import type { SortFieldConfig, SortState } from "./utils/sortUtils";
15
+ import type { CmsSourceConfig } from "./adapters/cms/types";
16
+ import type { DiscoveredContentType } from "./adapters/cms/contentTypeUtils";
17
+
18
+ // Re-export the CMS source config so consumers can import it from the public
19
+ // type surface without reaching into `adapters/cms`.
20
+ export type { CmsSourceConfig };
15
21
 
16
22
  /**
17
23
  * A single field to include in the GraphQL selection set of an SObject source.
@@ -99,6 +105,18 @@ export interface SObjectSourceConfig {
99
105
  orderByTypeName?: string;
100
106
  }
101
107
 
108
+ /**
109
+ * Discriminated union of every source kind a search experience can query.
110
+ *
111
+ * The `kind` field selects the runtime adapter (see `adapters/registry.ts`) and
112
+ * narrows the config to the fields that kind understands. New backends extend
113
+ * this union with a new `kind` + a matching adapter — no consumer changes.
114
+ *
115
+ * `CmsSourceConfig` is authored in `adapters/cms/types.ts` and unioned here so
116
+ * `SourceConfig` remains the single kind-agnostic config type.
117
+ */
118
+ export type SourceConfig = SObjectSourceConfig | CmsSourceConfig;
119
+
102
120
  /**
103
121
  * One global pagination config for the whole search experience. It is the
104
122
  * single source of truth — there is no per-source pagination. The same
@@ -133,7 +151,7 @@ export interface SearchPaginationConfig {
133
151
 
134
152
  /** Top-level config passed to {@link useSearch}. */
135
153
  export interface SearchConfig {
136
- sources: SObjectSourceConfig[];
154
+ sources: SourceConfig[];
137
155
  /**
138
156
  * Global pagination config — the single source of truth for page size, page
139
157
  * options, mode, and merge order. See {@link SearchPaginationConfig}. When
@@ -166,7 +184,12 @@ export interface SourceResult {
166
184
  }
167
185
 
168
186
  export interface SourceController {
169
- config: SObjectSourceConfig;
187
+ /**
188
+ * The source's config. Widened to the `SourceConfig` union so a controller
189
+ * can front any kind (SObject, CMS, …); consumers narrow on `config.kind`
190
+ * before reading kind-specific fields (`objectName`, `filterBy`, `sortBy`, …).
191
+ */
192
+ config: SourceConfig;
170
193
  result: SourceResult | null;
171
194
  loading: boolean;
172
195
  error: string | null;
@@ -193,16 +216,53 @@ export interface SourceController {
193
216
  }
194
217
 
195
218
  /**
196
- * The currently-selected search scope.
219
+ * The currently-selected search scope. Grammar:
220
+ *
221
+ * ```
222
+ * scope = "all" | <sobjectKey> | <cmsKey> | "<cmsKey>:<contentTypeFqn>"
223
+ * ```
197
224
  *
198
225
  * - `"all"`: every source in the config is queried and rendered.
199
- * - any source `key`: only that source is queried and rendered. Other sources
200
- * skip both the network request and the UI section.
226
+ * - `<sobjectKey>` / `<cmsKey>`: only that source is queried and rendered; the
227
+ * combined query still runs (a CMS source in scope sends its discovered
228
+ * content types if any are known, else omits them on the bootstrap call).
229
+ * - `"<cmsKey>:<contentTypeFqn>"`: a **CMS-only** search narrowed to one content
230
+ * type — SObject sources are dropped from the request and the CMS fragment
231
+ * sends `contentTypes = [<fqn>]`. `<fqn>` must pass `isValidCmsFqn`. These
232
+ * entries are appended to the scope dropdown after content-type discovery.
233
+ *
234
+ * The `":"` separator disambiguates the two halves; source keys match
235
+ * `/^[A-Za-z_][A-Za-z0-9_]*$/` (no colon), and FQNs match `CMS_FQN_PATTERN`.
201
236
  */
202
237
  export type SearchScope = "all" | string;
203
238
 
204
239
  export const ALL_SCOPE = "all" as const;
205
240
 
241
+ /** Separator between a CMS source key and a content-type FQN in a scope value. */
242
+ export const CMS_SCOPE_SEPARATOR = ":" as const;
243
+
244
+ /**
245
+ * A parsed {@link SearchScope}. `base` is the plain scope (`"all"` or a source
246
+ * key); `cmsFqn` is the selected CMS content-type FQN when the scope carries the
247
+ * `"<cmsKey>:<fqn>"` form, else `undefined`.
248
+ */
249
+ export interface ParsedScope {
250
+ base: SearchScope;
251
+ cmsFqn?: string;
252
+ }
253
+
254
+ /**
255
+ * Splits a {@link SearchScope} into `{ base, cmsFqn }`. Only the first `":"`
256
+ * separates the CMS source key from the content-type FQN; source keys contain
257
+ * no colon and FQNs (`sfdc_cms__…`) contain no colon either, so a single split
258
+ * is unambiguous. A scope with no separator has `cmsFqn === undefined`.
259
+ */
260
+ export function parseScope(scope: SearchScope): ParsedScope {
261
+ const sep = scope.indexOf(CMS_SCOPE_SEPARATOR);
262
+ if (sep === -1) return { base: scope };
263
+ return { base: scope.slice(0, sep), cmsFqn: scope.slice(sep + 1) };
264
+ }
265
+
206
266
  /**
207
267
  * One result node tagged with the source it came from. Produced by
208
268
  * {@link SearchHandle.mergedResults} so a single combined grid can render
@@ -300,6 +360,15 @@ export interface SearchHandle {
300
360
  globalPagination: GlobalPagination;
301
361
  loading: boolean;
302
362
  error: string | null;
363
+ /**
364
+ * CMS content types discovered at runtime from the CMS channel — each with
365
+ * its FQN and server-provided display `label` (empty until the first CMS
366
+ * result yields a channel id and discovery completes). The scope dropdown
367
+ * appends a `"<cmsKey>:<fqn>"` entry per type so a user can narrow a CMS
368
+ * search to a single content type. Empty for SObject-only configs or before
369
+ * discovery.
370
+ */
371
+ discoveredContentTypes: DiscoveredContentType[];
303
372
  resetAll: () => void;
304
373
  }
305
374
 
@@ -1,49 +1,10 @@
1
- import { useState } from "react";
2
- import { useNavigate } from "react-router";
3
- import { Search } from "lucide-react";
4
- import { Input } from "../components/ui/input";
5
- import { Button } from "../components/ui/button";
1
+ import { GlobalSearchBox } from "../features/search";
6
2
 
7
3
  export default function HomePage() {
8
- const navigate = useNavigate();
9
- const [text, setText] = useState("");
10
-
11
- const handleSubmit = (e: React.SyntheticEvent<HTMLFormElement>) => {
12
- e.preventDefault();
13
- // `q` matches the search feature's GLOBAL_QUERY_KEY, so the term
14
- // pre-fills the Account search page on arrival.
15
- const params = text ? `?q=${encodeURIComponent(text)}` : "";
16
- navigate(`/accounts${params}`);
17
- };
18
-
19
4
  return (
20
5
  <div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
21
- <div className="flex items-center gap-6 mb-6">
22
- <h1 className="text-2xl font-bold">Account Search</h1>
23
- <Button variant="outline" size="sm" onClick={() => navigate("/accounts")}>
24
- Browse All Accounts
25
- </Button>
26
- </div>
27
- <form onSubmit={handleSubmit} className="flex gap-2">
28
- <div className="relative flex-1">
29
- <label htmlFor="account-search-input" className="sr-only">
30
- Search accounts
31
- </label>
32
- <Search
33
- aria-hidden="true"
34
- className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground"
35
- />
36
- <Input
37
- id="account-search-input"
38
- type="text"
39
- value={text}
40
- onChange={(e) => setText(e.target.value)}
41
- placeholder="Search by name, phone, or industry..."
42
- className="pl-9"
43
- />
44
- </div>
45
- <Button type="submit">Search</Button>
46
- </form>
6
+ <h1 className="text-2xl font-bold mb-6">Home</h1>
7
+ <GlobalSearchBox placeholder="Search accounts, contacts, opportunities..." />
47
8
  </div>
48
9
  );
49
10
  }
@@ -2,7 +2,7 @@ import type { RouteObject } from 'react-router';
2
2
  import AppLayout from './appLayout';
3
3
  import Home from './pages/Home';
4
4
  import NotFound from './pages/NotFound';
5
- import AccountSearch from "./pages/AccountSearch";
5
+ import { Search as GlobalSearch, config } from "./features/search";
6
6
  import AccountObjectDetail from "./pages/AccountObjectDetailPage";
7
7
 
8
8
  export const routes: RouteObject[] = [
@@ -16,12 +16,19 @@ export const routes: RouteObject[] = [
16
16
  handle: { showInNavigation: true, label: "Home" }
17
17
  },
18
18
  {
19
- path: "accounts/:recordId",
20
- element: <AccountObjectDetail />
19
+ path: "search",
20
+ element: (
21
+ <GlobalSearch
22
+ config={config}
23
+ title="Search"
24
+ searchPlaceholder="Search accounts, contacts, opportunities, and content..."
25
+ />
26
+ ),
27
+ handle: { showInNavigation: true, label: "Search" }
21
28
  },
22
29
  {
23
- path: "accounts",
24
- element: <AccountSearch />
30
+ path: "accounts/:recordId",
31
+ element: <AccountObjectDetail />
25
32
  },
26
33
  {
27
34
  path: '*',
@@ -1 +1 @@
1
- {"root":["./src/app.tsx","./src/appLayout.tsx","./src/index.ts","./src/navigationMenu.tsx","./src/router-utils.tsx","./src/routes.tsx","./src/api/graphqlClient.ts","./src/components/AgentforceConversationClient.tsx","./src/components/ObjectBreadcrumb.tsx","./src/components/__inherit_AgentforceConversationClient.tsx","./src/components/alerts/status-alert.tsx","./src/components/layouts/card-layout.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/breadcrumb.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/datePicker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/field.tsx","./src/components/ui/index.ts","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/spinner.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/features/search/index.ts","./src/features/search/loadConfig.ts","./src/features/search/queryBuilder.ts","./src/features/search/types.ts","./src/features/search/api/distinctValuesService.ts","./src/features/search/api/searchService.ts","./src/features/search/components/MergedSearchResults.tsx","./src/features/search/components/Search.tsx","./src/features/search/components/SearchResults.tsx","./src/features/search/components/SourceSection.tsx","./src/features/search/components/controls/PaginationControls.tsx","./src/features/search/components/controls/ScopeSelector.tsx","./src/features/search/components/controls/SearchBar.tsx","./src/features/search/components/controls/SortControl.tsx","./src/features/search/components/filters/ActiveFilters.tsx","./src/features/search/components/filters/DefaultFilterPanel.tsx","./src/features/search/components/filters/FilterContext.tsx","./src/features/search/components/filters/inputs/BooleanFilter.tsx","./src/features/search/components/filters/inputs/DateRangeFilter.tsx","./src/features/search/components/filters/inputs/FilterFieldWrapper.tsx","./src/features/search/components/filters/inputs/MultiSelectFilter.tsx","./src/features/search/components/filters/inputs/NumericRangeFilter.tsx","./src/features/search/components/filters/inputs/SelectFilter.tsx","./src/features/search/components/filters/inputs/TextFilter.tsx","./src/features/search/components/results/DefaultResultRow.tsx","./src/features/search/hooks/useAsyncData.ts","./src/features/search/hooks/useDistinctValues.ts","./src/features/search/hooks/useSearch.ts","./src/features/search/utils/debounce.ts","./src/features/search/utils/fieldUtils.ts","./src/features/search/utils/filterUtils.ts","./src/features/search/utils/sortUtils.ts","./src/hooks/useAsyncData.ts","./src/lib/utils.ts","./src/pages/AccountObjectDetailPage.tsx","./src/pages/AccountSearch.tsx","./src/pages/Home.tsx","./src/pages/NotFound.tsx","./src/types/conversation.ts","./src/types/globals.d.ts","./src/utils/accountFields.ts","./e2e/app.spec.ts","./vite-env.d.ts","./vitest-env.d.ts","./vitest.setup.ts","../../../../../../../../../sdk/platform-sdk/types/env.d.ts"],"version":"5.9.3"}
1
+ {"root":["./src/app.tsx","./src/appLayout.tsx","./src/index.ts","./src/navigationMenu.tsx","./src/router-utils.tsx","./src/routes.tsx","./src/api/graphqlClient.ts","./src/components/AgentforceConversationClient.tsx","./src/components/ObjectBreadcrumb.tsx","./src/components/__inherit_AgentforceConversationClient.tsx","./src/components/alerts/status-alert.tsx","./src/components/layouts/card-layout.tsx","./src/components/ui/alert.tsx","./src/components/ui/avatar.tsx","./src/components/ui/badge.tsx","./src/components/ui/breadcrumb.tsx","./src/components/ui/button.tsx","./src/components/ui/calendar.tsx","./src/components/ui/card.tsx","./src/components/ui/checkbox.tsx","./src/components/ui/collapsible.tsx","./src/components/ui/datePicker.tsx","./src/components/ui/dialog.tsx","./src/components/ui/dropdown-menu.tsx","./src/components/ui/field.tsx","./src/components/ui/index.ts","./src/components/ui/input.tsx","./src/components/ui/label.tsx","./src/components/ui/pagination.tsx","./src/components/ui/popover.tsx","./src/components/ui/select.tsx","./src/components/ui/separator.tsx","./src/components/ui/skeleton.tsx","./src/components/ui/sonner.tsx","./src/components/ui/spinner.tsx","./src/components/ui/table.tsx","./src/components/ui/tabs.tsx","./src/features/search/constants.ts","./src/features/search/index.ts","./src/features/search/loadConfig.ts","./src/features/search/queryBuilder.ts","./src/features/search/types.ts","./src/features/search/__tests__/queryBuilder.test.ts","./src/features/search/adapters/registry.ts","./src/features/search/adapters/types.ts","./src/features/search/adapters/cms/channelResolver.ts","./src/features/search/adapters/cms/cmsQueryFragment.ts","./src/features/search/adapters/cms/contentTypeSessionCache.ts","./src/features/search/adapters/cms/contentTypeUtils.ts","./src/features/search/adapters/cms/index.ts","./src/features/search/adapters/cms/parseResponse.ts","./src/features/search/adapters/cms/searchChannel.ts","./src/features/search/adapters/cms/types.ts","./src/features/search/adapters/cms/__tests__/channelResolver.test.ts","./src/features/search/adapters/cms/__tests__/cmsQueryFragment.test.ts","./src/features/search/adapters/cms/__tests__/contentTypeSessionCache.test.ts","./src/features/search/adapters/cms/__tests__/contentTypeUtils.test.ts","./src/features/search/adapters/cms/__tests__/orgApiVersionService.test.ts","./src/features/search/adapters/cms/__tests__/parseResponse.test.ts","./src/features/search/adapters/cms/__tests__/searchChannel.test.ts","./src/features/search/adapters/cms/__tests__/searchableContentTypesService.test.ts","./src/features/search/adapters/cms/api/apiUtils.ts","./src/features/search/adapters/cms/api/orgApiVersionService.ts","./src/features/search/adapters/cms/api/searchableContentTypesService.ts","./src/features/search/adapters/cms/hooks/useSearchableContentTypes.ts","./src/features/search/adapters/sobject/index.ts","./src/features/search/adapters/sobject/parseResponse.ts","./src/features/search/adapters/sobject/queryFragment.ts","./src/features/search/api/distinctValuesService.ts","./src/features/search/api/searchService.ts","./src/features/search/api/__tests__/searchService.test.ts","./src/features/search/components/GlobalSearchBox.tsx","./src/features/search/components/MergedSearchResults.tsx","./src/features/search/components/Search.tsx","./src/features/search/components/SearchResults.tsx","./src/features/search/components/SourceSection.tsx","./src/features/search/components/__tests__/Search.test.tsx","./src/features/search/components/controls/PaginationControls.tsx","./src/features/search/components/controls/ScopeSelector.tsx","./src/features/search/components/controls/SearchBar.tsx","./src/features/search/components/controls/SortControl.tsx","./src/features/search/components/filters/ActiveFilters.tsx","./src/features/search/components/filters/DefaultFilterPanel.tsx","./src/features/search/components/filters/FilterContext.tsx","./src/features/search/components/filters/inputs/BooleanFilter.tsx","./src/features/search/components/filters/inputs/DateRangeFilter.tsx","./src/features/search/components/filters/inputs/FilterFieldWrapper.tsx","./src/features/search/components/filters/inputs/MultiSelectFilter.tsx","./src/features/search/components/filters/inputs/NumericRangeFilter.tsx","./src/features/search/components/filters/inputs/SelectFilter.tsx","./src/features/search/components/filters/inputs/TextFilter.tsx","./src/features/search/components/results/CmsResultRow.tsx","./src/features/search/components/results/DefaultResultRow.tsx","./src/features/search/components/results/resolveResultRenderer.ts","./src/features/search/components/results/__tests__/CmsResultRow.test.tsx","./src/features/search/components/results/__tests__/resolveResultRenderer.test.ts","./src/features/search/hooks/useAsyncData.ts","./src/features/search/hooks/useDistinctValues.ts","./src/features/search/hooks/useSearch.ts","./src/features/search/utils/debounce.ts","./src/features/search/utils/fieldUtils.ts","./src/features/search/utils/filterUtils.ts","./src/features/search/utils/sortUtils.ts","./src/hooks/useAsyncData.ts","./src/lib/utils.ts","./src/pages/AccountObjectDetailPage.tsx","./src/pages/Home.tsx","./src/pages/NotFound.tsx","./src/types/conversation.ts","./src/types/globals.d.ts","./src/utils/accountFields.ts","./e2e/app.spec.ts","./vite-env.d.ts","./vitest-env.d.ts","./vitest.setup.ts","../../../../../../../../../sdk/platform-sdk/types/env.d.ts"],"version":"5.9.3"}
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "11.55.1",
3
+ "version": "11.56.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
9
- "version": "11.55.1",
9
+ "version": "11.56.0",
10
10
  "license": "SEE LICENSE IN LICENSE.txt",
11
11
  "dependencies": {
12
12
  "fast-xml-parser": "^5.9.3",
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-base-sfdx-project",
3
- "version": "11.55.1",
3
+ "version": "11.56.0",
4
4
  "description": "Base SFDX project template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "publishConfig": {
package/package.json CHANGED
@@ -1,13 +1,15 @@
1
1
  {
2
2
  "name": "@salesforce/ui-bundle-template-app-react-template-b2e",
3
- "version": "11.55.1",
3
+ "version": "11.56.0",
4
4
  "description": "Salesforce React internal app template",
5
5
  "license": "SEE LICENSE IN LICENSE.txt",
6
6
  "author": "",
7
7
  "type": "module",
8
8
  "main": "index.js",
9
9
  "files": [
10
- "dist"
10
+ "dist",
11
+ "!**/__tests__",
12
+ "!**/*.test.*"
11
13
  ],
12
14
  "publishConfig": {
13
15
  "access": "public"
@@ -1,25 +0,0 @@
1
- import { Search, config } from "../features/search";
2
-
3
- /**
4
- * Account search page.
5
- *
6
- * Renders the config-driven `<Search>` feature (feature-react-search) locked to
7
- * the Account source via `restrictTo`. The shared `config.json` already defines
8
- * the Account source's searchable fields, filters, sort options, and
9
- * `routePattern` ("/accounts/:id"), so result rows link to the Account detail
10
- * page and the filter / sort chrome render automatically — no per-page wiring.
11
- *
12
- * `restrictTo` hides the scope selector and fetches only Accounts, so the page
13
- * reads as a single-object search even though the underlying feature supports
14
- * many sources.
15
- */
16
- export default function AccountSearch() {
17
- return (
18
- <Search
19
- config={config}
20
- title="Search Accounts"
21
- searchPlaceholder="Search by name, phone, or industry..."
22
- restrictTo={{ kind: "sobject", key: "accounts" }}
23
- />
24
- );
25
- }