@propertysimple/client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +79 -0
- package/README.md +348 -0
- package/client/src/directory-sync.d.ts +123 -0
- package/client/src/directory-sync.js +404 -0
- package/client/src/index.d.ts +5123 -0
- package/client/src/index.js +285 -0
- package/client/src/managed-sync.d.ts +129 -0
- package/client/src/managed-sync.js +410 -0
- package/contracts/src/catalog-derived.d.ts +7 -0
- package/contracts/src/catalog-derived.js +78 -0
- package/contracts/src/catalog-fields.d.ts +231 -0
- package/contracts/src/catalog-fields.js +194 -0
- package/contracts/src/catalog-inventory.d.ts +14 -0
- package/contracts/src/catalog-inventory.js +10 -0
- package/contracts/src/directory-execution.d.ts +320 -0
- package/contracts/src/directory-execution.js +122 -0
- package/contracts/src/directory-identity.d.ts +1255 -0
- package/contracts/src/directory-identity.js +129 -0
- package/contracts/src/directory-sales.d.ts +333 -0
- package/contracts/src/directory-sales.js +102 -0
- package/contracts/src/directory.d.ts +1088 -0
- package/contracts/src/directory.js +179 -0
- package/contracts/src/index.d.ts +18 -0
- package/contracts/src/index.js +18 -0
- package/contracts/src/intelligence.d.ts +366 -0
- package/contracts/src/intelligence.js +157 -0
- package/contracts/src/location-serving.d.ts +13 -0
- package/contracts/src/location-serving.js +89 -0
- package/contracts/src/location.d.ts +73 -0
- package/contracts/src/location.js +75 -0
- package/contracts/src/openapi.d.ts +22 -0
- package/contracts/src/openapi.js +109 -0
- package/contracts/src/operations.d.ts +8316 -0
- package/contracts/src/operations.js +56 -0
- package/contracts/src/opportunity-events.d.ts +38 -0
- package/contracts/src/opportunity-events.js +69 -0
- package/contracts/src/platform-api.d.ts +12 -0
- package/contracts/src/platform-api.js +1 -0
- package/contracts/src/professional-intelligence.d.ts +256 -0
- package/contracts/src/professional-intelligence.js +90 -0
- package/contracts/src/property-features.d.ts +94 -0
- package/contracts/src/property-features.js +398 -0
- package/contracts/src/publication.d.ts +44 -0
- package/contracts/src/publication.js +42 -0
- package/contracts/src/routes.d.ts +437 -0
- package/contracts/src/routes.js +496 -0
- package/contracts/src/schemas.d.ts +3198 -0
- package/contracts/src/schemas.js +674 -0
- package/contracts/src/search.d.ts +2630 -0
- package/contracts/src/search.js +223 -0
- package/contracts/src/smart-feed.d.ts +3731 -0
- package/contracts/src/smart-feed.js +109 -0
- package/openapi.json +43542 -0
- package/package.json +60 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { ListingSchema } from './schemas.js';
|
|
3
|
+
import { PropertyFeatureRegistrySchema } from './property-features.js';
|
|
4
|
+
import { CatalogInventoryStatusSchema } from './catalog-inventory.js';
|
|
5
|
+
const Id = z.string().min(1).max(256);
|
|
6
|
+
const FeatureId = z.string().regex(/^[a-z][a-z0-9_]{0,79}$/);
|
|
7
|
+
const Scalar = z.union([z.boolean(), z.string().max(200), z.number().finite()]);
|
|
8
|
+
export const SearchFeaturePredicate = z.strictObject({
|
|
9
|
+
feature: FeatureId,
|
|
10
|
+
operator: z.enum(['eq', 'neq', 'in', 'not_in', 'gt', 'gte', 'lt', 'lte']).default('eq'),
|
|
11
|
+
value: z.union([Scalar, z.array(Scalar).min(1).max(20)]),
|
|
12
|
+
});
|
|
13
|
+
const Range = z.strictObject({
|
|
14
|
+
min: z.number().nonnegative().optional(),
|
|
15
|
+
max: z.number().nonnegative().optional(),
|
|
16
|
+
});
|
|
17
|
+
const Decimal = z
|
|
18
|
+
.string()
|
|
19
|
+
.regex(/^\d+(?:\.\d{1,2})?$/)
|
|
20
|
+
.max(20);
|
|
21
|
+
export const SearchBounds = z
|
|
22
|
+
.tuple([
|
|
23
|
+
z.number().min(-180).max(180),
|
|
24
|
+
z.number().min(-90).max(90),
|
|
25
|
+
z.number().min(-180).max(180),
|
|
26
|
+
z.number().min(-90).max(90),
|
|
27
|
+
])
|
|
28
|
+
.refine(([w, s, e, n]) => w <= e && s <= n, 'Use west,south,east,north without crossing the antimeridian.');
|
|
29
|
+
export const SearchFilters = z.strictObject({
|
|
30
|
+
transaction_type: z.enum(['for_sale', 'for_rent']).optional(),
|
|
31
|
+
currency: z
|
|
32
|
+
.string()
|
|
33
|
+
.regex(/^[A-Z]{3}$/)
|
|
34
|
+
.optional(),
|
|
35
|
+
price_period: z.string().min(1).max(40).optional(),
|
|
36
|
+
price: z.strictObject({ min: Decimal.optional(), max: Decimal.optional() }).optional(),
|
|
37
|
+
bedrooms: Range.optional(),
|
|
38
|
+
bathrooms: Range.optional(),
|
|
39
|
+
living_area: Range.optional(),
|
|
40
|
+
living_area_unit: z.enum(['sqft', 'sqm']).optional(),
|
|
41
|
+
city: z.string().min(1).max(200).optional(),
|
|
42
|
+
region: z.string().min(1).max(100).optional(),
|
|
43
|
+
postal_code: z.string().min(1).max(30).optional(),
|
|
44
|
+
property_type: z.string().min(1).max(100).optional(),
|
|
45
|
+
must: z.array(SearchFeaturePredicate).max(30).default([]),
|
|
46
|
+
any: z.array(SearchFeaturePredicate).max(30).default([]),
|
|
47
|
+
/** Exclude known matches. Unknown evidence stays eligible; use must eq:false for confirmed absence. */
|
|
48
|
+
exclude: z.array(SearchFeaturePredicate).max(30).default([]),
|
|
49
|
+
prefer: z.array(SearchFeaturePredicate).max(10).default([]),
|
|
50
|
+
});
|
|
51
|
+
export const SearchRequest = z
|
|
52
|
+
.strictObject({
|
|
53
|
+
dataset_id: Id.optional(),
|
|
54
|
+
q: z.string().max(500).default(''),
|
|
55
|
+
filters: SearchFilters.prefault({}),
|
|
56
|
+
bbox: SearchBounds.optional(),
|
|
57
|
+
radius: z
|
|
58
|
+
.strictObject({
|
|
59
|
+
latitude: z.number().min(-90).max(90),
|
|
60
|
+
longitude: z.number().min(-180).max(180),
|
|
61
|
+
km: z.number().positive().max(20000),
|
|
62
|
+
})
|
|
63
|
+
.optional(),
|
|
64
|
+
sort: z.enum(['relevance', 'price_asc', 'price_desc', 'newest']).default('relevance'),
|
|
65
|
+
limit: z.number().int().min(1).max(100).default(24),
|
|
66
|
+
cursor: z.string().min(1).max(4096).optional(),
|
|
67
|
+
/** Optional refinements computed with the same authorized hits and checkpoint. */
|
|
68
|
+
facet_fields: z.array(z.string().min(1).max(80)).min(1).max(12).optional(),
|
|
69
|
+
facet_limit: z.number().int().min(1).max(100).optional(),
|
|
70
|
+
})
|
|
71
|
+
.refine((v) => !(v.bbox && v.radius), 'Choose bbox or radius, not both.')
|
|
72
|
+
.refine((v) => v.facet_limit === undefined || v.facet_fields !== undefined, 'Facet limit requires facet fields.');
|
|
73
|
+
export const SearchMeta = z.strictObject({
|
|
74
|
+
backend: z.enum(['typesense', 'postgres']),
|
|
75
|
+
consistency: z.enum(['current_index', 'completed_snapshot', 'degraded']),
|
|
76
|
+
generation: z.string(),
|
|
77
|
+
checkpoint: z.string(),
|
|
78
|
+
query_hash: z.string(),
|
|
79
|
+
checked_at: z.iso.datetime({ offset: true }),
|
|
80
|
+
inventory: CatalogInventoryStatusSchema.optional(),
|
|
81
|
+
snapshot: z
|
|
82
|
+
.strictObject({
|
|
83
|
+
published_through_at: z.iso.datetime({ offset: true }),
|
|
84
|
+
max_lag_seconds: z.number().int().min(60).max(3600),
|
|
85
|
+
updates_pending: z.boolean(),
|
|
86
|
+
excluded_count: z.number().int().min(0).max(1000),
|
|
87
|
+
})
|
|
88
|
+
.optional(),
|
|
89
|
+
total: z.number().int().nonnegative().nullable(),
|
|
90
|
+
total_relation: z.enum(['exact', 'unavailable']),
|
|
91
|
+
search_time_ms: z.number().nonnegative(),
|
|
92
|
+
timings: z
|
|
93
|
+
.strictObject({
|
|
94
|
+
authorization_ms: z.number().nonnegative(),
|
|
95
|
+
provider_ms: z.number().nonnegative(),
|
|
96
|
+
hydration_ms: z.number().nonnegative(),
|
|
97
|
+
total_ms: z.number().nonnegative(),
|
|
98
|
+
})
|
|
99
|
+
.optional(),
|
|
100
|
+
warnings: z.array(z.string()),
|
|
101
|
+
});
|
|
102
|
+
export const SearchMatch = z.strictObject({
|
|
103
|
+
feature: FeatureId,
|
|
104
|
+
value: Scalar,
|
|
105
|
+
origin: z.enum(['source_structured', 'source_text_interpreted']),
|
|
106
|
+
evidence_refs: z.array(z.string()),
|
|
107
|
+
});
|
|
108
|
+
const SearchFacetValues = z.array(z.strictObject({
|
|
109
|
+
field: z.string(),
|
|
110
|
+
values: z.array(z.strictObject({ value: Scalar, count: z.number().int().nonnegative() })),
|
|
111
|
+
assessed: z.number().int().nonnegative().nullable(),
|
|
112
|
+
conflicting: z.number().int().nonnegative().nullable(),
|
|
113
|
+
not_assessed: z.number().int().nonnegative().nullable(),
|
|
114
|
+
unknown: z.number().int().nonnegative().nullable(),
|
|
115
|
+
truncated: z.boolean(),
|
|
116
|
+
}));
|
|
117
|
+
export const SearchResponse = z.strictObject({
|
|
118
|
+
facets: SearchFacetValues.optional(),
|
|
119
|
+
data: z.array(z.strictObject({ listing: z.lazy(() => ListingSchema), matched_because: z.array(SearchMatch) })),
|
|
120
|
+
next_cursor: z.string().nullable(),
|
|
121
|
+
has_more: z.boolean(),
|
|
122
|
+
meta: SearchMeta,
|
|
123
|
+
});
|
|
124
|
+
export const SearchFacetRequest = z.strictObject({
|
|
125
|
+
query: SearchRequest,
|
|
126
|
+
fields: z.array(z.string().min(1).max(80)).min(1).max(12),
|
|
127
|
+
limit: z.number().int().min(1).max(100).default(20),
|
|
128
|
+
});
|
|
129
|
+
export const SearchFacetResponse = z.strictObject({
|
|
130
|
+
facets: SearchFacetValues,
|
|
131
|
+
meta: SearchMeta,
|
|
132
|
+
});
|
|
133
|
+
export const SearchMapRequest = z.strictObject({
|
|
134
|
+
query: SearchRequest,
|
|
135
|
+
// Camera bounds intersect, never replace, the applied bbox/radius and filters.
|
|
136
|
+
viewport: SearchBounds.optional(),
|
|
137
|
+
zoom: z.number().int().min(0).max(22).default(10),
|
|
138
|
+
limit: z.number().int().min(1).max(1000).default(500),
|
|
139
|
+
});
|
|
140
|
+
export const SearchMapResponse = z.strictObject({
|
|
141
|
+
points: z.array(z.strictObject({
|
|
142
|
+
id: Id,
|
|
143
|
+
latitude: z.number().min(-90).max(90),
|
|
144
|
+
longitude: z.number().min(-180).max(180),
|
|
145
|
+
price: Decimal.nullable(),
|
|
146
|
+
// Optional for older search producers; current responses preserve the
|
|
147
|
+
// canonical currency and source-established payment period together.
|
|
148
|
+
currency: z
|
|
149
|
+
.string()
|
|
150
|
+
.regex(/^[A-Z]{3}$/)
|
|
151
|
+
.nullable()
|
|
152
|
+
.optional(),
|
|
153
|
+
price_period: z.string().nullable().optional(),
|
|
154
|
+
})),
|
|
155
|
+
clusters: z.array(z.strictObject({
|
|
156
|
+
id: z.string(),
|
|
157
|
+
latitude: z.number().min(-90).max(90),
|
|
158
|
+
longitude: z.number().min(-180).max(180),
|
|
159
|
+
count: z.number().int().positive(),
|
|
160
|
+
bounds: SearchBounds,
|
|
161
|
+
})),
|
|
162
|
+
truncated: z.boolean(),
|
|
163
|
+
unmappable: z.number().int().nonnegative().nullable(),
|
|
164
|
+
mode: z.enum(['points', 'cells']),
|
|
165
|
+
coverage: z.enum(['complete', 'bounded']),
|
|
166
|
+
counts: z.strictObject({
|
|
167
|
+
matching: z.number().int().nonnegative(),
|
|
168
|
+
mappable: z.number().int().nonnegative(),
|
|
169
|
+
viewport: z.number().int().nonnegative(),
|
|
170
|
+
returned: z.number().int().nonnegative(),
|
|
171
|
+
represented: z.number().int().nonnegative(),
|
|
172
|
+
}),
|
|
173
|
+
valid_until: z.iso.datetime({ offset: true }),
|
|
174
|
+
meta: SearchMeta,
|
|
175
|
+
});
|
|
176
|
+
export const SearchSuggestRequest = z.strictObject({
|
|
177
|
+
dataset_id: Id.optional(),
|
|
178
|
+
q: z.string().min(1).max(200),
|
|
179
|
+
limit: z.coerce.number().int().min(1).max(20).default(8),
|
|
180
|
+
});
|
|
181
|
+
export const SearchSuggestResponse = z.strictObject({
|
|
182
|
+
suggestions: z.array(z.strictObject({
|
|
183
|
+
kind: z.enum(['feature', 'place', 'listing']),
|
|
184
|
+
label: z.string(),
|
|
185
|
+
value: z.string(),
|
|
186
|
+
feature: FeatureId.optional(),
|
|
187
|
+
// Consumers apply these exact filters instead of reparsing a display label
|
|
188
|
+
// as a full-text query (which could match another listing's description).
|
|
189
|
+
place: z
|
|
190
|
+
.strictObject({
|
|
191
|
+
city: z.string().min(1).max(256).optional(),
|
|
192
|
+
region: z.string().min(1).max(256).optional(),
|
|
193
|
+
postal_code: z.string().min(1).max(64).optional(),
|
|
194
|
+
})
|
|
195
|
+
.refine((place) => Object.keys(place).length > 0)
|
|
196
|
+
.optional(),
|
|
197
|
+
})),
|
|
198
|
+
meta: SearchMeta,
|
|
199
|
+
});
|
|
200
|
+
export const SearchInterpretRequest = z.strictObject({
|
|
201
|
+
dataset_id: Id.optional(),
|
|
202
|
+
text: z.string().min(1).max(500),
|
|
203
|
+
});
|
|
204
|
+
export const SearchInterpretResponse = z.strictObject({
|
|
205
|
+
query: SearchRequest,
|
|
206
|
+
chips: z.array(z.strictObject({
|
|
207
|
+
label: z.string(),
|
|
208
|
+
field: z.string(),
|
|
209
|
+
value: z.union([Scalar, z.array(Scalar)]),
|
|
210
|
+
})),
|
|
211
|
+
unresolved: z.array(z.string()),
|
|
212
|
+
requires_confirmation: z.boolean(),
|
|
213
|
+
method: z.literal('bounded_rules'),
|
|
214
|
+
});
|
|
215
|
+
export const SearchFeaturesResponse = PropertyFeatureRegistrySchema;
|
|
216
|
+
export const searchOperations = {
|
|
217
|
+
searchFeatures: { input: z.strictObject({}), output: SearchFeaturesResponse },
|
|
218
|
+
searchListings: { input: SearchRequest, output: SearchResponse },
|
|
219
|
+
searchFacets: { input: SearchFacetRequest, output: SearchFacetResponse },
|
|
220
|
+
searchMap: { input: SearchMapRequest, output: SearchMapResponse },
|
|
221
|
+
searchSuggest: { input: SearchSuggestRequest, output: SearchSuggestResponse },
|
|
222
|
+
searchInterpret: { input: SearchInterpretRequest, output: SearchInterpretResponse },
|
|
223
|
+
};
|