@salesforce/webapp-template-app-react-template-b2x-experimental 1.76.1 → 1.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CHANGELOG.md +8 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/package.json +3 -3
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoGraphQLService.ts +108 -156
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/api/objectInfoService.ts +5 -10
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/components/detail/UiApiDetailForm.tsx +2 -2
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/hooks/useObjectInfoBatch.ts +1 -1
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/filters/picklist.ts +5 -31
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/objectInfo/objectInfo.ts +46 -163
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/types/schema.d.ts +200 -0
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/graphQLObjectInfoAdapter.ts +37 -279
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/features/global-search/utils/recordUtils.ts +4 -4
- package/dist/force-app/main/default/webapplications/appreacttemplateb2x/src/index.ts +0 -5
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/dist/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.77.0](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.76.1...v1.77.0) (2026-03-06)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## [1.76.1](https://github.com/salesforce-experience-platform-emu/webapps/compare/v1.76.0...v1.76.1) (2026-03-06)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @salesforce/webapp-template-base-sfdx-project-experimental
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
"graphql:schema": "node scripts/get-graphql-schema.mjs"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@salesforce/sdk-data": "^1.
|
|
19
|
-
"@salesforce/webapp-experimental": "^1.
|
|
18
|
+
"@salesforce/sdk-data": "^1.77.0",
|
|
19
|
+
"@salesforce/webapp-experimental": "^1.77.0",
|
|
20
20
|
"@tailwindcss/vite": "^4.1.17",
|
|
21
21
|
"@tanstack/react-form": "^1.28.4",
|
|
22
22
|
"class-variance-authority": "^0.7.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"@graphql-eslint/eslint-plugin": "^4.1.0",
|
|
41
41
|
"@graphql-tools/utils": "^11.0.0",
|
|
42
42
|
"@playwright/test": "^1.49.0",
|
|
43
|
-
"@salesforce/vite-plugin-webapp-experimental": "^1.
|
|
43
|
+
"@salesforce/vite-plugin-webapp-experimental": "^1.77.0",
|
|
44
44
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
45
|
"@testing-library/react": "^16.1.0",
|
|
46
46
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -7,179 +7,131 @@
|
|
|
7
7
|
* @module api/objectInfoGraphQLService
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { getDataSDK } from "@salesforce/sdk-data";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/** Raw GraphQL response shape for uiapi.objectInfos (flexible for schema casing). */
|
|
19
|
-
export interface ObjectInfosGraphQLResponse {
|
|
20
|
-
uiapi?: {
|
|
21
|
-
objectInfos?: Array<Record<string, unknown>>;
|
|
22
|
-
};
|
|
23
|
-
}
|
|
10
|
+
import { getDataSDK, gql } from "@salesforce/sdk-data";
|
|
11
|
+
import type {
|
|
12
|
+
GetObjectInfosQuery,
|
|
13
|
+
GetObjectInfosQueryVariables,
|
|
14
|
+
GetPicklistValuesQuery,
|
|
15
|
+
GetPicklistValuesQueryVariables,
|
|
16
|
+
ObjectInfoInput,
|
|
17
|
+
} from "../types/schema";
|
|
24
18
|
|
|
25
19
|
/**
|
|
26
20
|
* Builds objectInfos query (metadata only). Uses apiNames only — do not pass objectInfoInputs.
|
|
27
21
|
*/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
|
|
22
|
+
const OBJECT_INFOS_QUERY = gql`
|
|
23
|
+
query GetObjectInfos($apiNames: [String!]!) {
|
|
24
|
+
uiapi {
|
|
25
|
+
objectInfos(apiNames: $apiNames) {
|
|
26
|
+
ApiName
|
|
27
|
+
label
|
|
28
|
+
labelPlural
|
|
29
|
+
nameFields
|
|
30
|
+
defaultRecordTypeId
|
|
31
|
+
keyPrefix
|
|
32
|
+
layoutable
|
|
33
|
+
queryable
|
|
34
|
+
searchable
|
|
35
|
+
updateable
|
|
36
|
+
deletable
|
|
37
|
+
createable
|
|
38
|
+
custom
|
|
39
|
+
mruEnabled
|
|
40
|
+
feedEnabled
|
|
41
|
+
fields {
|
|
42
|
+
ApiName
|
|
43
|
+
label
|
|
44
|
+
dataType
|
|
45
|
+
relationshipName
|
|
46
|
+
reference
|
|
47
|
+
compound
|
|
48
|
+
compoundFieldName
|
|
49
|
+
compoundComponentName
|
|
50
|
+
controllingFields
|
|
51
|
+
controllerName
|
|
52
|
+
referenceToInfos {
|
|
53
|
+
ApiName
|
|
54
|
+
nameFields
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
recordTypeInfos {
|
|
58
|
+
recordTypeId
|
|
59
|
+
name
|
|
60
|
+
master
|
|
61
|
+
available
|
|
62
|
+
defaultRecordTypeMapping
|
|
63
|
+
}
|
|
64
|
+
themeInfo {
|
|
65
|
+
color
|
|
66
|
+
iconUrl
|
|
67
|
+
}
|
|
68
|
+
childRelationships {
|
|
69
|
+
relationshipName
|
|
70
|
+
fieldName
|
|
71
|
+
childObjectApiName
|
|
72
|
+
}
|
|
73
|
+
dependentFields {
|
|
74
|
+
controllingField
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
86
80
|
|
|
87
81
|
/**
|
|
88
82
|
* Builds objectInfos query with picklist values (API v65.0+).
|
|
89
83
|
* Schema requires objectInfos to be called with either apiNames or objectInfoInputs, not both.
|
|
90
84
|
* This query uses objectInfoInputs only.
|
|
85
|
+
* Optimized to only fetch fields used by extractPicklistValuesFromGraphQLObjectInfo.
|
|
91
86
|
*/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
value
|
|
131
|
-
}
|
|
132
|
-
picklistValues {
|
|
133
|
-
label
|
|
134
|
-
value
|
|
135
|
-
validFor
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
recordTypeInfos {
|
|
141
|
-
recordTypeId
|
|
142
|
-
name
|
|
143
|
-
master
|
|
144
|
-
available
|
|
145
|
-
defaultRecordTypeMapping
|
|
146
|
-
}
|
|
147
|
-
themeInfo {
|
|
148
|
-
color
|
|
149
|
-
iconUrl
|
|
150
|
-
}
|
|
151
|
-
childRelationships {
|
|
152
|
-
relationshipName
|
|
153
|
-
fieldName
|
|
154
|
-
childObjectApiName
|
|
155
|
-
}
|
|
156
|
-
dependentFields {
|
|
157
|
-
controllingField
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}`;
|
|
87
|
+
const PICKLIST_VALUES_QUERY = gql`
|
|
88
|
+
query GetPicklistValues($objectInfoInputs: [ObjectInfoInput!]!) {
|
|
89
|
+
uiapi {
|
|
90
|
+
objectInfos(objectInfoInputs: $objectInfoInputs) {
|
|
91
|
+
ApiName
|
|
92
|
+
fields {
|
|
93
|
+
ApiName
|
|
94
|
+
... on PicklistField {
|
|
95
|
+
picklistValuesByRecordTypeIDs {
|
|
96
|
+
recordTypeID
|
|
97
|
+
defaultValue {
|
|
98
|
+
value
|
|
99
|
+
}
|
|
100
|
+
picklistValues {
|
|
101
|
+
label
|
|
102
|
+
value
|
|
103
|
+
validFor
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
`;
|
|
112
|
+
|
|
113
|
+
export async function queryForObjectInfos(apiNames: string[]): Promise<GetObjectInfosQuery> {
|
|
114
|
+
return runQuery<GetObjectInfosQuery, GetObjectInfosQueryVariables>(OBJECT_INFOS_QUERY, {
|
|
115
|
+
apiNames,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function queryForPicklistValues(
|
|
120
|
+
objectInfoInputs: ObjectInfoInput[],
|
|
121
|
+
): Promise<GetPicklistValuesQuery> {
|
|
122
|
+
return runQuery<GetPicklistValuesQuery, GetPicklistValuesQueryVariables>(PICKLIST_VALUES_QUERY, {
|
|
123
|
+
objectInfoInputs,
|
|
124
|
+
});
|
|
162
125
|
}
|
|
163
126
|
|
|
164
|
-
|
|
165
|
-
apiNames: string[],
|
|
166
|
-
options?: {
|
|
167
|
-
objectInfoInputs?: ObjectInfoInput[] | null;
|
|
168
|
-
},
|
|
169
|
-
): Promise<ObjectInfosGraphQLResponse> {
|
|
170
|
-
const names = apiNames.length ? apiNames : [];
|
|
171
|
-
const hasInputs = options?.objectInfoInputs != null && options.objectInfoInputs.length > 0;
|
|
172
|
-
const query = hasInputs ? buildObjectInfosWithPicklistsQuery() : buildObjectInfosQuery();
|
|
173
|
-
const variables: Record<string, unknown> = hasInputs
|
|
174
|
-
? { objectInfoInputs: options!.objectInfoInputs }
|
|
175
|
-
: { apiNames: names };
|
|
127
|
+
async function runQuery<Q, V>(query: string, variables: V): Promise<Q> {
|
|
176
128
|
const data = await getDataSDK();
|
|
177
|
-
const response = await data.graphql?.<
|
|
129
|
+
const response = await data.graphql?.<Q, V>(query, variables);
|
|
178
130
|
|
|
179
131
|
if (response?.errors?.length) {
|
|
180
132
|
const errorMessages = response.errors.map((e) => e.message).join("; ");
|
|
181
133
|
throw new Error(`GraphQL Error: ${errorMessages}`);
|
|
182
134
|
}
|
|
183
135
|
|
|
184
|
-
return response?.data ?? ({} as
|
|
136
|
+
return response?.data ?? ({} as Q);
|
|
185
137
|
}
|
|
@@ -4,7 +4,7 @@ import { FilterArraySchema } from "../types/filters/filters";
|
|
|
4
4
|
import type { PicklistValue } from "../types/filters/picklist";
|
|
5
5
|
import type { ObjectInfoBatchResponse } from "../types/objectInfo/objectInfo";
|
|
6
6
|
import { fetchAndValidate, safeEncodePath } from "../utils/apiUtils";
|
|
7
|
-
import {
|
|
7
|
+
import { queryForObjectInfos, queryForPicklistValues } from "./objectInfoGraphQLService";
|
|
8
8
|
import {
|
|
9
9
|
graphQLObjectInfosToBatchResponse,
|
|
10
10
|
extractPicklistValuesFromGraphQLObjectInfo,
|
|
@@ -47,7 +47,7 @@ export async function getObjectInfoBatch(objectApiNames: string): Promise<Object
|
|
|
47
47
|
if (inFlight) return inFlight;
|
|
48
48
|
const promise = (async () => {
|
|
49
49
|
try {
|
|
50
|
-
const response = await
|
|
50
|
+
const response = await queryForObjectInfos(names);
|
|
51
51
|
const nodes = response?.uiapi?.objectInfos ?? [];
|
|
52
52
|
const result = graphQLObjectInfosToBatchResponse(nodes, names);
|
|
53
53
|
objectInfoBatchCache.set(key, result);
|
|
@@ -79,14 +79,9 @@ export async function getPicklistValues(
|
|
|
79
79
|
fieldName: string,
|
|
80
80
|
recordTypeId: string = "012000000000000AAA",
|
|
81
81
|
): Promise<PicklistValue[]> {
|
|
82
|
-
const response = await
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
apiName: objectApiName,
|
|
86
|
-
fieldNames: [fieldName],
|
|
87
|
-
},
|
|
88
|
-
],
|
|
89
|
-
});
|
|
82
|
+
const response = await queryForPicklistValues([
|
|
83
|
+
{ apiName: objectApiName, fieldNames: [fieldName] },
|
|
84
|
+
]);
|
|
90
85
|
const nodes = response?.uiapi?.objectInfos ?? [];
|
|
91
86
|
const node = nodes[0];
|
|
92
87
|
if (!node) return [];
|
|
@@ -75,7 +75,7 @@ export function UiApiDetailForm({
|
|
|
75
75
|
|
|
76
76
|
const objectInfo: ObjectInfo | null = useMemo(() => {
|
|
77
77
|
if (!metadata?.fields) return null;
|
|
78
|
-
const apiName =
|
|
78
|
+
const apiName = metadata.ApiName;
|
|
79
79
|
return {
|
|
80
80
|
apiName,
|
|
81
81
|
fields: Object.fromEntries(
|
|
@@ -83,7 +83,7 @@ export function UiApiDetailForm({
|
|
|
83
83
|
name,
|
|
84
84
|
{
|
|
85
85
|
compoundFieldName: f.compoundFieldName ?? undefined,
|
|
86
|
-
dataType: f.dataType,
|
|
86
|
+
dataType: f.dataType ?? "",
|
|
87
87
|
},
|
|
88
88
|
]),
|
|
89
89
|
),
|
|
@@ -44,7 +44,7 @@ export function useObjectInfoBatch(objectApiNames: string[]): UseObjectInfoBatch
|
|
|
44
44
|
.then((res) => {
|
|
45
45
|
if (isCancelled.current) return;
|
|
46
46
|
const objectInfos = names
|
|
47
|
-
.map((apiName) => res.results?.find((r) => r.result?.
|
|
47
|
+
.map((apiName) => res.results?.find((r) => r.result?.ApiName === apiName)?.result)
|
|
48
48
|
.filter((r) => r != null) as ObjectInfoResult[];
|
|
49
49
|
setState({ objectInfos, loading: false, error: null });
|
|
50
50
|
})
|
|
@@ -1,32 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { PicklistValue as GraphQLPicklistValue } from "../objectInfo/objectInfo";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// Zod Schema for Picklist Value
|
|
8
|
-
const PicklistValueSchema = z
|
|
9
|
-
.object({
|
|
10
|
-
// 1. Cleanup: Attributes are usually an object, not just 'unknown'
|
|
11
|
-
attributes: z.record(z.string(), z.unknown()).nullish(),
|
|
12
|
-
label: z.string(),
|
|
13
|
-
|
|
14
|
-
// 2. Precise Typing: 'validFor' is an array of indices (numbers)
|
|
15
|
-
// pointing to the controlling field's values.
|
|
16
|
-
validFor: z.array(z.number()).nullish(),
|
|
17
|
-
value: z.string(),
|
|
18
|
-
|
|
19
|
-
// 3. Usability: Added common API fields that are useful for UI logic
|
|
20
|
-
// (marked optional in case the specific API version omits them)
|
|
21
|
-
defaultValue: z.boolean().nullish(),
|
|
22
|
-
active: z.boolean().nullish(),
|
|
23
|
-
})
|
|
24
|
-
.passthrough();
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Single picklist value from the API
|
|
28
|
-
*/
|
|
29
|
-
export type PicklistValue = z.infer<typeof PicklistValueSchema>;
|
|
30
|
-
|
|
31
|
-
// Export schema for validation
|
|
32
|
-
export const PicklistValueArraySchema = z.array(PicklistValueSchema);
|
|
3
|
+
export type PicklistValue = GraphQLPicklistValue & {
|
|
4
|
+
value: NonNullable<GraphQLPicklistValue["value"]>;
|
|
5
|
+
label: NonNullable<GraphQLPicklistValue["label"]>;
|
|
6
|
+
};
|
|
@@ -1,166 +1,49 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
nameFields: z.array(z.string()),
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// Zod Schema for Filtered Lookup Info (can be null or object)
|
|
23
|
-
const FilteredLookupInfoSchema = z.record(z.string(), z.unknown()).nullable();
|
|
24
|
-
|
|
25
|
-
// Zod Schema for Field Definition
|
|
26
|
-
// Using passthrough to allow extra fields that might be present in the API response
|
|
27
|
-
const FieldSchema = z
|
|
28
|
-
.object({
|
|
29
|
-
apiName: z.string(),
|
|
30
|
-
calculated: z.boolean(),
|
|
31
|
-
compound: z.boolean(),
|
|
32
|
-
compoundComponentName: z.string().nullable(),
|
|
33
|
-
compoundFieldName: z.string().nullable(),
|
|
34
|
-
controllerName: z.string().nullable(),
|
|
35
|
-
controllingFields: z.array(z.string()),
|
|
36
|
-
createable: z.boolean(),
|
|
37
|
-
custom: z.boolean(),
|
|
38
|
-
dataType: z.string(),
|
|
39
|
-
defaultValue: z.unknown().nullable(),
|
|
40
|
-
defaultedOnCreate: z.boolean(),
|
|
41
|
-
digits: z.number(),
|
|
42
|
-
externalId: z.boolean(),
|
|
43
|
-
extraTypeInfo: z.string().nullable(),
|
|
44
|
-
filterable: z.boolean(),
|
|
45
|
-
filteredLookupInfo: FilteredLookupInfoSchema,
|
|
46
|
-
highScaleNumber: z.boolean(),
|
|
47
|
-
htmlFormatted: z.boolean(),
|
|
48
|
-
inlineHelpText: z.string().nullable(),
|
|
49
|
-
label: z.string(),
|
|
50
|
-
length: z.number(),
|
|
51
|
-
maskType: z.string().nullable(),
|
|
52
|
-
nameField: z.boolean(),
|
|
53
|
-
polymorphicForeignKey: z.boolean(),
|
|
54
|
-
precision: z.number(),
|
|
55
|
-
reference: z.boolean(),
|
|
56
|
-
referenceTargetField: z.string().nullable(),
|
|
57
|
-
referenceToInfos: z.array(ReferenceToInfoSchema),
|
|
58
|
-
relationshipName: z.string().nullable(),
|
|
59
|
-
required: z.boolean(),
|
|
60
|
-
scale: z.number(),
|
|
61
|
-
searchPrefilterable: z.boolean(),
|
|
62
|
-
sortable: z.boolean(),
|
|
63
|
-
unique: z.boolean(),
|
|
64
|
-
updateable: z.boolean(),
|
|
65
|
-
})
|
|
66
|
-
.passthrough();
|
|
67
|
-
|
|
68
|
-
// Zod Schema for Record Type Info
|
|
69
|
-
// Using passthrough to allow extra fields that might be present in the API response
|
|
70
|
-
const RecordTypeInfoSchema = z
|
|
71
|
-
.object({
|
|
72
|
-
available: z.boolean(),
|
|
73
|
-
defaultRecordTypeMapping: z.boolean(),
|
|
74
|
-
master: z.boolean(),
|
|
75
|
-
name: z.string(),
|
|
76
|
-
recordTypeId: z.string(),
|
|
77
|
-
})
|
|
78
|
-
.passthrough();
|
|
79
|
-
|
|
80
|
-
// Zod Schema for Theme Info
|
|
81
|
-
const ThemeInfoSchema = z.object({
|
|
82
|
-
color: z.string(),
|
|
83
|
-
iconUrl: z.string(),
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// Zod Schema for Object Info Result
|
|
87
|
-
// Using passthrough to allow extra fields and using FieldSchema/RecordTypeInfoSchema with passthrough
|
|
88
|
-
const ObjectInfoResultSchema = z
|
|
89
|
-
.object({
|
|
90
|
-
apiName: z.string(),
|
|
91
|
-
associateEntityType: z.string().nullable(),
|
|
92
|
-
associateParentEntity: z.string().nullable(),
|
|
93
|
-
childRelationships: z.array(ChildRelationshipSchema),
|
|
94
|
-
compactLayoutable: z.boolean(),
|
|
95
|
-
createable: z.boolean(),
|
|
96
|
-
custom: z.boolean(),
|
|
97
|
-
defaultRecordTypeId: z.string(),
|
|
98
|
-
deletable: z.boolean(),
|
|
99
|
-
dependentFields: z.record(z.string(), z.unknown()),
|
|
100
|
-
eTag: z.string(),
|
|
101
|
-
feedEnabled: z.boolean(),
|
|
102
|
-
// Avoid using FieldSchema because of performance concerns with validating the high number of fields returned from the Salesforce API and causing the UI to freeze.
|
|
103
|
-
fields: z.record(z.string(), z.any()),
|
|
104
|
-
keyPrefix: z.string(),
|
|
105
|
-
label: z.string(),
|
|
106
|
-
labelPlural: z.string(),
|
|
107
|
-
layoutable: z.boolean(),
|
|
108
|
-
mruEnabled: z.boolean(),
|
|
109
|
-
nameFields: z.array(z.string()),
|
|
110
|
-
queryable: z.boolean(),
|
|
111
|
-
// Avoid using RecordTypeInfoSchema because of performance concerns with validating the high number of fields returned from the Salesforce API and causing the UI to freeze.
|
|
112
|
-
recordTypeInfos: z.record(z.string(), z.any()),
|
|
113
|
-
searchLayoutable: z.boolean(),
|
|
114
|
-
searchable: z.boolean(),
|
|
115
|
-
themeInfo: ThemeInfoSchema,
|
|
116
|
-
updateable: z.boolean(),
|
|
117
|
-
})
|
|
118
|
-
.passthrough();
|
|
119
|
-
|
|
120
|
-
// Zod Schema for Object Info Batch Result Item
|
|
121
|
-
const ObjectInfoBatchResultItemSchema = z.object({
|
|
122
|
-
result: ObjectInfoResultSchema,
|
|
123
|
-
statusCode: z.number(),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Zod Schema for Object Info Batch Response (array of items)
|
|
127
|
-
export const ObjectInfoBatchResponseSchema = z.object({
|
|
128
|
-
results: z.array(ObjectInfoBatchResultItemSchema),
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// TypeScript Types (inferred from Zod schemas)
|
|
132
|
-
export type ChildRelationship = z.infer<typeof ChildRelationshipSchema>;
|
|
133
|
-
export type ReferenceToInfo = z.infer<typeof ReferenceToInfoSchema>;
|
|
134
|
-
export type FilteredLookupInfo = z.infer<typeof FilteredLookupInfoSchema>;
|
|
135
|
-
export type Field = z.infer<typeof FieldSchema>;
|
|
136
|
-
export type RecordTypeInfo = z.infer<typeof RecordTypeInfoSchema>;
|
|
137
|
-
export type ThemeInfo = z.infer<typeof ThemeInfoSchema>;
|
|
138
|
-
export type ObjectInfoBatchResponse = z.infer<typeof ObjectInfoBatchResponseSchema>;
|
|
139
|
-
// Type Patching: Overwriting the "any" from the performance-optimized schema
|
|
140
|
-
// with the strict types defined above. This ensures Developers get strict typing
|
|
141
|
-
// even though the Runtime Validator skips the deep check.
|
|
142
|
-
export type ObjectInfoResult = Omit<
|
|
143
|
-
z.infer<typeof ObjectInfoResultSchema>,
|
|
144
|
-
"fields" | "recordTypeInfos"
|
|
145
|
-
> & {
|
|
146
|
-
fields: Record<string, Field>;
|
|
147
|
-
recordTypeInfos: Record<string, RecordTypeInfo>;
|
|
1
|
+
import type { GetObjectInfosQuery, GetPicklistValuesQuery } from "../schema";
|
|
2
|
+
|
|
3
|
+
// Generic utility types for extracting array item types
|
|
4
|
+
type ArrayItem<T> = T extends (infer Item)[] ? Item : never;
|
|
5
|
+
type NonNullableArrayItem<T> = NonNullable<ArrayItem<NonNullable<T>>>;
|
|
6
|
+
|
|
7
|
+
// ObjectInfos extraction
|
|
8
|
+
export type GetObjectInfosQueryObjectInfos = NonNullable<
|
|
9
|
+
GetObjectInfosQuery["uiapi"]["objectInfos"]
|
|
10
|
+
>;
|
|
11
|
+
export type GetObjectInfosQueryObjectInfo = NonNullableArrayItem<GetObjectInfosQueryObjectInfos>;
|
|
12
|
+
export type GetObjectInfosQueryField = NonNullableArrayItem<
|
|
13
|
+
GetObjectInfosQueryObjectInfo["fields"]
|
|
14
|
+
>;
|
|
15
|
+
|
|
16
|
+
// ObjectInfoResult types
|
|
17
|
+
export type ObjectInfoResult = Omit<GetObjectInfosQueryObjectInfo, "fields"> & {
|
|
18
|
+
fields: Record<string, GetObjectInfosQueryField>;
|
|
148
19
|
};
|
|
149
|
-
export type
|
|
150
|
-
|
|
151
|
-
"result"
|
|
152
|
-
> & {
|
|
153
|
-
result: ObjectInfoResult;
|
|
20
|
+
export type ObjectInfoBatchResponse = {
|
|
21
|
+
results: { result: ObjectInfoResult; statusCode: number }[];
|
|
154
22
|
};
|
|
155
23
|
|
|
156
|
-
//
|
|
157
|
-
export
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
24
|
+
// Picklist values extraction
|
|
25
|
+
export type GetPicklistValuesQueryObjectInfos = GetPicklistValuesQuery["uiapi"]["objectInfos"];
|
|
26
|
+
export type GetPicklistValuesQueryObjectInfo =
|
|
27
|
+
NonNullableArrayItem<GetPicklistValuesQueryObjectInfos>;
|
|
28
|
+
export type GetPicklistValuesQueryField = NonNullableArrayItem<
|
|
29
|
+
GetPicklistValuesQueryObjectInfo["fields"]
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
// Extract picklist-specific field type (the one with picklistValuesByRecordTypeIDs)
|
|
33
|
+
type GetPicklistValuesQueryPicklistField = Extract<
|
|
34
|
+
GetPicklistValuesQueryField,
|
|
35
|
+
{ picklistValuesByRecordTypeIDs?: unknown }
|
|
36
|
+
>;
|
|
37
|
+
|
|
38
|
+
// Extract types from picklistValuesByRecordTypeIDs
|
|
39
|
+
type PicklistValuesByRecordTypeIDs =
|
|
40
|
+
GetPicklistValuesQueryPicklistField["picklistValuesByRecordTypeIDs"];
|
|
41
|
+
type PicklistValuesByRecordTypeID = NonNullable<
|
|
42
|
+
NonNullable<PicklistValuesByRecordTypeIDs> extends (infer Item)[] ? Item : null
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
// Extract individual picklist value type
|
|
46
|
+
type PicklistValues = NonNullable<PicklistValuesByRecordTypeID>["picklistValues"];
|
|
47
|
+
export type PicklistValue = NonNullable<
|
|
48
|
+
NonNullable<PicklistValues> extends (infer Item)[] ? Item : null
|
|
49
|
+
>;
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
export type Maybe<T> = T | null;
|
|
2
|
+
export type InputMaybe<T> = Maybe<T>;
|
|
3
|
+
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
|
|
4
|
+
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
|
|
5
|
+
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
|
|
6
|
+
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = {
|
|
7
|
+
[_ in K]?: never;
|
|
8
|
+
};
|
|
9
|
+
export type Incremental<T> =
|
|
10
|
+
| T
|
|
11
|
+
| { [P in keyof T]?: P extends " $fragmentName" | "__typename" ? T[P] : never };
|
|
12
|
+
/** All built-in and custom scalars, mapped to their actual values */
|
|
13
|
+
export type Scalars = {
|
|
14
|
+
ID: { input: string; output: string };
|
|
15
|
+
String: { input: string; output: string };
|
|
16
|
+
Boolean: { input: boolean; output: boolean };
|
|
17
|
+
Int: { input: number; output: number };
|
|
18
|
+
Float: { input: number; output: number };
|
|
19
|
+
Base64: { input: string; output: string };
|
|
20
|
+
/** An arbitrary precision signed decimal */
|
|
21
|
+
BigDecimal: { input: number | string; output: number };
|
|
22
|
+
/** An arbitrary precision signed integer */
|
|
23
|
+
BigInteger: { input: number; output: number };
|
|
24
|
+
/** An 8-bit signed integer */
|
|
25
|
+
Byte: { input: number; output: number };
|
|
26
|
+
/** A UTF-16 code unit; a character on Unicode's BMP */
|
|
27
|
+
Char: { input: number; output: number };
|
|
28
|
+
Currency: { input: number | string; output: number };
|
|
29
|
+
Date: { input: string; output: string };
|
|
30
|
+
DateTime: { input: string; output: string };
|
|
31
|
+
Double: { input: number | string; output: number };
|
|
32
|
+
Email: { input: string; output: string };
|
|
33
|
+
EncryptedString: { input: string; output: string };
|
|
34
|
+
/** Can be set to an ID or a Reference to the result of another mutation operation. */
|
|
35
|
+
IdOrRef: { input: string; output: string };
|
|
36
|
+
JSON: { input: string; output: string };
|
|
37
|
+
Latitude: { input: number | string; output: number };
|
|
38
|
+
/** A 64-bit signed integer */
|
|
39
|
+
Long: { input: number; output: number };
|
|
40
|
+
LongTextArea: { input: string; output: string };
|
|
41
|
+
Longitude: { input: number | string; output: number };
|
|
42
|
+
MultiPicklist: { input: string; output: string };
|
|
43
|
+
Percent: { input: number | string; output: number };
|
|
44
|
+
PhoneNumber: { input: string; output: string };
|
|
45
|
+
Picklist: { input: string; output: string };
|
|
46
|
+
RichTextArea: { input: string; output: string };
|
|
47
|
+
/** A 16-bit signed integer */
|
|
48
|
+
Short: { input: number; output: number };
|
|
49
|
+
TextArea: { input: string; output: string };
|
|
50
|
+
Time: { input: string; output: string };
|
|
51
|
+
Url: { input: string; output: string };
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export enum DataType {
|
|
55
|
+
Address = "ADDRESS",
|
|
56
|
+
Anytype = "ANYTYPE",
|
|
57
|
+
Base64 = "BASE64",
|
|
58
|
+
Boolean = "BOOLEAN",
|
|
59
|
+
Combobox = "COMBOBOX",
|
|
60
|
+
Complexvalue = "COMPLEXVALUE",
|
|
61
|
+
Currency = "CURRENCY",
|
|
62
|
+
Date = "DATE",
|
|
63
|
+
Datetime = "DATETIME",
|
|
64
|
+
Double = "DOUBLE",
|
|
65
|
+
Email = "EMAIL",
|
|
66
|
+
Encryptedstring = "ENCRYPTEDSTRING",
|
|
67
|
+
Int = "INT",
|
|
68
|
+
Json = "JSON",
|
|
69
|
+
Junctionidlist = "JUNCTIONIDLIST",
|
|
70
|
+
Location = "LOCATION",
|
|
71
|
+
Long = "LONG",
|
|
72
|
+
Multipicklist = "MULTIPICKLIST",
|
|
73
|
+
Percent = "PERCENT",
|
|
74
|
+
Phone = "PHONE",
|
|
75
|
+
Picklist = "PICKLIST",
|
|
76
|
+
Reference = "REFERENCE",
|
|
77
|
+
String = "STRING",
|
|
78
|
+
Textarea = "TEXTAREA",
|
|
79
|
+
Time = "TIME",
|
|
80
|
+
Url = "URL",
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export enum FieldExtraTypeInfo {
|
|
84
|
+
ExternalLookup = "EXTERNAL_LOOKUP",
|
|
85
|
+
ImageUrl = "IMAGE_URL",
|
|
86
|
+
IndirectLookup = "INDIRECT_LOOKUP",
|
|
87
|
+
Personname = "PERSONNAME",
|
|
88
|
+
Plaintextarea = "PLAINTEXTAREA",
|
|
89
|
+
Richtextarea = "RICHTEXTAREA",
|
|
90
|
+
SwitchablePersonname = "SWITCHABLE_PERSONNAME",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Input for ObjectInfo and PickValues */
|
|
94
|
+
export type ObjectInfoInput = {
|
|
95
|
+
apiName: Scalars["String"]["input"];
|
|
96
|
+
fieldNames?: InputMaybe<Array<Scalars["String"]["input"]>>;
|
|
97
|
+
recordTypeIDs?: InputMaybe<Array<Scalars["ID"]["input"]>>;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export enum ResultOrder {
|
|
101
|
+
Asc = "ASC",
|
|
102
|
+
Desc = "DESC",
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export type GetObjectInfosQueryVariables = Exact<{
|
|
106
|
+
apiNames: Array<Scalars["String"]["input"]> | Scalars["String"]["input"];
|
|
107
|
+
}>;
|
|
108
|
+
|
|
109
|
+
export type GetObjectInfosQuery = {
|
|
110
|
+
uiapi: {
|
|
111
|
+
objectInfos?: Array<{
|
|
112
|
+
ApiName: string;
|
|
113
|
+
label?: string | null;
|
|
114
|
+
labelPlural?: string | null;
|
|
115
|
+
nameFields: Array<string | null>;
|
|
116
|
+
defaultRecordTypeId?: string | null;
|
|
117
|
+
keyPrefix?: string | null;
|
|
118
|
+
layoutable: boolean;
|
|
119
|
+
queryable: boolean;
|
|
120
|
+
searchable: boolean;
|
|
121
|
+
updateable: boolean;
|
|
122
|
+
deletable: boolean;
|
|
123
|
+
createable: boolean;
|
|
124
|
+
custom: boolean;
|
|
125
|
+
mruEnabled: boolean;
|
|
126
|
+
feedEnabled: boolean;
|
|
127
|
+
fields: Array<
|
|
128
|
+
| {
|
|
129
|
+
ApiName: string;
|
|
130
|
+
label?: string | null;
|
|
131
|
+
dataType?: DataType | null;
|
|
132
|
+
relationshipName?: string | null;
|
|
133
|
+
reference: boolean;
|
|
134
|
+
compound: boolean;
|
|
135
|
+
compoundFieldName?: string | null;
|
|
136
|
+
compoundComponentName?: string | null;
|
|
137
|
+
controllingFields: Array<string | null>;
|
|
138
|
+
controllerName?: string | null;
|
|
139
|
+
referenceToInfos: Array<{ ApiName: string; nameFields: Array<string | null> } | null>;
|
|
140
|
+
}
|
|
141
|
+
| {
|
|
142
|
+
ApiName: string;
|
|
143
|
+
label?: string | null;
|
|
144
|
+
dataType?: DataType | null;
|
|
145
|
+
relationshipName?: string | null;
|
|
146
|
+
reference: boolean;
|
|
147
|
+
compound: boolean;
|
|
148
|
+
compoundFieldName?: string | null;
|
|
149
|
+
compoundComponentName?: string | null;
|
|
150
|
+
controllingFields: Array<string | null>;
|
|
151
|
+
controllerName?: string | null;
|
|
152
|
+
referenceToInfos: Array<{ ApiName: string; nameFields: Array<string | null> } | null>;
|
|
153
|
+
}
|
|
154
|
+
| null
|
|
155
|
+
>;
|
|
156
|
+
recordTypeInfos: Array<{
|
|
157
|
+
recordTypeId?: string | null;
|
|
158
|
+
name?: string | null;
|
|
159
|
+
master: boolean;
|
|
160
|
+
available: boolean;
|
|
161
|
+
defaultRecordTypeMapping: boolean;
|
|
162
|
+
} | null>;
|
|
163
|
+
themeInfo?: { color?: string | null; iconUrl?: string | null } | null;
|
|
164
|
+
childRelationships: Array<{
|
|
165
|
+
relationshipName?: string | null;
|
|
166
|
+
fieldName?: string | null;
|
|
167
|
+
childObjectApiName: string;
|
|
168
|
+
} | null>;
|
|
169
|
+
dependentFields: Array<{ controllingField: string } | null>;
|
|
170
|
+
} | null> | null;
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export type GetPicklistValuesQueryVariables = Exact<{
|
|
175
|
+
objectInfoInputs: Array<ObjectInfoInput> | ObjectInfoInput;
|
|
176
|
+
}>;
|
|
177
|
+
|
|
178
|
+
export type GetPicklistValuesQuery = {
|
|
179
|
+
uiapi: {
|
|
180
|
+
objectInfos?: Array<{
|
|
181
|
+
ApiName: string;
|
|
182
|
+
fields: Array<
|
|
183
|
+
| {
|
|
184
|
+
ApiName: string;
|
|
185
|
+
picklistValuesByRecordTypeIDs?: Array<{
|
|
186
|
+
recordTypeID: string;
|
|
187
|
+
defaultValue?: { value?: string | null } | null;
|
|
188
|
+
picklistValues?: Array<{
|
|
189
|
+
label?: string | null;
|
|
190
|
+
value?: string | null;
|
|
191
|
+
validFor?: Array<number | null> | null;
|
|
192
|
+
}> | null;
|
|
193
|
+
} | null> | null;
|
|
194
|
+
}
|
|
195
|
+
| { ApiName: string }
|
|
196
|
+
| null
|
|
197
|
+
>;
|
|
198
|
+
} | null> | null;
|
|
199
|
+
};
|
|
200
|
+
};
|
|
@@ -10,259 +10,36 @@
|
|
|
10
10
|
|
|
11
11
|
import type {
|
|
12
12
|
ObjectInfoBatchResponse,
|
|
13
|
+
GetObjectInfosQueryObjectInfos,
|
|
14
|
+
GetPicklistValuesQueryObjectInfo,
|
|
15
|
+
GetPicklistValuesQueryField,
|
|
16
|
+
GetObjectInfosQueryObjectInfo,
|
|
13
17
|
ObjectInfoResult,
|
|
14
|
-
|
|
15
|
-
RecordTypeInfo,
|
|
16
|
-
ThemeInfo,
|
|
18
|
+
PicklistValue as GraphQLPicklistValue,
|
|
17
19
|
} from "../types/objectInfo/objectInfo";
|
|
18
20
|
import type { PicklistValue } from "../types/filters/picklist";
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function getNum(node: GraphQLNode, key: string): number {
|
|
28
|
-
const v = node[key] ?? node[key.charAt(0).toLowerCase() + key.slice(1)];
|
|
29
|
-
return typeof v === "number" ? v : 0;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getBool(node: GraphQLNode, key: string): boolean {
|
|
33
|
-
const v = node[key] ?? node[key.charAt(0).toLowerCase() + key.slice(1)];
|
|
34
|
-
return typeof v === "boolean" ? v : false;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getArr<T>(node: GraphQLNode, key: string, map: (item: unknown) => T): T[] {
|
|
38
|
-
const v = node[key] ?? node[key.charAt(0).toLowerCase() + key.slice(1)];
|
|
39
|
-
if (!Array.isArray(v)) return [];
|
|
40
|
-
return v.map((item) => map(item));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getObj(node: GraphQLNode, key: string): Record<string, unknown> {
|
|
44
|
-
const v = node[key] ?? node[key.charAt(0).toLowerCase() + key.slice(1)];
|
|
45
|
-
return v != null && typeof v === "object" && !Array.isArray(v)
|
|
46
|
-
? (v as Record<string, unknown>)
|
|
47
|
-
: {};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/** GraphQL returns dependentFields as [DependentField]; REST expects Record. Normalize to object. */
|
|
51
|
-
function toDependentFieldsRecord(v: unknown): Record<string, unknown> {
|
|
52
|
-
if (v == null) return {};
|
|
53
|
-
if (Array.isArray(v)) return {};
|
|
54
|
-
return typeof v === "object" ? (v as Record<string, unknown>) : {};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Canonical dataType casing for formatters (Phone, FormattedAddress) and reference resolution (Reference). */
|
|
58
|
-
const DATA_TYPE_CANONICAL: Record<string, string> = {
|
|
59
|
-
phone: "Phone",
|
|
60
|
-
email: "Email",
|
|
61
|
-
url: "Url",
|
|
62
|
-
address: "Address",
|
|
63
|
-
reference: "Reference",
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
function normalizeDataType(raw: string): string {
|
|
67
|
-
if (!raw) return raw;
|
|
68
|
-
const lower = raw.toLowerCase();
|
|
69
|
-
return DATA_TYPE_CANONICAL[lower] ?? raw;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** Normalize GraphQL Field to our Field type (REST-compatible). */
|
|
73
|
-
function mapField(fieldNode: unknown): [string, Field] {
|
|
74
|
-
const n = (fieldNode != null && typeof fieldNode === "object" ? fieldNode : {}) as GraphQLNode;
|
|
75
|
-
const apiName = getStr(n, "ApiName") || getStr(n, "apiName");
|
|
76
|
-
const label = getStr(n, "label");
|
|
77
|
-
const dataType = normalizeDataType(getStr(n, "dataType"));
|
|
78
|
-
const relationshipName = getStr(n, "relationshipName") || null;
|
|
79
|
-
const reference = getBool(n, "reference");
|
|
80
|
-
const compound = getBool(n, "compound");
|
|
81
|
-
const compoundFieldName = getStr(n, "compoundFieldName") || null;
|
|
82
|
-
const compoundComponentName = getStr(n, "compoundComponentName") || null;
|
|
83
|
-
const controllingFields = getArr(n, "controllingFields", String);
|
|
84
|
-
const controllerName = getStr(n, "controllerName") || null;
|
|
85
|
-
const referenceToInfos = getArr(n, "referenceToInfos", (item) => {
|
|
86
|
-
const r = (item != null && typeof item === "object" ? item : {}) as GraphQLNode;
|
|
87
|
-
return {
|
|
88
|
-
apiName: getStr(r, "apiName") || getStr(r, "ApiName"),
|
|
89
|
-
nameFields: getArr(r, "nameFields", String),
|
|
90
|
-
};
|
|
91
|
-
});
|
|
92
|
-
return [
|
|
93
|
-
apiName,
|
|
94
|
-
{
|
|
95
|
-
apiName,
|
|
96
|
-
label,
|
|
97
|
-
dataType,
|
|
98
|
-
relationshipName,
|
|
99
|
-
reference,
|
|
100
|
-
compound,
|
|
101
|
-
compoundFieldName,
|
|
102
|
-
compoundComponentName,
|
|
103
|
-
controllingFields,
|
|
104
|
-
controllerName,
|
|
105
|
-
referenceToInfos,
|
|
106
|
-
calculated: getBool(n, "calculated"),
|
|
107
|
-
createable: getBool(n, "createable"),
|
|
108
|
-
custom: getBool(n, "custom"),
|
|
109
|
-
defaultValue: n.defaultValue ?? null,
|
|
110
|
-
defaultedOnCreate: getBool(n, "defaultedOnCreate"),
|
|
111
|
-
digits: getNum(n, "digits"),
|
|
112
|
-
externalId: getBool(n, "externalId"),
|
|
113
|
-
extraTypeInfo: getStr(n, "extraTypeInfo") || null,
|
|
114
|
-
filterable: getBool(n, "filterable"),
|
|
115
|
-
filteredLookupInfo: null,
|
|
116
|
-
highScaleNumber: getBool(n, "highScaleNumber"),
|
|
117
|
-
htmlFormatted: getBool(n, "htmlFormatted"),
|
|
118
|
-
inlineHelpText: getStr(n, "inlineHelpText") || null,
|
|
119
|
-
length: getNum(n, "length"),
|
|
120
|
-
maskType: getStr(n, "maskType") || null,
|
|
121
|
-
nameField: getBool(n, "nameField"),
|
|
122
|
-
polymorphicForeignKey: getBool(n, "polymorphicForeignKey"),
|
|
123
|
-
precision: getNum(n, "precision"),
|
|
124
|
-
required: getBool(n, "required"),
|
|
125
|
-
scale: getNum(n, "scale"),
|
|
126
|
-
searchPrefilterable: getBool(n, "searchPrefilterable"),
|
|
127
|
-
sortable: getBool(n, "sortable"),
|
|
128
|
-
unique: getBool(n, "unique"),
|
|
129
|
-
updateable: getBool(n, "updateable"),
|
|
130
|
-
referenceTargetField: getStr(n, "referenceTargetField") || null,
|
|
131
|
-
} as Field,
|
|
132
|
-
];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/** Normalize GraphQL recordTypeInfo to RecordTypeInfo. */
|
|
136
|
-
function mapRecordTypeInfo(rtNode: unknown): [string, RecordTypeInfo] {
|
|
137
|
-
const n = (rtNode != null && typeof rtNode === "object" ? rtNode : {}) as GraphQLNode;
|
|
138
|
-
const recordTypeId = getStr(n, "recordTypeId");
|
|
139
|
-
return [
|
|
140
|
-
recordTypeId,
|
|
141
|
-
{
|
|
142
|
-
recordTypeId,
|
|
143
|
-
name: getStr(n, "name"),
|
|
144
|
-
master: getBool(n, "master"),
|
|
145
|
-
available: getBool(n, "available"),
|
|
146
|
-
defaultRecordTypeMapping: getBool(n, "defaultRecordTypeMapping"),
|
|
147
|
-
} as RecordTypeInfo,
|
|
148
|
-
];
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function toFieldsMap(fieldsNode: unknown): Record<string, Field> {
|
|
152
|
-
if (fieldsNode == null) return {};
|
|
153
|
-
if (Array.isArray(fieldsNode)) {
|
|
154
|
-
return Object.fromEntries((fieldsNode as unknown[]).map(mapField).filter(([k]) => k));
|
|
155
|
-
}
|
|
156
|
-
if (typeof fieldsNode === "object" && !Array.isArray(fieldsNode)) {
|
|
157
|
-
const entries = Object.entries(fieldsNode as Record<string, unknown>).map(([, v]) =>
|
|
158
|
-
mapField(v),
|
|
159
|
-
);
|
|
160
|
-
return Object.fromEntries(entries.filter(([k]) => k));
|
|
161
|
-
}
|
|
162
|
-
return {};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Ensures compound parent fields (e.g. BillingAddress, ShippingAddress) exist in the fields map
|
|
167
|
-
* with dataType "Address" so layout transform can set item.dataType for FormattedAddress.
|
|
168
|
-
* GraphQL may only return component fields (BillingStreet, etc.) with compoundFieldName.
|
|
169
|
-
*/
|
|
170
|
-
function ensureCompoundParentFields(fields: Record<string, Field>): Record<string, Field> {
|
|
171
|
-
const result = { ...fields };
|
|
172
|
-
for (const field of Object.values(fields)) {
|
|
173
|
-
const compoundName = field.compoundFieldName;
|
|
174
|
-
if (!compoundName || result[compoundName] != null) continue;
|
|
175
|
-
const dataType =
|
|
176
|
-
compoundName.endsWith("Address") || compoundName.endsWith("address") ? "Address" : "String";
|
|
177
|
-
result[compoundName] = {
|
|
178
|
-
...field,
|
|
179
|
-
apiName: compoundName,
|
|
180
|
-
label: compoundName,
|
|
181
|
-
dataType,
|
|
182
|
-
compound: true,
|
|
183
|
-
compoundFieldName: null,
|
|
184
|
-
compoundComponentName: null,
|
|
185
|
-
} as Field;
|
|
186
|
-
}
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function toRecordTypeInfosMap(rtNode: unknown): Record<string, RecordTypeInfo> {
|
|
191
|
-
if (rtNode == null) return {};
|
|
192
|
-
if (Array.isArray(rtNode)) {
|
|
193
|
-
return Object.fromEntries((rtNode as unknown[]).map(mapRecordTypeInfo).filter(([k]) => k));
|
|
194
|
-
}
|
|
195
|
-
if (typeof rtNode === "object" && !Array.isArray(rtNode)) {
|
|
196
|
-
const entries = Object.entries(rtNode as Record<string, unknown>).map(([, v]) =>
|
|
197
|
-
mapRecordTypeInfo(v),
|
|
198
|
-
);
|
|
199
|
-
return Object.fromEntries(entries.filter(([k]) => k));
|
|
200
|
-
}
|
|
201
|
-
return {};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** Map a single GraphQL ObjectInfo node to ObjectInfoResult (REST-compatible). */
|
|
205
|
-
export function graphQLObjectInfoToObjectInfoResult(node: GraphQLNode): ObjectInfoResult {
|
|
206
|
-
const themeInfoNode = getObj(node, "themeInfo");
|
|
207
|
-
const themeInfo: ThemeInfo = {
|
|
208
|
-
color: getStr(themeInfoNode, "color"),
|
|
209
|
-
iconUrl: getStr(themeInfoNode, "iconUrl"),
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
const childRelationships = getArr(node, "childRelationships", (item) => {
|
|
213
|
-
const c = (item != null && typeof item === "object" ? item : {}) as GraphQLNode;
|
|
214
|
-
return {
|
|
215
|
-
childObjectApiName: getStr(c, "childObjectApiName"),
|
|
216
|
-
fieldName: getStr(c, "fieldName"),
|
|
217
|
-
junctionIdListNames: getArr(c, "junctionIdListNames", String),
|
|
218
|
-
junctionReferenceTo: getArr(c, "junctionReferenceTo", String),
|
|
219
|
-
relationshipName: getStr(c, "relationshipName"),
|
|
220
|
-
};
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
const apiName = getStr(node, "ApiName") || getStr(node, "apiName");
|
|
224
|
-
const fieldsNode = node.fields ?? node.Fields;
|
|
225
|
-
const recordTypeInfosNode = node.recordTypeInfos ?? node.RecordTypeInfos;
|
|
226
|
-
|
|
22
|
+
export function graphQLObjectInfoToObjectInfoResult(
|
|
23
|
+
objectInfo: GetObjectInfosQueryObjectInfo,
|
|
24
|
+
): ObjectInfoResult {
|
|
25
|
+
const { fields, ...rest } = objectInfo;
|
|
26
|
+
const fieldsRecord = Object.fromEntries((fields ?? []).map((field) => [field?.ApiName, field]));
|
|
227
27
|
return {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
nameFields: getArr(node, "nameFields", String),
|
|
232
|
-
defaultRecordTypeId: getStr(node, "defaultRecordTypeId") || "012000000000000AAA",
|
|
233
|
-
keyPrefix: getStr(node, "keyPrefix"),
|
|
234
|
-
layoutable: getBool(node, "layoutable"),
|
|
235
|
-
queryable: getBool(node, "queryable"),
|
|
236
|
-
searchable: getBool(node, "searchable"),
|
|
237
|
-
updateable: getBool(node, "updateable"),
|
|
238
|
-
deletable: getBool(node, "deletable"),
|
|
239
|
-
createable: getBool(node, "createable"),
|
|
240
|
-
custom: getBool(node, "custom"),
|
|
241
|
-
mruEnabled: getBool(node, "mruEnabled"),
|
|
242
|
-
feedEnabled: getBool(node, "feedEnabled"),
|
|
243
|
-
fields: ensureCompoundParentFields(toFieldsMap(fieldsNode)),
|
|
244
|
-
recordTypeInfos: toRecordTypeInfosMap(recordTypeInfosNode),
|
|
245
|
-
themeInfo,
|
|
246
|
-
childRelationships,
|
|
247
|
-
associateEntityType: getStr(node, "associateEntityType") || null,
|
|
248
|
-
associateParentEntity: getStr(node, "associateParentEntity") || null,
|
|
249
|
-
compactLayoutable: getBool(node, "compactLayoutable"),
|
|
250
|
-
searchLayoutable: getBool(node, "searchLayoutable"),
|
|
251
|
-
dependentFields: toDependentFieldsRecord(node.dependentFields ?? node.DependentFields),
|
|
252
|
-
eTag: getStr(node, "eTag"),
|
|
253
|
-
} as ObjectInfoResult;
|
|
28
|
+
...rest,
|
|
29
|
+
fields: fieldsRecord,
|
|
30
|
+
};
|
|
254
31
|
}
|
|
255
32
|
|
|
256
33
|
/** Convert GraphQL objectInfos array to ObjectInfoBatchResponse (REST shape). */
|
|
257
34
|
export function graphQLObjectInfosToBatchResponse(
|
|
258
|
-
|
|
35
|
+
objectInfos: GetObjectInfosQueryObjectInfos,
|
|
259
36
|
_requestedApiNames: string[],
|
|
260
37
|
): ObjectInfoBatchResponse {
|
|
261
|
-
const results =
|
|
262
|
-
result: graphQLObjectInfoToObjectInfoResult(
|
|
38
|
+
const results = objectInfos.map((objectInfo) => ({
|
|
39
|
+
result: graphQLObjectInfoToObjectInfoResult(objectInfo!),
|
|
263
40
|
statusCode: 200,
|
|
264
41
|
}));
|
|
265
|
-
return { results }
|
|
42
|
+
return { results };
|
|
266
43
|
}
|
|
267
44
|
|
|
268
45
|
/**
|
|
@@ -270,50 +47,31 @@ export function graphQLObjectInfosToBatchResponse(
|
|
|
270
47
|
* Uses picklistValuesByRecordTypeIDs; prefers the given recordTypeId or first available.
|
|
271
48
|
*/
|
|
272
49
|
export function extractPicklistValuesFromGraphQLObjectInfo(
|
|
273
|
-
|
|
50
|
+
objectInfo: NonNullable<GetPicklistValuesQueryObjectInfo>,
|
|
274
51
|
fieldName: string,
|
|
275
52
|
recordTypeId?: string,
|
|
276
53
|
): PicklistValue[] {
|
|
277
|
-
const
|
|
278
|
-
if (
|
|
279
|
-
const fieldMap =
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
return [k, n];
|
|
285
|
-
}),
|
|
286
|
-
)
|
|
287
|
-
: (fieldsNode as Record<string, GraphQLNode>);
|
|
54
|
+
const fields = objectInfo.fields;
|
|
55
|
+
if (fields == null) return [];
|
|
56
|
+
const fieldMap: Record<string, GetPicklistValuesQueryField> = Object.fromEntries(
|
|
57
|
+
fields.map((field) => {
|
|
58
|
+
return [field?.ApiName, field];
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
288
61
|
const field = fieldMap[fieldName];
|
|
289
|
-
if (!field
|
|
290
|
-
const picklistData =
|
|
291
|
-
|
|
62
|
+
if (!field) return [];
|
|
63
|
+
const picklistData =
|
|
64
|
+
"picklistValuesByRecordTypeIDs" in field ? field.picklistValuesByRecordTypeIDs : undefined;
|
|
65
|
+
if (!picklistData) return [];
|
|
292
66
|
const rtId = recordTypeId ?? "012000000000000AAA";
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
if (!byRecordType) {
|
|
296
|
-
const first = arr[0];
|
|
297
|
-
if (!first) return [];
|
|
298
|
-
return mapPicklistValues(first);
|
|
299
|
-
}
|
|
300
|
-
return mapPicklistValues(byRecordType);
|
|
67
|
+
const byRecordType = picklistData.find((p) => p!.recordTypeID === rtId) ?? picklistData[0];
|
|
68
|
+
return mapPicklistValues(byRecordType?.picklistValues ?? []);
|
|
301
69
|
}
|
|
302
70
|
|
|
303
|
-
function mapPicklistValues(
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
label: typeof n.label === "string" ? n.label : "",
|
|
310
|
-
value: typeof n.value === "string" ? n.value : "",
|
|
311
|
-
validFor: Array.isArray(n.validFor) ? n.validFor : undefined,
|
|
312
|
-
attributes: (n.attributes != null && typeof n.attributes === "object"
|
|
313
|
-
? n.attributes
|
|
314
|
-
: undefined) as Record<string, unknown> | undefined,
|
|
315
|
-
defaultValue: typeof n.defaultValue === "boolean" ? n.defaultValue : undefined,
|
|
316
|
-
active: typeof n.active === "boolean" ? n.active : undefined,
|
|
317
|
-
} as PicklistValue;
|
|
318
|
-
});
|
|
71
|
+
function mapPicklistValues(values: GraphQLPicklistValue[]): PicklistValue[] {
|
|
72
|
+
return values.map((item) => ({
|
|
73
|
+
...item,
|
|
74
|
+
value: item.value ?? "",
|
|
75
|
+
label: item.label ?? "",
|
|
76
|
+
}));
|
|
319
77
|
}
|
|
@@ -49,13 +49,13 @@ const getFetchableFieldsFromLayoutItem = function (
|
|
|
49
49
|
|
|
50
50
|
// add field: fieldType
|
|
51
51
|
const fieldMetadata = metadata.fields[comp.apiName];
|
|
52
|
-
fields[comp.apiName] = fieldMetadata
|
|
52
|
+
fields[comp.apiName] = fieldMetadata?.dataType ?? "";
|
|
53
53
|
|
|
54
54
|
// add relatedField if one exists (Id field -> add relationship name so we request Owner.Name)
|
|
55
55
|
if (comp.apiName in metadata.fields) {
|
|
56
56
|
const relationshipName = fieldMetadata?.relationshipName;
|
|
57
57
|
if (relationshipName) {
|
|
58
|
-
fields[relationshipName] = fieldMetadata.dataType;
|
|
58
|
+
fields[relationshipName] = fieldMetadata.dataType ?? "";
|
|
59
59
|
|
|
60
60
|
relationFieldMap[comp.apiName] = relationshipName;
|
|
61
61
|
}
|
|
@@ -65,7 +65,7 @@ const getFetchableFieldsFromLayoutItem = function (
|
|
|
65
65
|
const idField = findIdFieldForRelationship(metadata, comp.apiName);
|
|
66
66
|
if (idField) {
|
|
67
67
|
const idMeta = metadata.fields[idField];
|
|
68
|
-
fields[idField] = idMeta
|
|
68
|
+
fields[idField] = idMeta?.dataType ?? "";
|
|
69
69
|
relationFieldMap[idField] = comp.apiName;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
@@ -137,7 +137,7 @@ export const calculateFieldsToFetch = function (
|
|
|
137
137
|
const fields = getFetchableFieldsFromLayout(metadata, layout, relationFieldMap);
|
|
138
138
|
let fieldsToFetch = Object.keys(fields);
|
|
139
139
|
if (shouldPrefixedWithEntityName) {
|
|
140
|
-
fieldsToFetch = fieldsToFetch.map((field) => `${metadata.
|
|
140
|
+
fieldsToFetch = fieldsToFetch.map((field) => `${metadata.ApiName}.${field}`);
|
|
141
141
|
}
|
|
142
142
|
// populate field types for o11y logging
|
|
143
143
|
const fieldTypes = Object.values(fields).filter((fieldType) => fieldType !== "");
|
|
@@ -87,13 +87,8 @@ export type {
|
|
|
87
87
|
FiltersResponse,
|
|
88
88
|
} from "./features/global-search/types/filters/filters";
|
|
89
89
|
|
|
90
|
-
export { PicklistValueArraySchema } from "./features/global-search/types/filters/picklist";
|
|
91
90
|
export type { PicklistValue } from "./features/global-search/types/filters/picklist";
|
|
92
91
|
|
|
93
|
-
export {
|
|
94
|
-
ObjectInfoBatchResponseSchema,
|
|
95
|
-
ObjectInfoResultSchema,
|
|
96
|
-
} from "./features/global-search/types/objectInfo/objectInfo";
|
|
97
92
|
export type {
|
|
98
93
|
ObjectInfoBatchResponse,
|
|
99
94
|
ObjectInfoResult,
|
package/dist/package.json
CHANGED