@land-catalyst/batch-data-sdk 1.3.2 → 1.3.3

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/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from "./client/client.interface";
12
12
  export * from "./core/logger.interface";
13
13
  export * from "./property-field/metadata";
14
14
  export * from "./property-field/utils";
15
+ export * from "./property-field/display";
15
16
  export * from "./property-field/search-criteria-filter-context";
16
17
  export type { SearchCriteriaFilterContext, SearchCriteriaFilterType, } from "./property-field/search-criteria-filter-context";
17
18
  export * from "./property-field/search-criteria-ai-context";
package/dist/index.js CHANGED
@@ -28,5 +28,6 @@ __exportStar(require("./client/client.interface"), exports);
28
28
  __exportStar(require("./core/logger.interface"), exports);
29
29
  __exportStar(require("./property-field/metadata"), exports);
30
30
  __exportStar(require("./property-field/utils"), exports);
31
+ __exportStar(require("./property-field/display"), exports);
31
32
  __exportStar(require("./property-field/search-criteria-filter-context"), exports);
32
33
  __exportStar(require("./property-field/search-criteria-ai-context"), exports);
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Property Field Display Utilities
3
+ *
4
+ * Utilities for formatting property field values for display in the UI.
5
+ * These helpers take raw property data and field metadata and produce
6
+ * human-readable formatted strings.
7
+ */
8
+ import type { Property } from "../core/types";
9
+ import { type PropertyFieldMetadata, type PropertyFieldType } from "./metadata";
10
+ /**
11
+ * Display field entry for rendering property data
12
+ */
13
+ export interface PropertyDisplayField {
14
+ /** The field path (e.g., "building.yearBuilt") */
15
+ fieldPath: string;
16
+ /** Human-readable label */
17
+ label: string;
18
+ /** Formatted display value */
19
+ value: string;
20
+ /** The field's data type */
21
+ dataType: PropertyFieldType;
22
+ }
23
+ /**
24
+ * Format a property field value based on its metadata
25
+ *
26
+ * @param value The raw field value
27
+ * @param metadata The field's metadata
28
+ * @returns Formatted display string, or empty string for null/undefined
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const meta = getPropertyFieldMetadata("valuation.estimatedValue");
33
+ * formatPropertyFieldValue(450000, meta); // "$450,000"
34
+ *
35
+ * const yearMeta = getPropertyFieldMetadata("building.yearBuilt");
36
+ * formatPropertyFieldValue(1985, yearMeta); // "1985"
37
+ * ```
38
+ */
39
+ export declare function formatPropertyFieldValue(value: unknown, metadata: PropertyFieldMetadata): string;
40
+ /**
41
+ * Get the display label for a property field path
42
+ *
43
+ * @param fieldPath The field path (e.g., "building.yearBuilt")
44
+ * @returns Human-readable label (e.g., "Year Built")
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * getPropertyFieldDisplayLabel("building.yearBuilt"); // "Year Built"
49
+ * getPropertyFieldDisplayLabel("owner.mailingAddress.city"); // "City"
50
+ * ```
51
+ */
52
+ export declare function getPropertyFieldDisplayLabel(fieldPath: string): string;
53
+ /**
54
+ * Get the display name for a property response group
55
+ *
56
+ * @param groupName The group name (e.g., "building", "propertyOwnerProfile")
57
+ * @returns Human-readable display name (e.g., "Building", "Owner Portfolio")
58
+ *
59
+ * @example
60
+ * ```typescript
61
+ * getPropertyGroupDisplayName("building"); // "Building"
62
+ * getPropertyGroupDisplayName("propertyOwnerProfile"); // "Owner Portfolio"
63
+ * ```
64
+ */
65
+ export declare function getPropertyGroupDisplayName(groupName: string): string;
66
+ /**
67
+ * Get all displayable fields for a property within a specific group
68
+ *
69
+ * Returns formatted field data for rendering in the UI. Fields with
70
+ * null/undefined values are excluded.
71
+ *
72
+ * @param property The property object (can be raw BatchData JSON)
73
+ * @param groupName The response group name (e.g., "building", "valuation")
74
+ * @returns Array of display field objects with label, formatted value, etc.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * const buildingFields = getPropertyFieldsForDisplay(property, "building");
79
+ * // Returns:
80
+ * // [
81
+ * // { fieldPath: "building.yearBuilt", label: "Year Built", value: "1985", dataType: "number" },
82
+ * // { fieldPath: "building.bedrooms", label: "Bedrooms", value: "3", dataType: "number" },
83
+ * // ...
84
+ * // ]
85
+ * ```
86
+ */
87
+ export declare function getPropertyFieldsForDisplay(property: Property | Record<string, unknown>, groupName: string): PropertyDisplayField[];
88
+ /**
89
+ * Get all response groups that have displayable data in a property
90
+ *
91
+ * @param property The property object
92
+ * @returns Array of group names that have at least one displayable field
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const groups = getPropertyGroupsWithData(property);
97
+ * // Returns: ["address", "building", "valuation", "owner", ...]
98
+ * ```
99
+ */
100
+ export declare function getPropertyGroupsWithData(property: Property | Record<string, unknown>): string[];
@@ -0,0 +1,338 @@
1
+ "use strict";
2
+ /**
3
+ * Property Field Display Utilities
4
+ *
5
+ * Utilities for formatting property field values for display in the UI.
6
+ * These helpers take raw property data and field metadata and produce
7
+ * human-readable formatted strings.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.formatPropertyFieldValue = formatPropertyFieldValue;
11
+ exports.getPropertyFieldDisplayLabel = getPropertyFieldDisplayLabel;
12
+ exports.getPropertyGroupDisplayName = getPropertyGroupDisplayName;
13
+ exports.getPropertyFieldsForDisplay = getPropertyFieldsForDisplay;
14
+ exports.getPropertyGroupsWithData = getPropertyGroupsWithData;
15
+ const metadata_1 = require("./metadata");
16
+ const utils_1 = require("./utils");
17
+ /**
18
+ * Group display name mappings
19
+ */
20
+ const GROUP_DISPLAY_NAMES = {
21
+ address: "Address",
22
+ assessment: "Assessment",
23
+ building: "Building",
24
+ deedHistory: "Deed History",
25
+ demographics: "Demographics",
26
+ foreclosure: "Foreclosure",
27
+ general: "General",
28
+ ids: "Identifiers",
29
+ intel: "Intel",
30
+ involuntaryLien: "Involuntary Liens",
31
+ legal: "Legal",
32
+ listing: "Listing",
33
+ lot: "Lot",
34
+ meta: "Meta",
35
+ mortgageHistory: "Mortgage History",
36
+ openLien: "Open Liens",
37
+ owner: "Owner",
38
+ permit: "Permits",
39
+ propertyOwnerProfile: "Owner Portfolio",
40
+ quickLists: "Quick Lists",
41
+ sale: "Sale History",
42
+ tax: "Tax",
43
+ valuation: "Valuation",
44
+ };
45
+ /**
46
+ * Convert a field name to a display label
47
+ * Handles camelCase and common abbreviations
48
+ */
49
+ function fieldNameToLabel(name) {
50
+ // Handle common abbreviations
51
+ const abbrevs = {
52
+ sqft: "Sq Ft",
53
+ apn: "APN",
54
+ adu: "ADU",
55
+ avm: "AVM",
56
+ hoa: "HOA",
57
+ id: "ID",
58
+ mls: "MLS",
59
+ url: "URL",
60
+ };
61
+ const lower = name.toLowerCase();
62
+ if (abbrevs[lower]) {
63
+ return abbrevs[lower];
64
+ }
65
+ // Split camelCase and capitalize
66
+ return name
67
+ .replace(/([A-Z])/g, " $1")
68
+ .replace(/^./, (c) => c.toUpperCase())
69
+ .trim();
70
+ }
71
+ /**
72
+ * Format a number as currency (USD)
73
+ */
74
+ function formatCurrency(value) {
75
+ return new Intl.NumberFormat("en-US", {
76
+ style: "currency",
77
+ currency: "USD",
78
+ minimumFractionDigits: 0,
79
+ maximumFractionDigits: 0,
80
+ }).format(value);
81
+ }
82
+ /**
83
+ * Format a number with commas
84
+ */
85
+ function formatNumber(value) {
86
+ return new Intl.NumberFormat("en-US").format(value);
87
+ }
88
+ /**
89
+ * Format a date string
90
+ */
91
+ function formatDate(value) {
92
+ try {
93
+ const date = typeof value === "string" ? new Date(value) : value;
94
+ if (isNaN(date.getTime()))
95
+ return String(value);
96
+ return date.toLocaleDateString("en-US", {
97
+ year: "numeric",
98
+ month: "short",
99
+ day: "numeric",
100
+ });
101
+ }
102
+ catch {
103
+ return String(value);
104
+ }
105
+ }
106
+ /**
107
+ * Check if a field name suggests currency formatting
108
+ */
109
+ function isCurrencyField(fieldPath, name) {
110
+ const lower = (fieldPath + name).toLowerCase();
111
+ return (lower.includes("value") ||
112
+ lower.includes("price") ||
113
+ lower.includes("assessment") ||
114
+ lower.includes("amount") ||
115
+ lower.includes("equity") ||
116
+ lower.includes("balance") ||
117
+ lower.includes("cost"));
118
+ }
119
+ /**
120
+ * Check if a field name suggests year formatting
121
+ */
122
+ function isYearField(name) {
123
+ const lower = name.toLowerCase();
124
+ return lower.includes("year") && !lower.includes("years");
125
+ }
126
+ /**
127
+ * Check if a field name suggests square footage formatting
128
+ */
129
+ function isSqftField(name) {
130
+ const lower = name.toLowerCase();
131
+ return (lower.includes("sqft") ||
132
+ lower.includes("area") ||
133
+ lower.includes("squarefeet"));
134
+ }
135
+ /**
136
+ * Check if a field name suggests acreage formatting
137
+ */
138
+ function isAcresField(name) {
139
+ const lower = name.toLowerCase();
140
+ return lower.includes("acre") || lower.includes("acreage");
141
+ }
142
+ /**
143
+ * Format a property field value based on its metadata
144
+ *
145
+ * @param value The raw field value
146
+ * @param metadata The field's metadata
147
+ * @returns Formatted display string, or empty string for null/undefined
148
+ *
149
+ * @example
150
+ * ```typescript
151
+ * const meta = getPropertyFieldMetadata("valuation.estimatedValue");
152
+ * formatPropertyFieldValue(450000, meta); // "$450,000"
153
+ *
154
+ * const yearMeta = getPropertyFieldMetadata("building.yearBuilt");
155
+ * formatPropertyFieldValue(1985, yearMeta); // "1985"
156
+ * ```
157
+ */
158
+ function formatPropertyFieldValue(value, metadata) {
159
+ // Handle null/undefined
160
+ if (value == null) {
161
+ return "";
162
+ }
163
+ const { dataType, name, fieldPath, possibleValuesRef } = metadata;
164
+ // Handle boolean
165
+ if (dataType === "boolean") {
166
+ return value ? "Yes" : "No";
167
+ }
168
+ // Handle date
169
+ if (dataType === "date") {
170
+ return formatDate(value);
171
+ }
172
+ // Handle array[string]
173
+ if (dataType === "array[string]") {
174
+ if (Array.isArray(value)) {
175
+ return value.filter((v) => v != null).join(", ");
176
+ }
177
+ return String(value);
178
+ }
179
+ // Handle array[number]
180
+ if (dataType === "array[number]") {
181
+ if (Array.isArray(value)) {
182
+ return value
183
+ .filter((v) => v != null)
184
+ .map(formatNumber)
185
+ .join(", ");
186
+ }
187
+ return String(value);
188
+ }
189
+ // Handle number with formatting rules
190
+ if (dataType === "number" && typeof value === "number") {
191
+ // Year - plain integer
192
+ if (isYearField(name)) {
193
+ return String(Math.round(value));
194
+ }
195
+ // Currency fields
196
+ if (isCurrencyField(fieldPath, name)) {
197
+ return formatCurrency(value);
198
+ }
199
+ // Square footage
200
+ if (isSqftField(name)) {
201
+ return `${formatNumber(Math.round(value))} sq ft`;
202
+ }
203
+ // Acreage
204
+ if (isAcresField(name)) {
205
+ return `${value.toFixed(2)} acres`;
206
+ }
207
+ // Default number formatting
208
+ if (Number.isInteger(value)) {
209
+ return formatNumber(value);
210
+ }
211
+ return value.toFixed(2);
212
+ }
213
+ // Handle string with possibleValues lookup
214
+ if (dataType === "string" && typeof value === "string") {
215
+ // If there's a possibleValuesRef, the value itself is usually the display value
216
+ // in POSSIBLE_VALUES_CONSTANTS (they're already human-readable)
217
+ if (possibleValuesRef) {
218
+ const possibleValues = metadata_1.POSSIBLE_VALUES_CONSTANTS[possibleValuesRef];
219
+ if (possibleValues?.includes(value)) {
220
+ return value; // Already display-friendly
221
+ }
222
+ }
223
+ return value;
224
+ }
225
+ // Handle object - just return stringified for now
226
+ if (dataType === "object" && typeof value === "object") {
227
+ return JSON.stringify(value);
228
+ }
229
+ // Default: convert to string
230
+ return String(value);
231
+ }
232
+ /**
233
+ * Get the display label for a property field path
234
+ *
235
+ * @param fieldPath The field path (e.g., "building.yearBuilt")
236
+ * @returns Human-readable label (e.g., "Year Built")
237
+ *
238
+ * @example
239
+ * ```typescript
240
+ * getPropertyFieldDisplayLabel("building.yearBuilt"); // "Year Built"
241
+ * getPropertyFieldDisplayLabel("owner.mailingAddress.city"); // "City"
242
+ * ```
243
+ */
244
+ function getPropertyFieldDisplayLabel(fieldPath) {
245
+ const metadata = metadata_1.PROPERTY_FIELD_METADATA[fieldPath];
246
+ if (metadata) {
247
+ return fieldNameToLabel(metadata.name);
248
+ }
249
+ // Fallback: use the last part of the path
250
+ const parts = fieldPath.split(".");
251
+ const last = parts[parts.length - 1] || fieldPath;
252
+ return fieldNameToLabel(last);
253
+ }
254
+ /**
255
+ * Get the display name for a property response group
256
+ *
257
+ * @param groupName The group name (e.g., "building", "propertyOwnerProfile")
258
+ * @returns Human-readable display name (e.g., "Building", "Owner Portfolio")
259
+ *
260
+ * @example
261
+ * ```typescript
262
+ * getPropertyGroupDisplayName("building"); // "Building"
263
+ * getPropertyGroupDisplayName("propertyOwnerProfile"); // "Owner Portfolio"
264
+ * ```
265
+ */
266
+ function getPropertyGroupDisplayName(groupName) {
267
+ return GROUP_DISPLAY_NAMES[groupName] || fieldNameToLabel(groupName);
268
+ }
269
+ /**
270
+ * Get all displayable fields for a property within a specific group
271
+ *
272
+ * Returns formatted field data for rendering in the UI. Fields with
273
+ * null/undefined values are excluded.
274
+ *
275
+ * @param property The property object (can be raw BatchData JSON)
276
+ * @param groupName The response group name (e.g., "building", "valuation")
277
+ * @returns Array of display field objects with label, formatted value, etc.
278
+ *
279
+ * @example
280
+ * ```typescript
281
+ * const buildingFields = getPropertyFieldsForDisplay(property, "building");
282
+ * // Returns:
283
+ * // [
284
+ * // { fieldPath: "building.yearBuilt", label: "Year Built", value: "1985", dataType: "number" },
285
+ * // { fieldPath: "building.bedrooms", label: "Bedrooms", value: "3", dataType: "number" },
286
+ * // ...
287
+ * // ]
288
+ * ```
289
+ */
290
+ function getPropertyFieldsForDisplay(property, groupName) {
291
+ const groupFields = (0, utils_1.getPropertyGroupFields)(groupName);
292
+ const results = [];
293
+ for (const metadata of groupFields) {
294
+ // Skip array element placeholders ([n] patterns)
295
+ if (metadata.isArrayElement)
296
+ continue;
297
+ const value = (0, utils_1.getPropertyFieldValue)(property, metadata.fieldPath);
298
+ // Skip null/undefined values
299
+ if (value == null)
300
+ continue;
301
+ // Skip empty strings
302
+ if (typeof value === "string" && value.trim() === "")
303
+ continue;
304
+ // Skip empty arrays
305
+ if (Array.isArray(value) && value.length === 0)
306
+ continue;
307
+ const formatted = formatPropertyFieldValue(value, metadata);
308
+ // Skip if formatting returned empty
309
+ if (!formatted)
310
+ continue;
311
+ results.push({
312
+ fieldPath: metadata.fieldPath,
313
+ label: getPropertyFieldDisplayLabel(metadata.fieldPath),
314
+ value: formatted,
315
+ dataType: metadata.dataType,
316
+ });
317
+ }
318
+ return results;
319
+ }
320
+ /**
321
+ * Get all response groups that have displayable data in a property
322
+ *
323
+ * @param property The property object
324
+ * @returns Array of group names that have at least one displayable field
325
+ *
326
+ * @example
327
+ * ```typescript
328
+ * const groups = getPropertyGroupsWithData(property);
329
+ * // Returns: ["address", "building", "valuation", "owner", ...]
330
+ * ```
331
+ */
332
+ function getPropertyGroupsWithData(property) {
333
+ const allGroups = Object.keys(GROUP_DISPLAY_NAMES);
334
+ return allGroups.filter((group) => {
335
+ const fields = getPropertyFieldsForDisplay(property, group);
336
+ return fields.length > 0;
337
+ });
338
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@land-catalyst/batch-data-sdk",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "TypeScript SDK for BatchData.io Property API - Types, Builders, and Utilities",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",