@salesforce/webapp-template-app-react-sample-b2x-experimental 1.68.0 → 1.69.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 +16 -0
- package/dist/force-app/main/default/data/Lease__c.json +13 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +13 -8
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/applicationApi.ts +78 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphqlClient.ts +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/index.ts +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/leadApi.ts +69 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/maintenanceRequestApi.ts +177 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectDetailService.ts +125 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoGraphQLService.ts +194 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoService.ts +199 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyDetailGraphQL.ts +497 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyListingGraphQL.ts +190 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/recordListGraphQLService.ts +365 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +20 -30
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/FiltersPanel.tsx +375 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/LoadingFallback.tsx +61 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyListingCard.tsx +164 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyMap.tsx +113 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/SearchResultCard.tsx +131 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/alerts/status-alert.tsx +45 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailFields.tsx +55 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailForm.tsx +146 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailHeader.tsx +34 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailLayoutSections.tsx +80 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/Section.tsx +108 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/SectionRow.tsx +20 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/UiApiDetailForm.tsx +140 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FieldValueDisplay.tsx +73 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedAddress.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedEmail.tsx +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedPhone.tsx +24 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedText.tsx +11 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedUrl.tsx +29 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/index.ts +6 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterField.tsx +54 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterInput.tsx +55 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterSelect.tsx +72 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/filters-form.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/submit-button.tsx +47 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/layout/card-layout.tsx +19 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/ResultCardFields.tsx +71 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchHeader.tsx +31 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchPagination.tsx +144 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchResultsPanel.tsx +197 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/shared/GlobalSearchInput.tsx +114 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/constants.ts +39 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/index.ts +33 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/form.tsx +204 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/index.ts +22 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useGeocode.ts +35 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useMaintenanceRequests.ts +39 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectInfoBatch.ts +65 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectSearchData.ts +395 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyAddresses.ts +36 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyDetail.ts +99 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingSearch.ts +75 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyMapMarkers.ts +100 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyPrimaryImages.ts +51 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordDetailLayout.ts +156 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordListGraphQL.ts +135 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useWeather.ts +173 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +263 -76
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Contact.tsx +158 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +137 -65
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/DetailPage.tsx +109 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/GlobalSearch.tsx +229 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +469 -21
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +244 -95
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +211 -39
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +26 -10
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx +165 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearchPlaceholder.tsx +49 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-01.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-02.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-03.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-04.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-05.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-06.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-07.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-08.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-09.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-10.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-11.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-12.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-13.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-14.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-15.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-16.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-17.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-18.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-19.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-20.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-21.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-22.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-23.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-24.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-25.jpg +0 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +32 -6
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/styles/global.css +23 -63
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/filters.ts +120 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/picklist.ts +32 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/index.ts +4 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/leaflet.d.ts +17 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/objectInfo/objectInfo.ts +166 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/recordDetail/recordDetail.ts +61 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/search/searchResults.ts +229 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/apiUtils.ts +125 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/cacheUtils.ts +76 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/debounce.ts +89 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldUtils.ts +354 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldValueExtractor.ts +67 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/filterUtils.ts +32 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formDataTransformUtils.ts +260 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formUtils.ts +142 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/geocode.ts +65 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLNodeFieldUtils.ts +186 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLObjectInfoAdapter.ts +319 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLRecordAdapter.ts +90 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/index.ts +59 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/layoutTransformUtils.ts +236 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/linkUtils.ts +14 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/paginationUtils.ts +49 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/recordUtils.ts +159 -0
- package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/sanitizationUtils.ts +49 -0
- package/dist/package.json +1 -1
- package/package.json +2 -2
- package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls +0 -111
- package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls-meta.xml +0 -6
- package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls +0 -93
- package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls-meta.xml +0 -6
package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/picklist.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions for picklist value structures
|
|
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);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type declarations for leaflet and react-leaflet when types are not installed in the type-check context.
|
|
3
|
+
* The app lists these in feature packageJson; this file satisfies TypeScript during source builds.
|
|
4
|
+
*/
|
|
5
|
+
declare module "leaflet" {
|
|
6
|
+
const L: Record<string, unknown>;
|
|
7
|
+
export default L;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
declare module "react-leaflet" {
|
|
11
|
+
import type { ComponentType } from "react";
|
|
12
|
+
export const MapContainer: ComponentType<Record<string, unknown>>;
|
|
13
|
+
export const TileLayer: ComponentType<Record<string, unknown>>;
|
|
14
|
+
export const Marker: ComponentType<Record<string, unknown>>;
|
|
15
|
+
export const Popup: ComponentType<Record<string, unknown>>;
|
|
16
|
+
export function useMap(): Record<string, unknown>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions and Zod schemas for Object Info Batch API response
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Zod Schema for Child Relationship
|
|
8
|
+
const ChildRelationshipSchema = z.object({
|
|
9
|
+
childObjectApiName: z.string(),
|
|
10
|
+
fieldName: z.string(),
|
|
11
|
+
junctionIdListNames: z.array(z.string()),
|
|
12
|
+
junctionReferenceTo: z.array(z.string()),
|
|
13
|
+
relationshipName: z.string(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Zod Schema for Reference To Info
|
|
17
|
+
const ReferenceToInfoSchema = z.object({
|
|
18
|
+
apiName: z.string(),
|
|
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>;
|
|
148
|
+
};
|
|
149
|
+
export type ObjectInfoBatchResultItem = Omit<
|
|
150
|
+
z.infer<typeof ObjectInfoBatchResultItemSchema>,
|
|
151
|
+
"result"
|
|
152
|
+
> & {
|
|
153
|
+
result: ObjectInfoResult;
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Export schemas for validation
|
|
157
|
+
export {
|
|
158
|
+
ChildRelationshipSchema,
|
|
159
|
+
ReferenceToInfoSchema,
|
|
160
|
+
FilteredLookupInfoSchema,
|
|
161
|
+
FieldSchema,
|
|
162
|
+
RecordTypeInfoSchema,
|
|
163
|
+
ThemeInfoSchema,
|
|
164
|
+
ObjectInfoResultSchema,
|
|
165
|
+
ObjectInfoBatchResultItemSchema,
|
|
166
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions and Zod schemas for Record Detail APIs:
|
|
5
|
+
* - Layout API (layout/{objectApiName}?layoutType=Full&mode=View&recordTypeId=...)
|
|
6
|
+
* - Record API (records/{recordId}?optionalFields=...)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const LayoutComponentSchema = z.object({
|
|
10
|
+
apiName: z.string().nullable(),
|
|
11
|
+
behavior: z.string().optional(),
|
|
12
|
+
componentType: z.enum(["Field", "CustomLink", "EmptySpace"]),
|
|
13
|
+
customLinkUrl: z.string().optional(),
|
|
14
|
+
label: z.string().optional(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type LayoutComponent = z.infer<typeof LayoutComponentSchema>;
|
|
18
|
+
|
|
19
|
+
const LayoutItemSchema = z.object({
|
|
20
|
+
editableForNew: z.boolean(),
|
|
21
|
+
editableForUpdate: z.boolean(),
|
|
22
|
+
label: z.string(),
|
|
23
|
+
layoutComponents: z.array(LayoutComponentSchema),
|
|
24
|
+
lookupIdApiName: z.string().nullable(),
|
|
25
|
+
required: z.boolean(),
|
|
26
|
+
sortable: z.boolean(),
|
|
27
|
+
uiBehavior: z.string().nullable(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export type LayoutItem = z.infer<typeof LayoutItemSchema>;
|
|
31
|
+
|
|
32
|
+
const LayoutRowSchema = z.object({
|
|
33
|
+
layoutItems: z.array(LayoutItemSchema),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const LayoutSectionSchema = z.object({
|
|
37
|
+
collapsible: z.boolean(),
|
|
38
|
+
columns: z.number(),
|
|
39
|
+
heading: z.string(),
|
|
40
|
+
id: z.string(),
|
|
41
|
+
layoutRows: z.array(LayoutRowSchema),
|
|
42
|
+
rows: z.number(),
|
|
43
|
+
tabOrder: z.string(),
|
|
44
|
+
useHeading: z.boolean(),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
export type LayoutSection = z.infer<typeof LayoutSectionSchema>;
|
|
48
|
+
|
|
49
|
+
export const LayoutResponseSchema = z.object({
|
|
50
|
+
eTag: z.string(),
|
|
51
|
+
id: z.string(),
|
|
52
|
+
layoutType: z.string(),
|
|
53
|
+
mode: z.string(),
|
|
54
|
+
objectApiName: z.string(),
|
|
55
|
+
recordTypeId: z.string(),
|
|
56
|
+
saveOptions: z.array(z.unknown()).optional(),
|
|
57
|
+
sections: z.array(LayoutSectionSchema),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export type LayoutResponse = z.infer<typeof LayoutResponseSchema>;
|
|
61
|
+
export type LayoutRow = z.infer<typeof LayoutRowSchema>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type definitions for search results and column structures.
|
|
5
|
+
* * ARCHITECTURE NOTE:
|
|
6
|
+
* We define recursive interfaces MANUALLY first.
|
|
7
|
+
* If we rely on z.infer<typeof LazySchema> where the schema is z.ZodTypeAny,
|
|
8
|
+
* TypeScript defaults the type to 'any', destroying type safety.
|
|
9
|
+
*/
|
|
10
|
+
export type ComplexFieldValue = {
|
|
11
|
+
apiName?: string;
|
|
12
|
+
childRelationships?: Record<string, unknown>;
|
|
13
|
+
eTag?: string;
|
|
14
|
+
fields?: Record<string, FieldValue>; // Recursive reference
|
|
15
|
+
id?: string;
|
|
16
|
+
lastModifiedById?: string | null;
|
|
17
|
+
lastModifiedDate?: string | null;
|
|
18
|
+
recordTypeId?: string | null;
|
|
19
|
+
recordTypeInfo?: unknown;
|
|
20
|
+
systemModstamp?: string | null;
|
|
21
|
+
weakEtag?: number;
|
|
22
|
+
};
|
|
23
|
+
export type FieldValue = {
|
|
24
|
+
displayValue: string | null;
|
|
25
|
+
value: string | number | boolean | null | ComplexFieldValue; // Recursive union
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Zod Schema for Inline Edit Attributes
|
|
29
|
+
export const InlineEditAttributesSchema = z.record(
|
|
30
|
+
z.string(),
|
|
31
|
+
z.object({
|
|
32
|
+
editable: z.boolean(),
|
|
33
|
+
required: z.boolean(),
|
|
34
|
+
}),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Inline edit attributes for a field
|
|
39
|
+
*/
|
|
40
|
+
export type InlineEditAttributes = z.infer<typeof InlineEditAttributesSchema>;
|
|
41
|
+
|
|
42
|
+
// Zod Schema for Column
|
|
43
|
+
export const ColumnSchema = z.object({
|
|
44
|
+
fieldApiName: z.string(),
|
|
45
|
+
inlineEditAttributes: InlineEditAttributesSchema.optional(),
|
|
46
|
+
label: z.string(),
|
|
47
|
+
lookupId: z.string().nullish(),
|
|
48
|
+
searchable: z.boolean(),
|
|
49
|
+
sortable: z.boolean(),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Column definition for list/result UI. Can be derived from Filter[] via targetFieldPath and label
|
|
54
|
+
* (e.g. filters.map(f => ({ fieldApiName: f.targetFieldPath, label: f.label, searchable: true, sortable: true }))).
|
|
55
|
+
*/
|
|
56
|
+
export type Column = z.infer<typeof ColumnSchema>;
|
|
57
|
+
|
|
58
|
+
// Export schema for validation
|
|
59
|
+
export const ColumnArraySchema = z.array(ColumnSchema);
|
|
60
|
+
|
|
61
|
+
// Zod Schema for Complex Field Value (recursive structure)
|
|
62
|
+
// Using z.lazy() to handle circular reference with FieldValueSchema
|
|
63
|
+
// Note: This schema is exported for advanced use cases but should be used carefully
|
|
64
|
+
// due to potential performance implications with deeply nested structures
|
|
65
|
+
export const ComplexFieldValueSchema: z.ZodType<ComplexFieldValue> = z.lazy(() =>
|
|
66
|
+
z.object({
|
|
67
|
+
apiName: z.string().optional(),
|
|
68
|
+
childRelationships: z.record(z.string(), z.unknown()).optional(),
|
|
69
|
+
eTag: z.string().optional(),
|
|
70
|
+
fields: z.record(z.string(), FieldValueSchema).optional(),
|
|
71
|
+
id: z.string().optional(),
|
|
72
|
+
lastModifiedById: z.string().nullish(),
|
|
73
|
+
lastModifiedDate: z.string().nullish(),
|
|
74
|
+
recordTypeId: z.string().nullish(),
|
|
75
|
+
recordTypeInfo: z.unknown().optional(),
|
|
76
|
+
systemModstamp: z.string().nullish(),
|
|
77
|
+
weakEtag: z.number().optional(),
|
|
78
|
+
}),
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// Zod Schema for Field Value (using z.lazy() to handle circular reference)
|
|
82
|
+
// Note: This schema is exported for validating individual field values on-demand
|
|
83
|
+
// Use FieldValueValidationSchema alias for clarity
|
|
84
|
+
// Using z.union([z.string(), z.null()]) instead of .nullish() to match FieldValue type definition
|
|
85
|
+
export const FieldValueSchema: z.ZodType<FieldValue> = z.lazy(() =>
|
|
86
|
+
z.object({
|
|
87
|
+
displayValue: z.union([z.string(), z.null()]),
|
|
88
|
+
value: z.union([
|
|
89
|
+
z.string(),
|
|
90
|
+
z.number(),
|
|
91
|
+
z.boolean(),
|
|
92
|
+
z.null(),
|
|
93
|
+
ComplexFieldValueSchema as z.ZodType<ComplexFieldValue>,
|
|
94
|
+
]),
|
|
95
|
+
}),
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
// Zod Schema for Search Result Record Data (lightweight validation)
|
|
99
|
+
// Using z.unknown() for fields to avoid expensive recursive validation of every field value
|
|
100
|
+
// This prevents UI freezing when validating large result sets (e.g., 100 records × 50 fields = 5,000 validations)
|
|
101
|
+
// Individual fields can be validated later when needed using FieldValueSchema
|
|
102
|
+
// Note: z.unknown() is safer than z.any() as it requires explicit type checking before use
|
|
103
|
+
export const SearchResultRecordDataSchema = z.object({
|
|
104
|
+
apiName: z.string(),
|
|
105
|
+
childRelationships: z.record(z.string(), z.unknown()),
|
|
106
|
+
eTag: z.string(),
|
|
107
|
+
fields: z.record(z.string(), z.unknown()), // Lightweight: avoids recursive validation, uses unknown for type safety
|
|
108
|
+
id: z.string(),
|
|
109
|
+
lastModifiedById: z.string().nullish(),
|
|
110
|
+
lastModifiedDate: z.string().nullish(),
|
|
111
|
+
recordTypeId: z.string().nullish(),
|
|
112
|
+
recordTypeInfo: z.unknown().nullish(),
|
|
113
|
+
systemModstamp: z.string().nullish(),
|
|
114
|
+
weakEtag: z.number(),
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Full validation schema for individual field values (use when validating specific fields)
|
|
118
|
+
// This can be used to validate a single field value when needed
|
|
119
|
+
export const FieldValueValidationSchema = FieldValueSchema;
|
|
120
|
+
|
|
121
|
+
// Zod Schema for Highlight Info
|
|
122
|
+
export const HighlightInfoSchema = z.object({
|
|
123
|
+
fields: z.record(z.string(), z.unknown()),
|
|
124
|
+
snippet: z.string().nullish(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Zod Schema for Search Info
|
|
128
|
+
export const SearchInfoSchema = z.object({
|
|
129
|
+
isPromoted: z.boolean(),
|
|
130
|
+
isSpellCorrected: z.boolean(),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Zod Schema for Search Result Record
|
|
134
|
+
export const SearchResultRecordSchema = z.object({
|
|
135
|
+
highlightInfo: HighlightInfoSchema,
|
|
136
|
+
record: SearchResultRecordDataSchema,
|
|
137
|
+
searchInfo: SearchInfoSchema,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Record structure within search results
|
|
142
|
+
* Note: The fields property is typed as Record<string, FieldValue> for type safety,
|
|
143
|
+
* but validation uses z.unknown() for performance (avoids recursive validation of all fields)
|
|
144
|
+
*/
|
|
145
|
+
export type SearchResultRecordData = Omit<
|
|
146
|
+
z.infer<typeof SearchResultRecordDataSchema>,
|
|
147
|
+
"fields"
|
|
148
|
+
> & {
|
|
149
|
+
fields: Record<string, FieldValue>;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Highlight information for search results
|
|
154
|
+
*/
|
|
155
|
+
export type HighlightInfo = z.infer<typeof HighlightInfoSchema>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Search information for results
|
|
159
|
+
*/
|
|
160
|
+
export type SearchInfo = z.infer<typeof SearchInfoSchema>;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Single record in search results (complete structure from API)
|
|
164
|
+
* Note: The record.fields property is typed as Record<string, FieldValue> for type safety,
|
|
165
|
+
* but validation uses z.unknown() for performance (avoids recursive validation of all fields)
|
|
166
|
+
*/
|
|
167
|
+
export type SearchResultRecord = Omit<z.infer<typeof SearchResultRecordSchema>, "record"> & {
|
|
168
|
+
record: SearchResultRecordData;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// Export schemas for validation
|
|
172
|
+
export const SearchResultRecordArraySchema = z.array(SearchResultRecordSchema);
|
|
173
|
+
|
|
174
|
+
// Zod Schema for Order By
|
|
175
|
+
export const OrderBySchema = z.object({
|
|
176
|
+
fieldApiName: z.string(),
|
|
177
|
+
isAscending: z.boolean(),
|
|
178
|
+
label: z.string(),
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Order by configuration
|
|
183
|
+
*/
|
|
184
|
+
export type OrderBy = z.infer<typeof OrderBySchema>;
|
|
185
|
+
|
|
186
|
+
// Zod Schema for Keyword Search Result
|
|
187
|
+
export const KeywordSearchResultSchema = z.object({
|
|
188
|
+
currentPageToken: z.string(),
|
|
189
|
+
error: z.string().nullish(),
|
|
190
|
+
nextPageToken: z.string().nullish(),
|
|
191
|
+
objectApiName: z.string(),
|
|
192
|
+
orderBy: z.array(OrderBySchema),
|
|
193
|
+
pageSize: z.number(),
|
|
194
|
+
previousPageToken: z.string().nullish(),
|
|
195
|
+
records: z.array(SearchResultRecordSchema),
|
|
196
|
+
relatedObjectApiNames: z.array(z.string()),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Keyword search result structure
|
|
201
|
+
*/
|
|
202
|
+
export type KeywordSearchResult = z.infer<typeof KeywordSearchResultSchema>;
|
|
203
|
+
|
|
204
|
+
// Zod Schema for Search Results Response
|
|
205
|
+
export const SearchResultsResponseSchema = z.object({
|
|
206
|
+
configurationName: z.string().nullish(),
|
|
207
|
+
keywordSearchResult: KeywordSearchResultSchema,
|
|
208
|
+
objectApiName: z.string(),
|
|
209
|
+
query: z.string(),
|
|
210
|
+
queryId: z.string(),
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Search results response structure
|
|
215
|
+
*/
|
|
216
|
+
export type SearchResultsResponse = z.infer<typeof SearchResultsResponseSchema>;
|
|
217
|
+
|
|
218
|
+
// Zod Schema for Column Info Response
|
|
219
|
+
export const ColumnInfoResponseSchema = z.record(z.string(), z.unknown()).and(
|
|
220
|
+
z.object({
|
|
221
|
+
columns: ColumnArraySchema.optional(),
|
|
222
|
+
fields: ColumnArraySchema.optional(),
|
|
223
|
+
}),
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Column info response structure
|
|
228
|
+
*/
|
|
229
|
+
export type ColumnInfoResponse = z.infer<typeof ColumnInfoResponseSchema>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* API Utilities
|
|
3
|
+
*
|
|
4
|
+
* Generic utility functions for API requests, validation, and URL handling.
|
|
5
|
+
* These utilities are framework-agnostic and can be reused across different API services.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ZodSchema } from "zod";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Options for fetchAndValidate utility function
|
|
12
|
+
*/
|
|
13
|
+
export interface FetchAndValidateOptions<T> {
|
|
14
|
+
/** Zod schema for validation */
|
|
15
|
+
schema: ZodSchema<T>;
|
|
16
|
+
/** Error context for better error messages (e.g., "object info batch", "list info") */
|
|
17
|
+
errorContext: string;
|
|
18
|
+
/** Optional function to extract/transform data from response before validation */
|
|
19
|
+
extractData?: (data: unknown) => unknown;
|
|
20
|
+
/** Optional AbortSignal to cancel the request */
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Generic utility function to fetch, parse, and validate API responses.
|
|
26
|
+
* Handles common patterns: fetch -> check status -> parse JSON -> validate Zod -> handle errors
|
|
27
|
+
*
|
|
28
|
+
* @param fetchFn - Function that returns a Promise<Response> (e.g., uiApiClient.get or uiApiClient.post)
|
|
29
|
+
* @param options - Configuration options including schema, error context, optional data extraction, and AbortSignal
|
|
30
|
+
* @returns Promise resolving to validated data of type T
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* - Handles abort signals properly to prevent race conditions
|
|
34
|
+
* - Provides detailed error messages with context
|
|
35
|
+
* - Validates responses using Zod schemas for type safety
|
|
36
|
+
* - Supports data extraction/transformation before validation
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```tsx
|
|
40
|
+
* const data = await fetchAndValidate(
|
|
41
|
+
* (signal) => apiClient.get('/endpoint', { signal }),
|
|
42
|
+
* {
|
|
43
|
+
* schema: MySchema,
|
|
44
|
+
* errorContext: 'user data',
|
|
45
|
+
* extractData: (data) => data.items,
|
|
46
|
+
* signal: abortController.signal
|
|
47
|
+
* }
|
|
48
|
+
* );
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export async function fetchAndValidate<T>(
|
|
52
|
+
fetchFn: (signal?: AbortSignal) => Promise<Response>,
|
|
53
|
+
options: FetchAndValidateOptions<T>,
|
|
54
|
+
): Promise<T> {
|
|
55
|
+
const { schema, errorContext, extractData, signal } = options;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetchFn(signal);
|
|
59
|
+
|
|
60
|
+
if (signal?.aborted) {
|
|
61
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
throw new Error(`Failed to fetch ${errorContext}: ${response.status} ${response.statusText}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const data = await response.json();
|
|
69
|
+
|
|
70
|
+
if (signal?.aborted) {
|
|
71
|
+
throw new DOMException("The operation was aborted.", "AbortError");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const dataToValidate = extractData ? extractData(data) : data;
|
|
75
|
+
|
|
76
|
+
const validationResult = schema.safeParse(dataToValidate);
|
|
77
|
+
|
|
78
|
+
if (!validationResult.success) {
|
|
79
|
+
throw new Error(`Invalid ${errorContext} response format: ${validationResult.error.message}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return validationResult.data;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (error instanceof Error && error.name === "ZodError") {
|
|
93
|
+
throw new Error(`Invalid ${errorContext} response format: ${error.message}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (
|
|
97
|
+
error instanceof Error &&
|
|
98
|
+
(error.message.includes("Failed to fetch") || error.message.includes("Invalid"))
|
|
99
|
+
) {
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
throw new Error(
|
|
104
|
+
`Error fetching ${errorContext}: ${
|
|
105
|
+
error instanceof Error ? error.message : (error?.toString() ?? "Unknown error")
|
|
106
|
+
}`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Helper to safely encode path components for URLs
|
|
113
|
+
* Wraps encodeURIComponent for better semantic meaning
|
|
114
|
+
*
|
|
115
|
+
* @param segment - The path segment to encode
|
|
116
|
+
* @returns URL-encoded path segment
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```tsx
|
|
120
|
+
* const safePath = safeEncodePath('Account Name'); // 'Account%20Name'
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
export function safeEncodePath(segment: string): string {
|
|
124
|
+
return encodeURIComponent(segment);
|
|
125
|
+
}
|