@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.
Files changed (131) hide show
  1. package/dist/CHANGELOG.md +16 -0
  2. package/dist/force-app/main/default/data/Lease__c.json +13 -0
  3. package/dist/force-app/main/default/webapplications/appreactsampleb2x/package.json +13 -8
  4. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/applicationApi.ts +78 -0
  5. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/graphqlClient.ts +17 -0
  6. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/index.ts +19 -0
  7. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/leadApi.ts +69 -0
  8. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/maintenanceRequestApi.ts +177 -0
  9. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectDetailService.ts +125 -0
  10. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoGraphQLService.ts +194 -0
  11. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/objectInfoService.ts +199 -0
  12. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyDetailGraphQL.ts +497 -0
  13. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/propertyListingGraphQL.ts +190 -0
  14. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/api/recordListGraphQLService.ts +365 -0
  15. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/appLayout.tsx +20 -30
  16. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/FiltersPanel.tsx +375 -0
  17. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/LoadingFallback.tsx +61 -0
  18. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyListingCard.tsx +164 -0
  19. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/PropertyMap.tsx +113 -0
  20. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/SearchResultCard.tsx +131 -0
  21. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/alerts/status-alert.tsx +45 -0
  22. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailFields.tsx +55 -0
  23. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailForm.tsx +146 -0
  24. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailHeader.tsx +34 -0
  25. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/DetailLayoutSections.tsx +80 -0
  26. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/Section.tsx +108 -0
  27. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/SectionRow.tsx +20 -0
  28. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/UiApiDetailForm.tsx +140 -0
  29. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FieldValueDisplay.tsx +73 -0
  30. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedAddress.tsx +29 -0
  31. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedEmail.tsx +17 -0
  32. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedPhone.tsx +24 -0
  33. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedText.tsx +11 -0
  34. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/FormattedUrl.tsx +29 -0
  35. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/detail/formatted/index.ts +6 -0
  36. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterField.tsx +54 -0
  37. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterInput.tsx +55 -0
  38. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/filters/FilterSelect.tsx +72 -0
  39. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/filters-form.tsx +114 -0
  40. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/forms/submit-button.tsx +47 -0
  41. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/layout/card-layout.tsx +19 -0
  42. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/ResultCardFields.tsx +71 -0
  43. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchHeader.tsx +31 -0
  44. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchPagination.tsx +144 -0
  45. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/search/SearchResultsPanel.tsx +197 -0
  46. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/components/shared/GlobalSearchInput.tsx +114 -0
  47. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/constants.ts +39 -0
  48. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/features/global-search/index.ts +33 -0
  49. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/form.tsx +204 -0
  50. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/index.ts +22 -0
  51. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useGeocode.ts +35 -0
  52. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useMaintenanceRequests.ts +39 -0
  53. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectInfoBatch.ts +65 -0
  54. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useObjectSearchData.ts +395 -0
  55. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyAddresses.ts +36 -0
  56. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyDetail.ts +99 -0
  57. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyListingSearch.ts +75 -0
  58. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyMapMarkers.ts +100 -0
  59. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/usePropertyPrimaryImages.ts +51 -0
  60. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordDetailLayout.ts +156 -0
  61. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useRecordListGraphQL.ts +135 -0
  62. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/hooks/useWeather.ts +173 -0
  63. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Application.tsx +263 -76
  64. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Contact.tsx +158 -0
  65. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Dashboard.tsx +137 -65
  66. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/DetailPage.tsx +109 -0
  67. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/GlobalSearch.tsx +229 -0
  68. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Home.tsx +469 -21
  69. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/Maintenance.tsx +244 -95
  70. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyDetails.tsx +211 -39
  71. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertyListings.tsx +26 -10
  72. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearch.tsx +165 -0
  73. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/pages/PropertySearchPlaceholder.tsx +49 -0
  74. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-01.jpg +0 -0
  75. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-02.jpg +0 -0
  76. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-03.jpg +0 -0
  77. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-04.jpg +0 -0
  78. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-05.jpg +0 -0
  79. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-06.jpg +0 -0
  80. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-07.jpg +0 -0
  81. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-08.jpg +0 -0
  82. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-09.jpg +0 -0
  83. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-10.jpg +0 -0
  84. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-11.jpg +0 -0
  85. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-12.jpg +0 -0
  86. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-13.jpg +0 -0
  87. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-14.jpg +0 -0
  88. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-15.jpg +0 -0
  89. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-16.jpg +0 -0
  90. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-17.jpg +0 -0
  91. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-18.jpg +0 -0
  92. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-19.jpg +0 -0
  93. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-20.jpg +0 -0
  94. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-21.jpg +0 -0
  95. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-22.jpg +0 -0
  96. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-23.jpg +0 -0
  97. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-24.jpg +0 -0
  98. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/public/property-25.jpg +0 -0
  99. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/routes.tsx +32 -6
  100. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/styles/global.css +23 -63
  101. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/filters.ts +120 -0
  102. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/filters/picklist.ts +32 -0
  103. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/index.ts +4 -0
  104. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/leaflet.d.ts +17 -0
  105. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/objectInfo/objectInfo.ts +166 -0
  106. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/recordDetail/recordDetail.ts +61 -0
  107. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/types/search/searchResults.ts +229 -0
  108. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/apiUtils.ts +125 -0
  109. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/cacheUtils.ts +76 -0
  110. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/debounce.ts +89 -0
  111. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldUtils.ts +354 -0
  112. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/fieldValueExtractor.ts +67 -0
  113. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/filterUtils.ts +32 -0
  114. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formDataTransformUtils.ts +260 -0
  115. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/formUtils.ts +142 -0
  116. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/geocode.ts +65 -0
  117. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLNodeFieldUtils.ts +186 -0
  118. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLObjectInfoAdapter.ts +319 -0
  119. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/graphQLRecordAdapter.ts +90 -0
  120. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/index.ts +59 -0
  121. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/layoutTransformUtils.ts +236 -0
  122. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/linkUtils.ts +14 -0
  123. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/paginationUtils.ts +49 -0
  124. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/recordUtils.ts +159 -0
  125. package/dist/force-app/main/default/webapplications/appreactsampleb2x/src/utils/sanitizationUtils.ts +49 -0
  126. package/dist/package.json +1 -1
  127. package/package.json +2 -2
  128. package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls +0 -111
  129. package/dist/force-app/main/default/classes/MaintenanceRequestListAction.cls-meta.xml +0 -6
  130. package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls +0 -93
  131. package/dist/force-app/main/default/classes/MaintenanceRequestUpdatePriorityAction.cls-meta.xml +0 -6
@@ -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,4 @@
1
+ export * from "./filters/filters";
2
+ export * from "./objectInfo/objectInfo";
3
+ export * from "./recordDetail/recordDetail";
4
+ export * from "./search/searchResults";
@@ -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
+ }