@land-catalyst/batch-data-sdk 1.2.0 → 1.2.2

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.
@@ -343,7 +343,7 @@ export interface OrSearchCriteria {
343
343
  * Note: According to API docs, "involuntary-lien" is only listed for quickLists/orQuickLists arrays,
344
344
  * but it's included here for consistency. If the API rejects it for quickList, it can be removed.
345
345
  */
346
- export type QuickListValue = "absentee-owner" | "active-auction" | "active-listing" | "canceled-listing" | "cash-buyer" | "corporate-owned" | "expired-listing" | "failed-listing" | "fix-and-flip" | "free-and-clear" | "for-sale-by-owner" | "has-hoa" | "has-hoa-fees" | "high-equity" | "inherited" | "involuntary-lien" | "in-state-absentee-owner" | "listed-below-market-price" | "low-equity" | "mailing-address-vacant" | "notice-of-default" | "notice-of-lis-pendens" | "notice-of-sale" | "on-market" | "out-of-state-absentee-owner" | "out-of-state-owner" | "owner-occupied" | "pending-listing" | "preforeclosure" | "recently-sold" | "same-property-and-mailing-address" | "tax-default" | "tired-landlord" | "unknown-equity" | "vacant" | "vacant-lot";
346
+ export type QuickListValue = "absentee-owner" | "active-auction" | "active-listing" | "canceled-listing" | "cash-buyer" | "corporate-owned" | "expired-listing" | "failed-listing" | "fix-and-flip" | "free-and-clear" | "for-sale-by-owner" | "has-hoa" | "has-hoa-fees" | "high-equity" | "inherited" | "involuntary-lien" | "in-state-absentee-owner" | "listed-below-market-price" | "low-equity" | "mailing-address-vacant" | "notice-of-default" | "notice-of-lis-pendens" | "notice-of-sale" | "on-market" | "out-of-state-absentee-owner" | "out-of-state-owner" | "owner-occupied" | "pending-listing" | "preforeclosure" | "recently-sold" | "same-property-and-mailing-address" | "tax-default" | "tired-landlord" | "unknown-equity" | "vacant" | "vacant-lot" | "senior-owner" | "trust-owned";
347
347
  /**
348
348
  * QuickList value that can be prefixed with "not-" to exclude
349
349
  */
package/dist/index.d.ts CHANGED
@@ -12,6 +12,9 @@ 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/search-criteria-filter-context";
16
+ export type { SearchCriteriaFilterContext, SearchCriteriaFilterType, } from "./property-field/search-criteria-filter-context";
17
+ export * from "./property-field/search-criteria-ai-context";
15
18
  export { type PropertyFieldPathType, type PropertyFieldValueType, type FieldMetadataForPath, } from "./property-field/types";
16
19
  export type { SearchCriteriaFieldMapping, SearchCriteriaFieldMetadata, } from "./property-field/utils";
17
20
  export type { RequestMiddleware, ResponseMiddleware, ErrorMiddleware, HttpMiddleware, } from "./client/client";
package/dist/index.js CHANGED
@@ -28,3 +28,5 @@ __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/search-criteria-filter-context"), exports);
32
+ __exportStar(require("./property-field/search-criteria-ai-context"), exports);
@@ -0,0 +1,117 @@
1
+ /**
2
+ * SearchCriteria AI Context Builder
3
+ *
4
+ * Builds complete context for AI services to generate SearchCriteria from natural language.
5
+ * This includes all domain documentation, filter types, examples, and rules.
6
+ */
7
+ /**
8
+ * Domain metadata with description
9
+ */
10
+ export interface SearchCriteriaDomain {
11
+ /**
12
+ * Domain name (e.g., "address", "building", "assessment")
13
+ */
14
+ name: string;
15
+ /**
16
+ * Response group name used to fetch filter contexts (e.g., "address", "building")
17
+ * If not provided, this domain doesn't have filterable fields
18
+ */
19
+ responseGroup?: string;
20
+ /**
21
+ * Human-readable description of what this domain filters
22
+ */
23
+ description: string;
24
+ }
25
+ /**
26
+ * All available SearchCriteria domains with their descriptions
27
+ */
28
+ export declare const SEARCH_CRITERIA_DOMAINS: SearchCriteriaDomain[];
29
+ /**
30
+ * Array of all valid SearchCriteria domain names (for autocomplete)
31
+ * Using `as const` preserves literal types for better IDE autocomplete
32
+ */
33
+ export declare const SEARCH_CRITERIA_DOMAIN_NAMES: readonly ["query", "address", "assessment", "building", "lot", "owner", "sale", "tax", "valuation", "listing", "openLien", "foreclosure", "quickList"];
34
+ /**
35
+ * Type representing valid SearchCriteria domain names
36
+ * This union type provides better autocomplete in IDEs
37
+ */
38
+ export type SearchCriteriaDomainName = (typeof SEARCH_CRITERIA_DOMAIN_NAMES)[number];
39
+ /**
40
+ * Options for building AI context
41
+ */
42
+ export interface SearchCriteriaAIContextOptions {
43
+ /**
44
+ * Maximum number of fields to include per domain (to avoid overwhelming the prompt)
45
+ * If not specified, all fields will be included
46
+ */
47
+ maxFieldsPerDomain?: number;
48
+ /**
49
+ * Maximum number of examples per field
50
+ * If not specified, all examples will be included
51
+ */
52
+ maxExamplesPerField?: number;
53
+ }
54
+ /**
55
+ * Build domain context documentation for specified domains
56
+ * @param domains Array of domain names to include (e.g., ["address", "building", "assessment"])
57
+ * If undefined or empty array, includes all domains (default)
58
+ * Type-safe: only valid domain names from SEARCH_CRITERIA_DOMAINS are allowed
59
+ * @param options Options for customizing the context (applies to all specified domains)
60
+ * @returns Formatted domain documentation string
61
+ * @example
62
+ * ```typescript
63
+ * // Get context for all domains (default)
64
+ * const allContext = buildSearchCriteriaContext();
65
+ *
66
+ * // Get context for specific domains
67
+ * const context = buildSearchCriteriaContext(["address", "building", "assessment"]);
68
+ *
69
+ * // Get context for all domains with options
70
+ * const allContextWithOptions = buildSearchCriteriaContext(undefined, { maxFieldsPerDomain: 5 });
71
+ *
72
+ * // Get context for specific domains with options
73
+ * const selectiveContext = buildSearchCriteriaContext(["address", "building"], { maxFieldsPerDomain: 5 });
74
+ * ```
75
+ */
76
+ export declare function buildSearchCriteriaContext(domains?: SearchCriteriaDomainName[], options?: SearchCriteriaAIContextOptions): string;
77
+ /**
78
+ * Build complete system prompt for AI to generate SearchCriteria from natural language
79
+ * @param domains Optional domain names to include. If not specified, includes all domains.
80
+ * Only domains that exist in SEARCH_CRITERIA_DOMAINS are allowed
81
+ * @param options Options for customizing the context (applies to all specified domains)
82
+ * @returns Complete system prompt string
83
+ * @example
84
+ * ```typescript
85
+ * // Full context with all domains
86
+ * const systemPrompt = buildSearchCriteriaSystemPrompt();
87
+ *
88
+ * // Selective context for specific domains
89
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(["address", "building", "assessment"]);
90
+ *
91
+ * // All domains with options
92
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(undefined, { maxFieldsPerDomain: 5 });
93
+ *
94
+ * // Specific domains with options
95
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(["address", "building"], { maxFieldsPerDomain: 5 });
96
+ * ```
97
+ */
98
+ export declare function buildSearchCriteriaSystemPrompt(domains?: SearchCriteriaDomainName[], options?: SearchCriteriaAIContextOptions): string;
99
+ /**
100
+ * Get context documentation for a specific domain
101
+ * @param domainName The domain name (e.g., "address", "building", "assessment")
102
+ * @param options Options for customizing the context
103
+ * @returns Formatted documentation string for the domain
104
+ * @example
105
+ * ```typescript
106
+ * const addressContext = getDomainContext("address");
107
+ * // Returns formatted documentation for all address fields
108
+ * ```
109
+ */
110
+ export declare function getDomainContext(domainName: string, options?: SearchCriteriaAIContextOptions): string;
111
+ /**
112
+ * Build user prompt with existing criteria context
113
+ * @param userPrompt The user's natural language prompt
114
+ * @param existingCriteria Optional existing search criteria to modify
115
+ * @returns Formatted user prompt string
116
+ */
117
+ export declare function buildSearchCriteriaUserPrompt(userPrompt: string, existingCriteria?: unknown): string;
@@ -0,0 +1,351 @@
1
+ "use strict";
2
+ /**
3
+ * SearchCriteria AI Context Builder
4
+ *
5
+ * Builds complete context for AI services to generate SearchCriteria from natural language.
6
+ * This includes all domain documentation, filter types, examples, and rules.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.SEARCH_CRITERIA_DOMAIN_NAMES = exports.SEARCH_CRITERIA_DOMAINS = void 0;
10
+ exports.buildSearchCriteriaContext = buildSearchCriteriaContext;
11
+ exports.buildSearchCriteriaSystemPrompt = buildSearchCriteriaSystemPrompt;
12
+ exports.getDomainContext = getDomainContext;
13
+ exports.buildSearchCriteriaUserPrompt = buildSearchCriteriaUserPrompt;
14
+ const search_criteria_filter_context_1 = require("./search-criteria-filter-context");
15
+ /**
16
+ * All available SearchCriteria domains with their descriptions
17
+ */
18
+ exports.SEARCH_CRITERIA_DOMAINS = [
19
+ {
20
+ name: "query",
21
+ description: 'Geographic scope string (required, e.g., "US", "AZ", "Maricopa County, AZ", "Phoenix, AZ")',
22
+ },
23
+ {
24
+ name: "address",
25
+ responseGroup: "address",
26
+ description: "Address filters for location-based searches",
27
+ },
28
+ {
29
+ name: "assessment",
30
+ responseGroup: "assessment",
31
+ description: "Property assessment and market value filters",
32
+ },
33
+ {
34
+ name: "building",
35
+ responseGroup: "building",
36
+ description: "Building characteristics and features",
37
+ },
38
+ {
39
+ name: "lot",
40
+ responseGroup: "lot",
41
+ description: "Lot characteristics and dimensions",
42
+ },
43
+ {
44
+ name: "owner",
45
+ responseGroup: "owner",
46
+ description: "Owner information and characteristics",
47
+ },
48
+ {
49
+ name: "sale",
50
+ responseGroup: "sale",
51
+ description: "Sale history and transaction information",
52
+ },
53
+ {
54
+ name: "tax",
55
+ responseGroup: "tax",
56
+ description: "Tax information and delinquency status",
57
+ },
58
+ {
59
+ name: "valuation",
60
+ responseGroup: "valuation",
61
+ description: "Property valuation estimates",
62
+ },
63
+ {
64
+ name: "listing",
65
+ responseGroup: "listing",
66
+ description: "Current listing information",
67
+ },
68
+ {
69
+ name: "openLien",
70
+ responseGroup: "openLien",
71
+ description: "Open mortgage and lien information",
72
+ },
73
+ {
74
+ name: "foreclosure",
75
+ responseGroup: "foreclosure",
76
+ description: "Foreclosure status and auction information",
77
+ },
78
+ {
79
+ name: "quickList",
80
+ responseGroup: "quickList",
81
+ description: "Pre-defined quick filters (owner-occupied, vacant, absentee-owner, high-equity, low-equity, free-and-clear, active-listing, recently-sold, preforeclosure, tax-default, etc.). Can be negated with 'not-' prefix.",
82
+ },
83
+ ];
84
+ /**
85
+ * Array of all valid SearchCriteria domain names (for autocomplete)
86
+ * Using `as const` preserves literal types for better IDE autocomplete
87
+ */
88
+ exports.SEARCH_CRITERIA_DOMAIN_NAMES = [
89
+ "query",
90
+ "address",
91
+ "assessment",
92
+ "building",
93
+ "lot",
94
+ "owner",
95
+ "sale",
96
+ "tax",
97
+ "valuation",
98
+ "listing",
99
+ "openLien",
100
+ "foreclosure",
101
+ "quickList",
102
+ ];
103
+ /**
104
+ * Build domain context documentation for specified domains
105
+ * @param domains Array of domain names to include (e.g., ["address", "building", "assessment"])
106
+ * If undefined or empty array, includes all domains (default)
107
+ * Type-safe: only valid domain names from SEARCH_CRITERIA_DOMAINS are allowed
108
+ * @param options Options for customizing the context (applies to all specified domains)
109
+ * @returns Formatted domain documentation string
110
+ * @example
111
+ * ```typescript
112
+ * // Get context for all domains (default)
113
+ * const allContext = buildSearchCriteriaContext();
114
+ *
115
+ * // Get context for specific domains
116
+ * const context = buildSearchCriteriaContext(["address", "building", "assessment"]);
117
+ *
118
+ * // Get context for all domains with options
119
+ * const allContextWithOptions = buildSearchCriteriaContext(undefined, { maxFieldsPerDomain: 5 });
120
+ *
121
+ * // Get context for specific domains with options
122
+ * const selectiveContext = buildSearchCriteriaContext(["address", "building"], { maxFieldsPerDomain: 5 });
123
+ * ```
124
+ */
125
+ function buildSearchCriteriaContext(domains, options) {
126
+ // Default to all domains if undefined or empty array
127
+ const selectedDomains = !domains || domains.length === 0
128
+ ? exports.SEARCH_CRITERIA_DOMAINS.map((d) => d.name)
129
+ : domains;
130
+ // Validate that all specified domains exist
131
+ const validDomainNames = new Set(exports.SEARCH_CRITERIA_DOMAINS.map((d) => d.name));
132
+ const invalidDomains = selectedDomains.filter((d) => !validDomainNames.has(d));
133
+ if (invalidDomains.length > 0) {
134
+ throw new Error(`Invalid domain(s): ${invalidDomains.join(", ")}. Valid domains are: ${Array.from(validDomainNames).join(", ")}`);
135
+ }
136
+ return buildDomainDocumentation(selectedDomains, options || {});
137
+ }
138
+ /**
139
+ * Build complete system prompt for AI to generate SearchCriteria from natural language
140
+ * @param domains Optional domain names to include. If not specified, includes all domains.
141
+ * Only domains that exist in SEARCH_CRITERIA_DOMAINS are allowed
142
+ * @param options Options for customizing the context (applies to all specified domains)
143
+ * @returns Complete system prompt string
144
+ * @example
145
+ * ```typescript
146
+ * // Full context with all domains
147
+ * const systemPrompt = buildSearchCriteriaSystemPrompt();
148
+ *
149
+ * // Selective context for specific domains
150
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(["address", "building", "assessment"]);
151
+ *
152
+ * // All domains with options
153
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(undefined, { maxFieldsPerDomain: 5 });
154
+ *
155
+ * // Specific domains with options
156
+ * const systemPrompt = buildSearchCriteriaSystemPrompt(["address", "building"], { maxFieldsPerDomain: 5 });
157
+ * ```
158
+ */
159
+ function buildSearchCriteriaSystemPrompt(domains, options) {
160
+ // Build domain documentation using the unified function
161
+ const domainDocs = buildSearchCriteriaContext(domains, options);
162
+ return `You are an expert at creating property search criteria for real estate professionals. Your task is to convert natural language requests into structured BatchData.io search criteria.
163
+
164
+ ${domainDocs}
165
+
166
+ FILTER TYPES:
167
+ - StringFilter: { equals?, contains?, startsWith?, endsWith?, inList?, matches? }
168
+ - NumericRangeFilter: { min?, max? }
169
+ - DateRangeFilter: { minDate?, maxDate? }
170
+ - GeoLocationDistance: { latitude, longitude, distanceMiles }
171
+ - GeoLocationBoundingBox: { minLat, maxLat, minLon, maxLon }
172
+ - GeoLocationPolygon: { geoPoints: [{ latitude, longitude }] }
173
+
174
+ IMPORTANT RULES:
175
+ 1. Always include a "query" field with geographic scope (state, county, city, or "US")
176
+ 2. Use numeric ranges (min/max) for numeric filters like yearBuilt, bedroomCount, price, etc.
177
+ 3. Use string filters (equals, contains, inList) for text fields
178
+ 4. Be specific and realistic:
179
+ - "3 bedrooms" = building.bedroomCount: {min: 3, max: 3}
180
+ - "at least 3 bedrooms" = building.bedroomCount: {min: 3}
181
+ - "3-4 bedrooms" = building.bedroomCount: {min: 3, max: 4}
182
+ - "under $400,000" = assessment.totalMarketValue: {max: 400000}
183
+ - "over $500,000" = assessment.totalMarketValue: {min: 500000}
184
+ - "built after 2010" = building.yearBuilt: {min: 2010}
185
+ 5. Extract geographic information and populate both query and address filters appropriately
186
+ 6. Only include domains that are clearly relevant to the prompt
187
+ 7. If modifying existing criteria, merge intelligently - update what's mentioned, keep what's not
188
+
189
+ Return your response as a JSON array. Each object should have:
190
+ - searchCriteria: The complete SearchCriteria object
191
+ - description: A natural language description of what this search criteria finds
192
+
193
+ If the prompt suggests multiple distinct searches, return multiple objects. Otherwise, return a single object.`;
194
+ }
195
+ /**
196
+ * Get context documentation for a specific domain
197
+ * @param domainName The domain name (e.g., "address", "building", "assessment")
198
+ * @param options Options for customizing the context
199
+ * @returns Formatted documentation string for the domain
200
+ * @example
201
+ * ```typescript
202
+ * const addressContext = getDomainContext("address");
203
+ * // Returns formatted documentation for all address fields
204
+ * ```
205
+ */
206
+ function getDomainContext(domainName, options = {}) {
207
+ const { maxFieldsPerDomain, maxExamplesPerField } = options;
208
+ // Find the domain
209
+ const domain = exports.SEARCH_CRITERIA_DOMAINS.find((d) => d.name === domainName);
210
+ if (!domain) {
211
+ return `Domain "${domainName}" not found.`;
212
+ }
213
+ // Build fields documentation
214
+ const fields = [];
215
+ if (domain.responseGroup) {
216
+ // Get filter contexts for this domain
217
+ const filterContexts = (0, search_criteria_filter_context_1.getDomainFilterContexts)(domain.responseGroup);
218
+ // Apply limits if specified
219
+ const limitedContexts = maxFieldsPerDomain
220
+ ? filterContexts.slice(0, maxFieldsPerDomain)
221
+ : filterContexts;
222
+ fields.push(...limitedContexts.map((context) => ({
223
+ name: context.searchCriteriaPath.split(".").pop() ||
224
+ context.searchCriteriaPath,
225
+ type: context.filterType,
226
+ description: context.description,
227
+ filterGuidance: context.filterGuidance,
228
+ examples: maxExamplesPerField
229
+ ? context.examples.slice(0, maxExamplesPerField)
230
+ : context.examples,
231
+ })));
232
+ }
233
+ // Build the documentation string
234
+ let doc = `- ${domain.name}: ${domain.description}`;
235
+ if (fields.length > 0) {
236
+ doc += "\n Available fields:\n";
237
+ for (const field of fields) {
238
+ doc += ` - ${field.name} (${field.type}): ${field.description}\n`;
239
+ if (field.filterGuidance) {
240
+ doc += ` Filter: ${field.filterGuidance}\n`;
241
+ }
242
+ if (field.examples && field.examples.length > 0) {
243
+ doc += ` Examples: ${field.examples.join(", ")}\n`;
244
+ }
245
+ }
246
+ }
247
+ return doc;
248
+ }
249
+ /**
250
+ * Build domain documentation string for specified domains
251
+ * @param domainNames Array of domain names to include
252
+ * @param options Options for customizing the context
253
+ */
254
+ function buildDomainDocumentation(domainNames, options = {}) {
255
+ const { maxFieldsPerDomain, maxExamplesPerField } = options;
256
+ // Filter domains to only include specified ones
257
+ const selectedDomains = exports.SEARCH_CRITERIA_DOMAINS.filter((domain) => domainNames.includes(domain.name));
258
+ // Ensure query is always first if included
259
+ const queryDomain = selectedDomains.find((d) => d.name === "query");
260
+ const otherDomains = selectedDomains.filter((d) => d.name !== "query");
261
+ const orderedDomains = queryDomain
262
+ ? [queryDomain, ...otherDomains]
263
+ : otherDomains;
264
+ const domains = orderedDomains.map((domain) => ({
265
+ ...domain,
266
+ fields: [],
267
+ }));
268
+ // Populate fields from metadata and filter context for each domain
269
+ for (const domain of domains) {
270
+ if (domain.responseGroup) {
271
+ // Get filter contexts for this domain (includes filter type and examples)
272
+ const filterContexts = (0, search_criteria_filter_context_1.getDomainFilterContexts)(domain.responseGroup);
273
+ // Apply limits if specified
274
+ const limitedContexts = maxFieldsPerDomain
275
+ ? filterContexts.slice(0, maxFieldsPerDomain)
276
+ : filterContexts;
277
+ domain.fields = limitedContexts.map((context) => ({
278
+ name: context.searchCriteriaPath.split(".").pop() ||
279
+ context.searchCriteriaPath,
280
+ type: context.filterType,
281
+ description: context.description,
282
+ filterGuidance: context.filterGuidance,
283
+ examples: maxExamplesPerField
284
+ ? context.examples.slice(0, maxExamplesPerField)
285
+ : context.examples,
286
+ }));
287
+ }
288
+ }
289
+ // Build the documentation string
290
+ let doc = "";
291
+ const queryDomainItem = domains.find((d) => d.name === "query");
292
+ const optionalDomains = domains.filter((d) => d.name !== "query");
293
+ if (queryDomainItem) {
294
+ doc += "REQUIRED:\n";
295
+ doc += `- query: ${queryDomainItem.description}\n`;
296
+ if (optionalDomains.length > 0) {
297
+ doc += "\nOPTIONAL DOMAINS:\n";
298
+ }
299
+ }
300
+ else if (optionalDomains.length > 0) {
301
+ doc += "OPTIONAL DOMAINS:\n";
302
+ }
303
+ for (const domain of optionalDomains) {
304
+ doc += `\n- ${domain.name}: ${domain.description}`;
305
+ if (domain.fields.length > 0) {
306
+ doc += "\n Available fields:\n";
307
+ for (const field of domain.fields) {
308
+ doc += ` - ${field.name} (${field.type}): ${field.description}\n`;
309
+ if (field.filterGuidance) {
310
+ doc += ` Filter: ${field.filterGuidance}\n`;
311
+ }
312
+ if (field.examples && field.examples.length > 0) {
313
+ doc += ` Examples: ${field.examples.join(", ")}\n`;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ return doc;
319
+ }
320
+ /**
321
+ * Build user prompt with existing criteria context
322
+ * @param userPrompt The user's natural language prompt
323
+ * @param existingCriteria Optional existing search criteria to modify
324
+ * @returns Formatted user prompt string
325
+ */
326
+ function buildSearchCriteriaUserPrompt(userPrompt, existingCriteria) {
327
+ const basePrompt = `Example response format:
328
+ [
329
+ {
330
+ "searchCriteria": {
331
+ "query": "Maricopa County, AZ",
332
+ "building": {
333
+ "bedroomCount": { "min": 3, "max": 3 },
334
+ "yearBuilt": { "min": 2000 }
335
+ },
336
+ "assessment": {
337
+ "totalMarketValue": { "max": 500000 }
338
+ }
339
+ },
340
+ "description": "3-bedroom homes built after 2000 in Maricopa County, Arizona, with a market value under $500,000"
341
+ }
342
+ ]
343
+
344
+ ${existingCriteria ? `\nEXISTING SEARCH CRITERIA TO MODIFY:\n${JSON.stringify(existingCriteria, null, 2)}\n\nModify the existing criteria based on the user's prompt. Merge intelligently - update fields mentioned in the prompt, preserve others.` : ""}
345
+
346
+ USER PROMPT:
347
+ ${userPrompt}
348
+
349
+ Return only valid JSON, no markdown formatting or code blocks.`;
350
+ return basePrompt;
351
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * SearchCriteria Filter Context
3
+ *
4
+ * Provides context about how filters should work for each SearchCriteria field.
5
+ * This includes filter type information and usage guidance.
6
+ */
7
+ import type { PropertyFieldMetadata } from "./metadata";
8
+ /**
9
+ * Filter type for a SearchCriteria field
10
+ */
11
+ export type SearchCriteriaFilterType = "StringFilter" | "NumericRangeFilter" | "DateRangeFilter" | "QuickListValue";
12
+ /**
13
+ * Context about how a SearchCriteria field should be filtered
14
+ */
15
+ export interface SearchCriteriaFilterContext {
16
+ /**
17
+ * The SearchCriteria field path (e.g., "address.city", "building.yearBuilt")
18
+ */
19
+ searchCriteriaPath: string;
20
+ /**
21
+ * The type of filter to use (StringFilter, NumericRangeFilter, DateRangeFilter)
22
+ */
23
+ filterType: SearchCriteriaFilterType;
24
+ /**
25
+ * The corresponding Property field path, if any
26
+ */
27
+ propertyPath: string | undefined;
28
+ /**
29
+ * Property field metadata, if available
30
+ */
31
+ propertyMetadata: PropertyFieldMetadata | undefined;
32
+ /**
33
+ * Human-readable description of the field and how to filter it
34
+ */
35
+ description: string;
36
+ /**
37
+ * Example filter values or usage patterns
38
+ */
39
+ examples: string[];
40
+ /**
41
+ * Additional context about how this filter should be used
42
+ */
43
+ filterGuidance: string;
44
+ }
45
+ /**
46
+ * Get filter context for a SearchCriteria field path
47
+ * @param searchCriteriaPath The SearchCriteria field path (e.g., "address.city", "building.yearBuilt", "quickList")
48
+ * @returns Filter context including filter type, examples, and guidance
49
+ * @example
50
+ * ```typescript
51
+ * const context = getSearchCriteriaFilterContext("building.bedroomCount");
52
+ * console.log(context.filterType); // "NumericRangeFilter"
53
+ * console.log(context.examples); // ["{ min: 3, max: 3 } // exactly 3", ...]
54
+ * console.log(context.filterGuidance); // "Use NumericRangeFilter with: min, max..."
55
+ *
56
+ * const quickListContext = getSearchCriteriaFilterContext("quickList");
57
+ * console.log(quickListContext.filterType); // "QuickListValue"
58
+ * ```
59
+ */
60
+ export declare function getSearchCriteriaFilterContext(searchCriteriaPath: string): SearchCriteriaFilterContext;
61
+ /**
62
+ * Get a formatted string with complete filter context for a SearchCriteria field
63
+ * This is useful for generating documentation or AI prompts
64
+ * @param searchCriteriaPath The SearchCriteria field path
65
+ * @returns A formatted string with all filter context information
66
+ * @example
67
+ * ```typescript
68
+ * const contextString = getSearchCriteriaFilterContextString("building.bedroomCount");
69
+ * // Returns a formatted string with filter type, examples, and guidance
70
+ * ```
71
+ */
72
+ export declare function getSearchCriteriaFilterContextString(searchCriteriaPath: string): string;
73
+ /**
74
+ * Get filter context for all fields in a SearchCriteria domain/group
75
+ * @param domainName The domain name (e.g., "address", "building", "assessment", "quickList")
76
+ * @returns Array of filter contexts for all fields in that domain
77
+ * @example
78
+ * ```typescript
79
+ * const addressFields = getDomainFilterContexts("address");
80
+ * // Returns filter contexts for all address.* fields
81
+ *
82
+ * const quickListContexts = getDomainFilterContexts("quickList");
83
+ * // Returns contexts for quickList, quickLists, and orQuickLists
84
+ * ```
85
+ */
86
+ export declare function getDomainFilterContexts(domainName: string): SearchCriteriaFilterContext[];
87
+ /**
88
+ * Get a formatted documentation string for all fields in a domain
89
+ * @param domainName The domain name
90
+ * @returns A formatted string with filter context for all fields in the domain
91
+ */
92
+ export declare function getDomainFilterContextDocumentation(domainName: string): string;
@@ -0,0 +1,550 @@
1
+ "use strict";
2
+ /**
3
+ * SearchCriteria Filter Context
4
+ *
5
+ * Provides context about how filters should work for each SearchCriteria field.
6
+ * This includes filter type information and usage guidance.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getSearchCriteriaFilterContext = getSearchCriteriaFilterContext;
10
+ exports.getSearchCriteriaFilterContextString = getSearchCriteriaFilterContextString;
11
+ exports.getDomainFilterContexts = getDomainFilterContexts;
12
+ exports.getDomainFilterContextDocumentation = getDomainFilterContextDocumentation;
13
+ const metadata_1 = require("./metadata");
14
+ /**
15
+ * Mapping of SearchCriteria field paths to their filter types
16
+ * This is derived from the SearchCriteria type definitions
17
+ */
18
+ const SEARCH_CRITERIA_FILTER_TYPE_MAP = {
19
+ // Address fields - StringFilter
20
+ "address.street": "StringFilter",
21
+ "address.city": "StringFilter",
22
+ "address.locality": "StringFilter",
23
+ "address.county": "StringFilter",
24
+ "address.countyFipsCode": "StringFilter",
25
+ "address.state": "StringFilter",
26
+ "address.zip": "StringFilter",
27
+ "address.unitType": "StringFilter",
28
+ "address.formattedStreet": "StringFilter",
29
+ "address.streetNoUnit": "StringFilter",
30
+ "address.initialGeoStatus": "StringFilter",
31
+ "address.geoStatus": "StringFilter",
32
+ "address.cityState": "StringFilter",
33
+ "address.countyState": "StringFilter",
34
+ // Assessment fields - NumericRangeFilter
35
+ "assessment.assessmentYear": "NumericRangeFilter",
36
+ "assessment.totalAssessedValue": "NumericRangeFilter",
37
+ "assessment.assessedImprovementValue": "NumericRangeFilter",
38
+ "assessment.assessedLandValue": "NumericRangeFilter",
39
+ "assessment.marketValueYear": "NumericRangeFilter",
40
+ "assessment.landMarketValue": "NumericRangeFilter",
41
+ "assessment.improvementMarketValue": "NumericRangeFilter",
42
+ "assessment.totalMarketValue": "NumericRangeFilter",
43
+ // Building fields - mix of StringFilter and NumericRangeFilter
44
+ "building.airConditioningSource": "StringFilter",
45
+ "building.basementType": "StringFilter",
46
+ "building.totalBuildingAreaSquareFeet": "NumericRangeFilter",
47
+ "building.buildingClass": "StringFilter",
48
+ "building.buildingCondition": "StringFilter",
49
+ "building.buildingQuality": "StringFilter",
50
+ "building.buildingType": "StringFilter",
51
+ "building.driveway": "StringFilter",
52
+ "building.exteriorWalls": "StringFilter",
53
+ "building.floorCover": "StringFilter",
54
+ "building.garageParkingSpaceCount": "NumericRangeFilter",
55
+ "building.garage": "StringFilter",
56
+ "building.heatSource": "StringFilter",
57
+ "building.heatingFuelType": "StringFilter",
58
+ "building.interiorWalls": "StringFilter",
59
+ "building.buildingCount": "NumericRangeFilter",
60
+ "building.bathroomCount": "NumericRangeFilter",
61
+ "building.calculatedBathroomCount": "NumericRangeFilter",
62
+ "building.bedroomCount": "NumericRangeFilter",
63
+ "building.patio": "StringFilter",
64
+ "building.storyCount": "NumericRangeFilter",
65
+ "building.features": "StringFilter",
66
+ "building.residentialUnitCount": "NumericRangeFilter",
67
+ "building.pool": "StringFilter",
68
+ "building.porch": "StringFilter",
69
+ "building.roofCover": "StringFilter",
70
+ "building.roofType": "StringFilter",
71
+ "building.sewer": "StringFilter",
72
+ "building.style": "StringFilter",
73
+ "building.roomCount": "NumericRangeFilter",
74
+ "building.unitCount": "NumericRangeFilter",
75
+ "building.constructionType": "StringFilter",
76
+ "building.waterService": "StringFilter",
77
+ "building.yearBuilt": "NumericRangeFilter",
78
+ // Demographics - mix
79
+ "demographics.age": "NumericRangeFilter",
80
+ "demographics.householdSize": "NumericRangeFilter",
81
+ "demographics.income": "NumericRangeFilter",
82
+ "demographics.netWorth": "NumericRangeFilter",
83
+ "demographics.discretionaryIncome": "NumericRangeFilter",
84
+ "demographics.homeownerRenter": "StringFilter",
85
+ "demographics.businessOwner": "StringFilter",
86
+ "demographics.gender": "StringFilter",
87
+ "demographics.investments": "StringFilter",
88
+ "demographics.demographics": "StringFilter",
89
+ "demographics.religiousAffiliation": "StringFilter",
90
+ // Foreclosure - mix
91
+ "foreclosure.status": "StringFilter",
92
+ "foreclosure.recordingDate": "DateRangeFilter",
93
+ "foreclosure.auctionDate": "DateRangeFilter",
94
+ "foreclosure.releaseDate": "DateRangeFilter",
95
+ "foreclosure.auctionMinimumBidAmount": "NumericRangeFilter",
96
+ "foreclosure.pastDueAmount": "NumericRangeFilter",
97
+ // General - StringFilter
98
+ "general.propertyTypeCategory": "StringFilter",
99
+ "general.propertyTypeDetail": "StringFilter",
100
+ // IDs - StringFilter
101
+ "ids.addressHash": "StringFilter",
102
+ "ids.mailingAddressHash": "StringFilter",
103
+ "ids.fipsCode": "StringFilter",
104
+ "ids.apn": "StringFilter",
105
+ "ids.taxId": "StringFilter",
106
+ // Intel - mix
107
+ "intel.lastSoldDate": "DateRangeFilter",
108
+ "intel.lastSoldPrice": "NumericRangeFilter",
109
+ "intel.salePropensity": "NumericRangeFilter",
110
+ // Involuntary Lien - mix
111
+ "involuntaryLien.lienType": "StringFilter",
112
+ "involuntaryLien.lienTypeCode": "StringFilter",
113
+ "involuntaryLien.documentType": "StringFilter",
114
+ "involuntaryLien.documentTypeCode": "StringFilter",
115
+ "involuntaryLien.recordingDate": "DateRangeFilter",
116
+ "involuntaryLien.filingDate": "DateRangeFilter",
117
+ "involuntaryLien.lienAmount": "NumericRangeFilter",
118
+ // Legal - StringFilter
119
+ "legal.subdivisionName": "StringFilter",
120
+ // Listing - mix
121
+ "listing.description": "StringFilter",
122
+ "listing.price": "NumericRangeFilter",
123
+ "listing.daysOnMarket": "NumericRangeFilter",
124
+ "listing.status": "StringFilter",
125
+ "listing.statusCategory": "StringFilter",
126
+ "listing.failedListingDate": "DateRangeFilter",
127
+ "listing.soldDate": "DateRangeFilter",
128
+ // Lot - mix
129
+ "lot.lotSizeAcres": "NumericRangeFilter",
130
+ "lot.lotDepthFeet": "NumericRangeFilter",
131
+ "lot.lotFrontageFeet": "NumericRangeFilter",
132
+ "lot.lotSizeSquareFeet": "NumericRangeFilter",
133
+ "lot.zoningCode": "StringFilter",
134
+ // Open Lien - mix
135
+ "openLien.allLoanTypes": "StringFilter",
136
+ "openLien.juniorLoanTypes": "StringFilter",
137
+ "openLien.totalOpenLienCount": "NumericRangeFilter",
138
+ "openLien.totalOpenLienBalance": "NumericRangeFilter",
139
+ "openLien.firstLoanLtv": "NumericRangeFilter",
140
+ "openLien.firstLoanInterestRate": "NumericRangeFilter",
141
+ "openLien.secondLoanInterestRate": "NumericRangeFilter",
142
+ "openLien.thirdLoanInterestRate": "NumericRangeFilter",
143
+ "openLien.fourthLoanInterestRate": "NumericRangeFilter",
144
+ "openLien.firstLoanType": "StringFilter",
145
+ "openLien.secondLoanType": "StringFilter",
146
+ "openLien.thirdLoanType": "StringFilter",
147
+ "openLien.fourthLoanType": "StringFilter",
148
+ "openLien.firstLoanRecordingDate": "DateRangeFilter",
149
+ "openLien.lastLoanRecordingDate": "DateRangeFilter",
150
+ // Owner - mix
151
+ "owner.firstName": "StringFilter",
152
+ "owner.lastName": "StringFilter",
153
+ "owner.mailingStreet": "StringFilter",
154
+ "owner.mailingCity": "StringFilter",
155
+ "owner.mailingState": "StringFilter",
156
+ "owner.mailingZip": "StringFilter",
157
+ "owner.ownerStatusType": "StringFilter",
158
+ "owner.lengthOfResidenceMonths": "NumericRangeFilter",
159
+ "owner.lengthOfResidenceYears": "NumericRangeFilter",
160
+ "owner.ownershipStartDate": "DateRangeFilter",
161
+ "owner.mailingAddressHash": "StringFilter",
162
+ // Permit - mix
163
+ "permit.permitCount": "NumericRangeFilter",
164
+ "permit.latestDate": "DateRangeFilter",
165
+ "permit.earliestDate": "DateRangeFilter",
166
+ "permit.totalJobValue": "NumericRangeFilter",
167
+ "permit.allTags": "StringFilter",
168
+ // Property Owner Profile - NumericRangeFilter
169
+ "propertyOwnerProfile.propertiesCount": "NumericRangeFilter",
170
+ "propertyOwnerProfile.propertiesTotalEquity": "NumericRangeFilter",
171
+ "propertyOwnerProfile.propertiesTotalEstimatedValue": "NumericRangeFilter",
172
+ "propertyOwnerProfile.mortgagesTotalBalance": "NumericRangeFilter",
173
+ "propertyOwnerProfile.mortgagesCount": "NumericRangeFilter",
174
+ "propertyOwnerProfile.mortgagesAverageBalance": "NumericRangeFilter",
175
+ // Sale - mix
176
+ "sale.lastSalePrice": "NumericRangeFilter",
177
+ "sale.lastSaleDate": "DateRangeFilter",
178
+ "sale.lastSaleDocumentType": "StringFilter",
179
+ "sale.lastSalePricePerSquareFoot": "NumericRangeFilter",
180
+ "sale.flipLength": "NumericRangeFilter",
181
+ "sale.flipLengthCategory": "NumericRangeFilter",
182
+ "sale.flipProfit": "NumericRangeFilter",
183
+ // Tax - NumericRangeFilter
184
+ "tax.taxDelinquentYear": "NumericRangeFilter",
185
+ // Valuation - NumericRangeFilter
186
+ "valuation.estimatedValue": "NumericRangeFilter",
187
+ "valuation.ltv": "NumericRangeFilter",
188
+ "valuation.equityPercent": "NumericRangeFilter",
189
+ };
190
+ /**
191
+ * Get filter guidance text based on filter type
192
+ */
193
+ function getFilterGuidance(filterType) {
194
+ switch (filterType) {
195
+ case "StringFilter":
196
+ return `Use StringFilter with: equals (exact match), contains (substring), startsWith, endsWith, inList (array of values), or matches (regex). Example: { equals: "Phoenix" } or { inList: ["Phoenix", "Tucson"] }`;
197
+ case "NumericRangeFilter":
198
+ return `Use NumericRangeFilter with: min (minimum value), max (maximum value), or both. Example: { min: 3, max: 5 } for "3-5 bedrooms" or { min: 2010 } for "built after 2010"`;
199
+ case "DateRangeFilter":
200
+ return `Use DateRangeFilter with: minDate (earliest date), maxDate (latest date), or both. Dates should be in ISO 8601 format (YYYY-MM-DD). Example: { minDate: "2020-01-01" } for "after 2020"`;
201
+ case "QuickListValue":
202
+ return `Use a QuickListValue string. Can be prefixed with "not-" to exclude. Available as: quickList (single value), quickLists (array), orQuickLists (array for OR logic). Example: "owner-occupied" or "not-vacant"`;
203
+ default:
204
+ return "Unknown filter type";
205
+ }
206
+ }
207
+ /**
208
+ * Get example values based on field and filter type
209
+ */
210
+ function getExamples(fieldPath, filterType, _propertyMetadata) {
211
+ const examples = [];
212
+ switch (filterType) {
213
+ case "StringFilter":
214
+ if (fieldPath.includes("city")) {
215
+ examples.push('{ equals: "Phoenix" }');
216
+ examples.push('{ inList: ["Phoenix", "Tucson", "Mesa"] }');
217
+ }
218
+ else if (fieldPath.includes("state")) {
219
+ examples.push('{ equals: "AZ" }');
220
+ }
221
+ else if (fieldPath.includes("status")) {
222
+ examples.push('{ equals: "active" }');
223
+ }
224
+ else {
225
+ examples.push('{ equals: "value" }');
226
+ examples.push('{ contains: "substring" }');
227
+ }
228
+ break;
229
+ case "NumericRangeFilter":
230
+ if (fieldPath.includes("bedroom") || fieldPath.includes("bathroom")) {
231
+ examples.push("{ min: 3, max: 3 } // exactly 3");
232
+ examples.push("{ min: 3 } // at least 3");
233
+ examples.push("{ min: 3, max: 5 } // between 3 and 5");
234
+ }
235
+ else if (fieldPath.includes("yearBuilt")) {
236
+ examples.push("{ min: 2010 } // built after 2010");
237
+ examples.push("{ max: 2000 } // built before 2000");
238
+ }
239
+ else if (fieldPath.includes("Value") || fieldPath.includes("Price")) {
240
+ examples.push("{ max: 400000 } // under $400,000");
241
+ examples.push("{ min: 500000 } // over $500,000");
242
+ examples.push("{ min: 300000, max: 500000 } // between $300k and $500k");
243
+ }
244
+ else {
245
+ examples.push("{ min: 0 }");
246
+ examples.push("{ max: 100 }");
247
+ examples.push("{ min: 10, max: 50 }");
248
+ }
249
+ break;
250
+ case "DateRangeFilter":
251
+ examples.push('{ minDate: "2020-01-01" } // after January 1, 2020');
252
+ examples.push('{ maxDate: "2023-12-31" } // before December 31, 2023');
253
+ examples.push('{ minDate: "2020-01-01", maxDate: "2023-12-31" } // between dates');
254
+ break;
255
+ }
256
+ return examples;
257
+ }
258
+ /**
259
+ * Valid QuickList values with descriptions (from core/types.ts and metadata)
260
+ */
261
+ const QUICK_LIST_VALUES_WITH_DESCRIPTIONS = [
262
+ {
263
+ value: "absentee-owner",
264
+ description: "Property owner does not reside at the property",
265
+ },
266
+ {
267
+ value: "active-auction",
268
+ description: "Property has an auction date greater than the current date",
269
+ },
270
+ {
271
+ value: "active-listing",
272
+ description: "Property has an active listing",
273
+ },
274
+ {
275
+ value: "canceled-listing",
276
+ description: "Property has a canceled listing",
277
+ },
278
+ {
279
+ value: "cash-buyer",
280
+ description: "Property was purchased with cash",
281
+ },
282
+ {
283
+ value: "corporate-owned",
284
+ description: "Property is owned by a corporate entity (i.e., not an individual)",
285
+ },
286
+ {
287
+ value: "expired-listing",
288
+ description: "Property has an expired listing",
289
+ },
290
+ {
291
+ value: "failed-listing",
292
+ description: "The listing was either cancelled or expired and not sold",
293
+ },
294
+ {
295
+ value: "fix-and-flip",
296
+ description: "Property has been bought and sold in the last 12 months",
297
+ },
298
+ {
299
+ value: "free-and-clear",
300
+ description: "Property is free of any mortgage",
301
+ },
302
+ {
303
+ value: "for-sale-by-owner",
304
+ description: "Property is listed for sale by the owner",
305
+ },
306
+ {
307
+ value: "has-hoa",
308
+ description: "Property is part of a homeowner association",
309
+ },
310
+ {
311
+ value: "has-hoa-fees",
312
+ description: "Property has homeowner association fees",
313
+ },
314
+ {
315
+ value: "high-equity",
316
+ description: "Property owner has more than 20% equity in the property",
317
+ },
318
+ {
319
+ value: "inherited",
320
+ description: "Property owner has inherited the property",
321
+ },
322
+ {
323
+ value: "involuntary-lien",
324
+ description: "Property has an involuntary lien recorded against it, indicating a legal claim due to unpaid debts or obligations",
325
+ },
326
+ {
327
+ value: "in-state-absentee-owner",
328
+ description: "Property owner does not reside at the property, and the property address and owner mailing address are in the same state",
329
+ },
330
+ {
331
+ value: "listed-below-market-price",
332
+ description: "Property is listed below market price",
333
+ },
334
+ {
335
+ value: "low-equity",
336
+ description: "Property owner has less than 20% equity in the property",
337
+ },
338
+ {
339
+ value: "mailing-address-vacant",
340
+ description: "Property mailing address is vacant",
341
+ },
342
+ {
343
+ value: "notice-of-default",
344
+ description: "Property has a default of a loan by the borrower of the mortgage",
345
+ },
346
+ {
347
+ value: "notice-of-lis-pendens",
348
+ description: "Property has a pending lawsuit",
349
+ },
350
+ {
351
+ value: "notice-of-sale",
352
+ description: "Property has received a notice of sale",
353
+ },
354
+ {
355
+ value: "on-market",
356
+ description: "Property is available for sale",
357
+ },
358
+ {
359
+ value: "out-of-state-absentee-owner",
360
+ description: "Property owner does not reside at the property, and the property address and owner mailing address are in different states",
361
+ },
362
+ {
363
+ value: "out-of-state-owner",
364
+ description: "Property owner resides out of state",
365
+ },
366
+ {
367
+ value: "owner-occupied",
368
+ description: "Property is occupied by the owner",
369
+ },
370
+ {
371
+ value: "pending-listing",
372
+ description: "Property listing is pending (not yet active)",
373
+ },
374
+ {
375
+ value: "preforeclosure",
376
+ description: "Property is in pre-foreclosure",
377
+ },
378
+ {
379
+ value: "recently-sold",
380
+ description: "Property was sold in the last 12 months",
381
+ },
382
+ {
383
+ value: "same-property-and-mailing-address",
384
+ description: "Property address is the same as the mailing address",
385
+ },
386
+ {
387
+ value: "tax-default",
388
+ description: "Property is in tax default and the first tax default was at least three years ago",
389
+ },
390
+ {
391
+ value: "tired-landlord",
392
+ description: "Property is a single family residence owned for at least 10 years",
393
+ },
394
+ {
395
+ value: "unknown-equity",
396
+ description: "Property equity is unknown",
397
+ },
398
+ {
399
+ value: "vacant",
400
+ description: "USPS has flagged the property as vacant",
401
+ },
402
+ {
403
+ value: "vacant-lot",
404
+ description: "Property is likely to be a vacant lot",
405
+ },
406
+ {
407
+ value: "senior-owner",
408
+ description: "An individual property owner who resides in the property for 25 or more years, has senior tax exemption, or currently holds an open reverse mortgage",
409
+ },
410
+ {
411
+ value: "trust-owned",
412
+ description: "Property is owned by a trust",
413
+ },
414
+ ];
415
+ /**
416
+ * Get filter context for a SearchCriteria field path
417
+ * @param searchCriteriaPath The SearchCriteria field path (e.g., "address.city", "building.yearBuilt", "quickList")
418
+ * @returns Filter context including filter type, examples, and guidance
419
+ * @example
420
+ * ```typescript
421
+ * const context = getSearchCriteriaFilterContext("building.bedroomCount");
422
+ * console.log(context.filterType); // "NumericRangeFilter"
423
+ * console.log(context.examples); // ["{ min: 3, max: 3 } // exactly 3", ...]
424
+ * console.log(context.filterGuidance); // "Use NumericRangeFilter with: min, max..."
425
+ *
426
+ * const quickListContext = getSearchCriteriaFilterContext("quickList");
427
+ * console.log(quickListContext.filterType); // "QuickListValue"
428
+ * ```
429
+ */
430
+ function getSearchCriteriaFilterContext(searchCriteriaPath) {
431
+ // Handle special quickList fields
432
+ if (searchCriteriaPath === "quickList" ||
433
+ searchCriteriaPath === "quickLists" ||
434
+ searchCriteriaPath === "orQuickLists") {
435
+ return {
436
+ searchCriteriaPath,
437
+ filterType: "QuickListValue",
438
+ propertyPath: undefined,
439
+ propertyMetadata: undefined,
440
+ description: `Pre-defined quick filter${searchCriteriaPath === "quickList" ? "" : "s"} for common property characteristics. Values can be prefixed with "not-" to exclude (e.g., "not-vacant" excludes vacant properties). Use ${searchCriteriaPath === "orQuickLists" ? "orQuickLists array for OR logic" : searchCriteriaPath === "quickLists" ? "quickLists array for multiple values" : "quickList for a single value"}.`,
441
+ examples: [
442
+ ...QUICK_LIST_VALUES_WITH_DESCRIPTIONS.slice(0, 5).map((item) => `"${item.value}" // ${item.description}`),
443
+ '"not-vacant" // Exclude vacant properties',
444
+ '"not-owner-occupied" // Exclude owner-occupied properties',
445
+ `\nAll available QuickList values (${QUICK_LIST_VALUES_WITH_DESCRIPTIONS.length} total):\n${QUICK_LIST_VALUES_WITH_DESCRIPTIONS.map((item) => ` - "${item.value}": ${item.description}`).join("\n")}`,
446
+ ],
447
+ filterGuidance: getFilterGuidance("QuickListValue"),
448
+ };
449
+ }
450
+ const filterType = SEARCH_CRITERIA_FILTER_TYPE_MAP[searchCriteriaPath] || "StringFilter";
451
+ // Try to get Property field metadata
452
+ const propertyMetadata = (0, metadata_1.getFieldMetadata)(searchCriteriaPath);
453
+ const propertyPath = propertyMetadata ? searchCriteriaPath : undefined;
454
+ // Build description
455
+ let description;
456
+ if (propertyMetadata) {
457
+ description = `Filter on ${propertyMetadata.description}`;
458
+ }
459
+ else {
460
+ const parts = searchCriteriaPath.split(".");
461
+ const fieldName = parts[parts.length - 1] || searchCriteriaPath;
462
+ description = `Filter on ${fieldName} field`;
463
+ }
464
+ // Get examples and guidance
465
+ const examples = getExamples(searchCriteriaPath, filterType, propertyMetadata);
466
+ const filterGuidance = getFilterGuidance(filterType);
467
+ return {
468
+ searchCriteriaPath,
469
+ filterType,
470
+ propertyPath,
471
+ propertyMetadata: propertyMetadata || undefined,
472
+ description,
473
+ examples,
474
+ filterGuidance,
475
+ };
476
+ }
477
+ /**
478
+ * Get a formatted string with complete filter context for a SearchCriteria field
479
+ * This is useful for generating documentation or AI prompts
480
+ * @param searchCriteriaPath The SearchCriteria field path
481
+ * @returns A formatted string with all filter context information
482
+ * @example
483
+ * ```typescript
484
+ * const contextString = getSearchCriteriaFilterContextString("building.bedroomCount");
485
+ * // Returns a formatted string with filter type, examples, and guidance
486
+ * ```
487
+ */
488
+ function getSearchCriteriaFilterContextString(searchCriteriaPath) {
489
+ const context = getSearchCriteriaFilterContext(searchCriteriaPath);
490
+ const parts = [];
491
+ parts.push(`Field: ${context.searchCriteriaPath}`);
492
+ parts.push(`Filter Type: ${context.filterType}`);
493
+ parts.push(`Description: ${context.description}`);
494
+ if (context.propertyMetadata) {
495
+ parts.push(`Property Field: ${context.propertyPath} (${context.propertyMetadata.dataType})`);
496
+ }
497
+ parts.push(`\nFilter Guidance:\n${context.filterGuidance}`);
498
+ if (context.examples.length > 0) {
499
+ parts.push(`\nExamples:`);
500
+ context.examples.forEach((example) => {
501
+ parts.push(` ${example}`);
502
+ });
503
+ }
504
+ return parts.join("\n");
505
+ }
506
+ /**
507
+ * Get filter context for all fields in a SearchCriteria domain/group
508
+ * @param domainName The domain name (e.g., "address", "building", "assessment", "quickList")
509
+ * @returns Array of filter contexts for all fields in that domain
510
+ * @example
511
+ * ```typescript
512
+ * const addressFields = getDomainFilterContexts("address");
513
+ * // Returns filter contexts for all address.* fields
514
+ *
515
+ * const quickListContexts = getDomainFilterContexts("quickList");
516
+ * // Returns contexts for quickList, quickLists, and orQuickLists
517
+ * ```
518
+ */
519
+ function getDomainFilterContexts(domainName) {
520
+ const contexts = [];
521
+ // Handle special quickList domain
522
+ if (domainName === "quickList") {
523
+ contexts.push(getSearchCriteriaFilterContext("quickList"));
524
+ contexts.push(getSearchCriteriaFilterContext("quickLists"));
525
+ contexts.push(getSearchCriteriaFilterContext("orQuickLists"));
526
+ return contexts;
527
+ }
528
+ // Find all fields that start with the domain name
529
+ for (const fieldPath in SEARCH_CRITERIA_FILTER_TYPE_MAP) {
530
+ if (fieldPath.startsWith(`${domainName}.`)) {
531
+ contexts.push(getSearchCriteriaFilterContext(fieldPath));
532
+ }
533
+ }
534
+ return contexts.sort((a, b) => a.searchCriteriaPath.localeCompare(b.searchCriteriaPath));
535
+ }
536
+ /**
537
+ * Get a formatted documentation string for all fields in a domain
538
+ * @param domainName The domain name
539
+ * @returns A formatted string with filter context for all fields in the domain
540
+ */
541
+ function getDomainFilterContextDocumentation(domainName) {
542
+ const contexts = getDomainFilterContexts(domainName);
543
+ const parts = [];
544
+ parts.push(`\n${domainName.toUpperCase()} DOMAIN:`);
545
+ parts.push("=".repeat(50));
546
+ for (const context of contexts) {
547
+ parts.push(`\n${getSearchCriteriaFilterContextString(context.searchCriteriaPath)}`);
548
+ }
549
+ return parts.join("\n");
550
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@land-catalyst/batch-data-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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",