@land-catalyst/batch-data-sdk 1.1.12
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/LICENSE +22 -0
- package/README.md +1032 -0
- package/dist/builders.d.ts +821 -0
- package/dist/builders.js +2622 -0
- package/dist/client.d.ts +435 -0
- package/dist/client.interface.d.ts +210 -0
- package/dist/client.interface.js +2 -0
- package/dist/client.js +757 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +19 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +28 -0
- package/dist/logger.interface.d.ts +19 -0
- package/dist/logger.interface.js +47 -0
- package/dist/types.d.ts +1738 -0
- package/dist/types.js +9 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,1032 @@
|
|
|
1
|
+
# @land-catalyst/batch-data-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for BatchData.io Property API - Complete type definitions, fluent query builders, and utilities.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
This package is published to the public npm registry. To install it:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @land-catalyst/batch-data-sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or add to `package.json`:
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@land-catalyst/batch-data-sdk": "^1.0.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- ✅ **Complete TypeScript Types** - Full type definitions for all BatchData API endpoints and responses
|
|
25
|
+
- ✅ **Fluent Query Builders** - Type-safe, chainable API for constructing search criteria
|
|
26
|
+
- ✅ **Property Search Response Types** - Comprehensive types for property search results
|
|
27
|
+
- ✅ **HTTP Client** - Ready-to-use API client for BatchData endpoints
|
|
28
|
+
- ✅ **Custom Error Classes** - Specialized error handling for BatchData operations
|
|
29
|
+
|
|
30
|
+
## Examples
|
|
31
|
+
|
|
32
|
+
### Client Setup
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { BatchDataClient } from "@land-catalyst/batch-data-sdk";
|
|
36
|
+
import { ConsoleLogger } from "@land-catalyst/batch-data-sdk";
|
|
37
|
+
|
|
38
|
+
// Create a client instance
|
|
39
|
+
const client = new BatchDataClient({
|
|
40
|
+
apiKey: process.env.BATCHDATA_API_KEY!,
|
|
41
|
+
// Optional: provide custom logger
|
|
42
|
+
logger: new ConsoleLogger(),
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Basic Property Search
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import {
|
|
50
|
+
SearchCriteriaBuilder,
|
|
51
|
+
PropertySearchRequestBuilder,
|
|
52
|
+
PropertyLookupOptionsBuilder,
|
|
53
|
+
} from "@land-catalyst/batch-data-sdk";
|
|
54
|
+
|
|
55
|
+
// Simple search by location
|
|
56
|
+
const request = new PropertySearchRequestBuilder()
|
|
57
|
+
.searchCriteria((c) => c.query("Maricopa County, AZ"))
|
|
58
|
+
.options((o) => o.take(10).skip(0))
|
|
59
|
+
.build();
|
|
60
|
+
|
|
61
|
+
const response = await client.searchProperties(request);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Address-Based Search
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import {
|
|
68
|
+
SearchCriteriaBuilder,
|
|
69
|
+
PropertySearchRequestBuilder,
|
|
70
|
+
} from "@land-catalyst/batch-data-sdk";
|
|
71
|
+
|
|
72
|
+
// Search by address with filters
|
|
73
|
+
const request = new PropertySearchRequestBuilder()
|
|
74
|
+
.searchCriteria((c) =>
|
|
75
|
+
c
|
|
76
|
+
.query("Maricopa County, AZ")
|
|
77
|
+
.address((a) =>
|
|
78
|
+
a
|
|
79
|
+
.city((city) => city.equals("Phoenix"))
|
|
80
|
+
.state((state) => state.equals("AZ"))
|
|
81
|
+
.zip((zip) => zip.inList(["85001", "85002", "85003"]))
|
|
82
|
+
.street((street) => street.contains("Main"))
|
|
83
|
+
)
|
|
84
|
+
)
|
|
85
|
+
.build();
|
|
86
|
+
|
|
87
|
+
const response = await client.searchProperties(request);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### String Filter Examples
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
import { StringFilterBuilder } from "@land-catalyst/batch-data-sdk";
|
|
94
|
+
|
|
95
|
+
// All string filter methods can be chained
|
|
96
|
+
const filter = new StringFilterBuilder()
|
|
97
|
+
.equals("Phoenix")
|
|
98
|
+
.contains("Phx")
|
|
99
|
+
.startsWith("Ph")
|
|
100
|
+
.endsWith("ix")
|
|
101
|
+
.matches(["Phoenix.*", ".*Arizona"])
|
|
102
|
+
.inList(["Phoenix", "Tucson", "Flagstaff"])
|
|
103
|
+
.build();
|
|
104
|
+
|
|
105
|
+
// Use in address search
|
|
106
|
+
const request = new PropertySearchRequestBuilder()
|
|
107
|
+
.searchCriteria((c) =>
|
|
108
|
+
c
|
|
109
|
+
.query("AZ")
|
|
110
|
+
.address((a) =>
|
|
111
|
+
a
|
|
112
|
+
.city((city) => city.equals("Phoenix").contains("Phx"))
|
|
113
|
+
.state((state) => state.inList(["AZ", "CA", "NV"]))
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
.build();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Numeric Range Filter Examples
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { NumericRangeFilterBuilder } from "@land-catalyst/batch-data-sdk";
|
|
123
|
+
|
|
124
|
+
// Numeric ranges with min/max
|
|
125
|
+
const filter = new NumericRangeFilterBuilder()
|
|
126
|
+
.min(100000)
|
|
127
|
+
.max(500000)
|
|
128
|
+
.build();
|
|
129
|
+
|
|
130
|
+
// Use in assessment search
|
|
131
|
+
const request = new PropertySearchRequestBuilder()
|
|
132
|
+
.searchCriteria((c) =>
|
|
133
|
+
c
|
|
134
|
+
.query("Maricopa County, AZ")
|
|
135
|
+
.assessment((a) =>
|
|
136
|
+
a
|
|
137
|
+
.totalAssessedValue((v) => v.min(100000).max(500000))
|
|
138
|
+
.assessmentYear((y) => y.min(2020).max(2023))
|
|
139
|
+
)
|
|
140
|
+
)
|
|
141
|
+
.build();
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Date Range Filter Examples
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
import { DateRangeFilterBuilder } from "@land-catalyst/batch-data-sdk";
|
|
148
|
+
|
|
149
|
+
// Date ranges
|
|
150
|
+
const filter = new DateRangeFilterBuilder()
|
|
151
|
+
.minDate("2020-01-01")
|
|
152
|
+
.maxDate("2023-12-31")
|
|
153
|
+
.build();
|
|
154
|
+
|
|
155
|
+
// Use in sale search
|
|
156
|
+
const request = new PropertySearchRequestBuilder()
|
|
157
|
+
.searchCriteria((c) =>
|
|
158
|
+
c
|
|
159
|
+
.query("AZ")
|
|
160
|
+
.sale((s) =>
|
|
161
|
+
s.lastSaleDate((d) =>
|
|
162
|
+
d.minDate("2020-01-01").maxDate("2023-12-31")
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
.build();
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Building Criteria Search
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
const request = new PropertySearchRequestBuilder()
|
|
173
|
+
.searchCriteria((c) =>
|
|
174
|
+
c
|
|
175
|
+
.query("Maricopa County, AZ")
|
|
176
|
+
.building((b) =>
|
|
177
|
+
b
|
|
178
|
+
.yearBuilt((y) => y.min(1990).max(2020))
|
|
179
|
+
.bedroomCount((br) => br.min(3).max(5))
|
|
180
|
+
.bathroomCount((ba) => ba.min(2).max(4))
|
|
181
|
+
.totalBuildingAreaSquareFeet((area) => area.min(1500).max(3000))
|
|
182
|
+
.buildingType((type) => type.equals("Single Family"))
|
|
183
|
+
.pool((pool) => pool.equals("Yes"))
|
|
184
|
+
.airConditioningSource((ac) => ac.contains("Central"))
|
|
185
|
+
)
|
|
186
|
+
)
|
|
187
|
+
.build();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Assessment Criteria Search
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
const request = new PropertySearchRequestBuilder()
|
|
194
|
+
.searchCriteria((c) =>
|
|
195
|
+
c
|
|
196
|
+
.query("Maricopa County, AZ")
|
|
197
|
+
.assessment((a) =>
|
|
198
|
+
a
|
|
199
|
+
.totalAssessedValue((v) => v.min(100000).max(500000))
|
|
200
|
+
.totalMarketValue((v) => v.min(200000).max(600000))
|
|
201
|
+
.assessmentYear((y) => y.min(2020).max(2023))
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
.build();
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Demographics Criteria Search
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
const request = new PropertySearchRequestBuilder()
|
|
211
|
+
.searchCriteria((c) =>
|
|
212
|
+
c
|
|
213
|
+
.query("Maricopa County, AZ")
|
|
214
|
+
.demographics((d) =>
|
|
215
|
+
d
|
|
216
|
+
.age((a) => a.min(25).max(65))
|
|
217
|
+
.income((i) => i.min(50000).max(150000))
|
|
218
|
+
.netWorth((nw) => nw.min(100000).max(1000000))
|
|
219
|
+
.householdSize((hs) => hs.min(2).max(5))
|
|
220
|
+
.homeownerRenter((hr) => hr.equals("Homeowner"))
|
|
221
|
+
.gender((g) => g.equals("M").inList(["M", "F"]))
|
|
222
|
+
.hasChildren(true)
|
|
223
|
+
.businessOwner((bo) => bo.equals("Yes"))
|
|
224
|
+
)
|
|
225
|
+
)
|
|
226
|
+
.build();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Complex Multi-Criteria Search
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
const request = new PropertySearchRequestBuilder()
|
|
233
|
+
.searchCriteria((c) =>
|
|
234
|
+
c
|
|
235
|
+
.query("Maricopa County, AZ")
|
|
236
|
+
// Address filters
|
|
237
|
+
.address((a) =>
|
|
238
|
+
a
|
|
239
|
+
.city((city) => city.equals("Phoenix"))
|
|
240
|
+
.state((state) => state.equals("AZ"))
|
|
241
|
+
.zip((zip) => zip.startsWith("85"))
|
|
242
|
+
)
|
|
243
|
+
// Building filters
|
|
244
|
+
.building((b) =>
|
|
245
|
+
b
|
|
246
|
+
.yearBuilt((y) => y.min(1990).max(2020))
|
|
247
|
+
.bedroomCount((br) => br.min(3).max(5))
|
|
248
|
+
.bathroomCount((ba) => ba.min(2).max(4))
|
|
249
|
+
)
|
|
250
|
+
// Assessment filters
|
|
251
|
+
.assessment((a) =>
|
|
252
|
+
a
|
|
253
|
+
.totalAssessedValue((v) => v.min(100000).max(500000))
|
|
254
|
+
.assessmentYear((y) => y.min(2020).max(2023))
|
|
255
|
+
)
|
|
256
|
+
// Demographics filters
|
|
257
|
+
.demographics((d) =>
|
|
258
|
+
d
|
|
259
|
+
.income((i) => i.min(50000).max(150000))
|
|
260
|
+
.age((a) => a.min(25).max(65))
|
|
261
|
+
)
|
|
262
|
+
// Quick list
|
|
263
|
+
.quickList("vacant")
|
|
264
|
+
)
|
|
265
|
+
.options((o) =>
|
|
266
|
+
o
|
|
267
|
+
.take(50)
|
|
268
|
+
.skip(0)
|
|
269
|
+
.bedrooms(3, 5)
|
|
270
|
+
.bathrooms(2, 4)
|
|
271
|
+
.yearBuilt(1990, 2020)
|
|
272
|
+
.skipTrace(true)
|
|
273
|
+
.images(true)
|
|
274
|
+
.quicklistCounts(true)
|
|
275
|
+
)
|
|
276
|
+
.build();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Property Search with Options
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
const request = new PropertySearchRequestBuilder()
|
|
283
|
+
.searchCriteria((c) => c.query("Maricopa County, AZ"))
|
|
284
|
+
.options((o) =>
|
|
285
|
+
o
|
|
286
|
+
// Pagination
|
|
287
|
+
.pagination(0, 50)
|
|
288
|
+
.skip(0)
|
|
289
|
+
.take(50)
|
|
290
|
+
// Distance filtering
|
|
291
|
+
.distance(5) // 5 miles
|
|
292
|
+
.distance(undefined, 8800, 26400) // yards and feet
|
|
293
|
+
.distance(undefined, undefined, undefined, 8, 8047) // km and meters
|
|
294
|
+
// Bounding box
|
|
295
|
+
.boundingBox(
|
|
296
|
+
{ latitude: 33.5, longitude: -112.1 },
|
|
297
|
+
{ latitude: 33.4, longitude: -112.0 }
|
|
298
|
+
)
|
|
299
|
+
// Property features
|
|
300
|
+
.bedrooms(3, 5)
|
|
301
|
+
.bathrooms(2, 4)
|
|
302
|
+
.stories(1, 2)
|
|
303
|
+
.area(80, 120) // percentage
|
|
304
|
+
.yearBuilt(1990, 2020)
|
|
305
|
+
.lotSize(90, 110) // percentage
|
|
306
|
+
// Flags
|
|
307
|
+
.skipTrace(true)
|
|
308
|
+
.aggregateLoanTypes(true)
|
|
309
|
+
.images(true)
|
|
310
|
+
.showRequests(true)
|
|
311
|
+
.areaPolygon(true)
|
|
312
|
+
.quicklistCounts(true)
|
|
313
|
+
.useSubdivision(true)
|
|
314
|
+
// Formatting
|
|
315
|
+
.dateFormat("YYYY-MM-DD")
|
|
316
|
+
// Sorting
|
|
317
|
+
.sort("totalAssessedValue", "desc", "session-12345")
|
|
318
|
+
.build()
|
|
319
|
+
)
|
|
320
|
+
.build();
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Property Subscription
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import {
|
|
327
|
+
PropertySubscriptionBuilder,
|
|
328
|
+
DeliveryConfigBuilder,
|
|
329
|
+
} from "@land-catalyst/batch-data-sdk";
|
|
330
|
+
|
|
331
|
+
// Webhook subscription
|
|
332
|
+
const subscription = new PropertySubscriptionBuilder()
|
|
333
|
+
.searchCriteria((c) =>
|
|
334
|
+
c
|
|
335
|
+
.query("Maricopa County, AZ")
|
|
336
|
+
.address((a) =>
|
|
337
|
+
a
|
|
338
|
+
.city((city) => city.equals("Phoenix"))
|
|
339
|
+
.state((state) => state.equals("AZ"))
|
|
340
|
+
)
|
|
341
|
+
.quickList("vacant")
|
|
342
|
+
)
|
|
343
|
+
.deliveryConfig((dc) =>
|
|
344
|
+
dc.webhook("https://example.com/webhook", {
|
|
345
|
+
"X-API-Key": "secret",
|
|
346
|
+
})
|
|
347
|
+
)
|
|
348
|
+
.build();
|
|
349
|
+
|
|
350
|
+
const response = await client.createPropertySubscription(subscription);
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Property Subscription with Kinesis
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
const subscription = new PropertySubscriptionBuilder()
|
|
357
|
+
.searchCriteria((c) => c.query("US").quickList("owner-occupied"))
|
|
358
|
+
.deliveryConfig((dc) =>
|
|
359
|
+
dc.kinesis(
|
|
360
|
+
"my-stream",
|
|
361
|
+
"us-east-1",
|
|
362
|
+
"access-key-id",
|
|
363
|
+
"secret-access-key"
|
|
364
|
+
)
|
|
365
|
+
)
|
|
366
|
+
.build();
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Property Lookup by Address
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import {
|
|
373
|
+
PropertyLookupRequestBuilder,
|
|
374
|
+
PropertyLookupRequestItemBuilder,
|
|
375
|
+
} from "@land-catalyst/batch-data-sdk";
|
|
376
|
+
|
|
377
|
+
// Single property lookup
|
|
378
|
+
const request = new PropertyLookupRequestBuilder()
|
|
379
|
+
.addItem(
|
|
380
|
+
new PropertyLookupRequestItemBuilder()
|
|
381
|
+
.address({
|
|
382
|
+
street: "2800 N 24th St",
|
|
383
|
+
city: "Phoenix",
|
|
384
|
+
state: "AZ",
|
|
385
|
+
zip: "85008",
|
|
386
|
+
})
|
|
387
|
+
.build()
|
|
388
|
+
)
|
|
389
|
+
.options((o) => o.take(1).skipTrace(true))
|
|
390
|
+
.build();
|
|
391
|
+
|
|
392
|
+
const response = await client.lookupProperty(request);
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Property Lookup by Multiple Identifiers
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
const request = new PropertyLookupRequestBuilder()
|
|
399
|
+
.addItem(
|
|
400
|
+
new PropertyLookupRequestItemBuilder()
|
|
401
|
+
.propertyId("12345")
|
|
402
|
+
.build()
|
|
403
|
+
)
|
|
404
|
+
.addItem(
|
|
405
|
+
new PropertyLookupRequestItemBuilder()
|
|
406
|
+
.apn("123-45-678")
|
|
407
|
+
.countyFipsCode("04013")
|
|
408
|
+
.build()
|
|
409
|
+
)
|
|
410
|
+
.addItem(
|
|
411
|
+
new PropertyLookupRequestItemBuilder()
|
|
412
|
+
.hash("abc123def456")
|
|
413
|
+
.build()
|
|
414
|
+
)
|
|
415
|
+
.options((o) =>
|
|
416
|
+
o
|
|
417
|
+
.take(10)
|
|
418
|
+
.images(true)
|
|
419
|
+
.skipTrace(true)
|
|
420
|
+
.showRequests(true)
|
|
421
|
+
)
|
|
422
|
+
.build();
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Async Property Lookup
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import {
|
|
429
|
+
PropertyLookupAsyncRequestBuilder,
|
|
430
|
+
AsyncPropertyLookupOptionsBuilder,
|
|
431
|
+
} from "@land-catalyst/batch-data-sdk";
|
|
432
|
+
|
|
433
|
+
const request = new PropertyLookupAsyncRequestBuilder()
|
|
434
|
+
.addItem(
|
|
435
|
+
new PropertyLookupRequestItemBuilder()
|
|
436
|
+
.address({
|
|
437
|
+
street: "2800 N 24th St",
|
|
438
|
+
city: "Phoenix",
|
|
439
|
+
state: "AZ",
|
|
440
|
+
zip: "85008",
|
|
441
|
+
})
|
|
442
|
+
.build()
|
|
443
|
+
)
|
|
444
|
+
.options(
|
|
445
|
+
new AsyncPropertyLookupOptionsBuilder()
|
|
446
|
+
.webhook(
|
|
447
|
+
"https://example.com/webhook",
|
|
448
|
+
"https://example.com/error-webhook"
|
|
449
|
+
)
|
|
450
|
+
.build()
|
|
451
|
+
)
|
|
452
|
+
.build();
|
|
453
|
+
|
|
454
|
+
const response = await client.lookupPropertyAsync(request);
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
### Async Property Search
|
|
458
|
+
|
|
459
|
+
```typescript
|
|
460
|
+
import { PropertySearchAsyncRequestBuilder } from "@land-catalyst/batch-data-sdk";
|
|
461
|
+
|
|
462
|
+
const request = new PropertySearchAsyncRequestBuilder()
|
|
463
|
+
.searchCriteria((c) =>
|
|
464
|
+
c
|
|
465
|
+
.query("Maricopa County, AZ")
|
|
466
|
+
.address((a) =>
|
|
467
|
+
a
|
|
468
|
+
.city((city) => city.equals("Phoenix"))
|
|
469
|
+
.state((state) => state.equals("AZ"))
|
|
470
|
+
)
|
|
471
|
+
)
|
|
472
|
+
.options(
|
|
473
|
+
new AsyncPropertyLookupOptionsBuilder()
|
|
474
|
+
.webhook("https://example.com/webhook")
|
|
475
|
+
.take(100)
|
|
476
|
+
.build()
|
|
477
|
+
)
|
|
478
|
+
.build();
|
|
479
|
+
|
|
480
|
+
const response = await client.searchPropertiesAsync(request);
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
### Address Verification
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
const request = {
|
|
487
|
+
requests: [
|
|
488
|
+
{
|
|
489
|
+
street: "2800 N 24th St",
|
|
490
|
+
city: "Phoenix",
|
|
491
|
+
state: "Arizona",
|
|
492
|
+
zip: "85008",
|
|
493
|
+
},
|
|
494
|
+
],
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const response = await client.verifyAddress(request);
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Address Autocomplete
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
const request = {
|
|
504
|
+
searchCriteria: {
|
|
505
|
+
query: "2800 N 24th St Phoenix",
|
|
506
|
+
},
|
|
507
|
+
options: {
|
|
508
|
+
skip: 0,
|
|
509
|
+
take: 5,
|
|
510
|
+
},
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
const response = await client.autocompleteAddress(request);
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Geocoding
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// Geocode address to coordinates
|
|
520
|
+
const geocodeRequest = {
|
|
521
|
+
requests: [
|
|
522
|
+
{
|
|
523
|
+
address: "2800 N 24th St, Phoenix, AZ, 85008",
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const geocodeResponse = await client.geocodeAddress(geocodeRequest);
|
|
529
|
+
|
|
530
|
+
// Reverse geocode coordinates to address
|
|
531
|
+
const reverseGeocodeRequest = {
|
|
532
|
+
request: {
|
|
533
|
+
latitude: 33.47865,
|
|
534
|
+
longitude: -112.03029,
|
|
535
|
+
},
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
const reverseGeocodeResponse =
|
|
539
|
+
await client.reverseGeocodeAddress(reverseGeocodeRequest);
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Builder Accumulation Pattern
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
// Multiple calls to the same method accumulate changes
|
|
546
|
+
const criteria = new AddressSearchCriteriaBuilder()
|
|
547
|
+
.street((s) => s.equals("Main"))
|
|
548
|
+
.street((s) => s.contains("St"))
|
|
549
|
+
.street((s) => s.startsWith("M"))
|
|
550
|
+
.build();
|
|
551
|
+
|
|
552
|
+
// Result: { street: { equals: "Main", contains: "St", startsWith: "M" } }
|
|
553
|
+
|
|
554
|
+
// Direct values replace previous builder configuration
|
|
555
|
+
const criteria2 = new AddressSearchCriteriaBuilder()
|
|
556
|
+
.street((s) => s.equals("Main St"))
|
|
557
|
+
.street({ equals: "New Street" }) // Replaces previous
|
|
558
|
+
.build();
|
|
559
|
+
|
|
560
|
+
// Result: { street: { equals: "New Street" } }
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
### Using Direct Values vs Lambda Configurators
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
// Both patterns are supported - use whichever is more convenient
|
|
567
|
+
|
|
568
|
+
// Lambda pattern (recommended for complex configurations)
|
|
569
|
+
const request1 = new PropertySearchRequestBuilder()
|
|
570
|
+
.searchCriteria((c) =>
|
|
571
|
+
c
|
|
572
|
+
.query("AZ")
|
|
573
|
+
.address((a) =>
|
|
574
|
+
a
|
|
575
|
+
.city((city) => city.equals("Phoenix").contains("Phx"))
|
|
576
|
+
.state((state) => state.inList(["AZ", "CA"]))
|
|
577
|
+
)
|
|
578
|
+
)
|
|
579
|
+
.build();
|
|
580
|
+
|
|
581
|
+
// Direct value pattern (useful for simple cases)
|
|
582
|
+
const request2 = new PropertySearchRequestBuilder()
|
|
583
|
+
.searchCriteria({
|
|
584
|
+
query: "AZ",
|
|
585
|
+
address: {
|
|
586
|
+
city: { equals: "Phoenix" },
|
|
587
|
+
state: { equals: "AZ" },
|
|
588
|
+
},
|
|
589
|
+
})
|
|
590
|
+
.options({
|
|
591
|
+
take: 10,
|
|
592
|
+
skip: 0,
|
|
593
|
+
})
|
|
594
|
+
.build();
|
|
595
|
+
```
|
|
596
|
+
|
|
597
|
+
### Complex Nested Search
|
|
598
|
+
|
|
599
|
+
```typescript
|
|
600
|
+
// Ultra-complex search with all criteria types
|
|
601
|
+
const request = new PropertySearchRequestBuilder()
|
|
602
|
+
.searchCriteria((c) =>
|
|
603
|
+
c
|
|
604
|
+
.query("Maricopa County, AZ")
|
|
605
|
+
// Address with multiple filters
|
|
606
|
+
.address((a) =>
|
|
607
|
+
a
|
|
608
|
+
.street((s) =>
|
|
609
|
+
s
|
|
610
|
+
.equals("Main")
|
|
611
|
+
.contains("Street")
|
|
612
|
+
.startsWith("M")
|
|
613
|
+
.endsWith("St")
|
|
614
|
+
.inList(["Main St", "Main Street", "Main Ave"])
|
|
615
|
+
)
|
|
616
|
+
.city((city) =>
|
|
617
|
+
city.equals("Phoenix").contains("Phx").matches(["Phoenix.*"])
|
|
618
|
+
)
|
|
619
|
+
.state((state) => state.equals("AZ"))
|
|
620
|
+
.zip((zip) => zip.startsWith("85").endsWith("01"))
|
|
621
|
+
)
|
|
622
|
+
// Assessment with accumulation
|
|
623
|
+
.assessment((a) =>
|
|
624
|
+
a
|
|
625
|
+
.assessmentYear((y) => y.min(2020).max(2023))
|
|
626
|
+
.assessmentYear((y) => y.min(2019)) // Accumulates
|
|
627
|
+
.totalAssessedValue((v) => v.min(100000).max(500000))
|
|
628
|
+
.totalAssessedValue((v) => v.min(150000)) // Accumulates
|
|
629
|
+
)
|
|
630
|
+
// Building criteria
|
|
631
|
+
.building((b) =>
|
|
632
|
+
b
|
|
633
|
+
.buildingType((t) =>
|
|
634
|
+
t.equals("Single Family").inList([
|
|
635
|
+
"Single Family",
|
|
636
|
+
"Townhouse",
|
|
637
|
+
"Condo",
|
|
638
|
+
])
|
|
639
|
+
)
|
|
640
|
+
.yearBuilt((y) => y.min(1990).max(2020))
|
|
641
|
+
.bedroomCount((br) => br.min(3).max(5))
|
|
642
|
+
.bathroomCount((ba) => ba.min(2).max(4))
|
|
643
|
+
.pool((pool) => pool.equals("Yes"))
|
|
644
|
+
)
|
|
645
|
+
// Demographics
|
|
646
|
+
.demographics((d) =>
|
|
647
|
+
d
|
|
648
|
+
.age((a) => a.min(25).max(65))
|
|
649
|
+
.income((i) => i.min(50000).max(150000))
|
|
650
|
+
.income((i) => i.min(60000)) // Accumulates
|
|
651
|
+
.gender((g) => g.equals("M").inList(["M", "F"]))
|
|
652
|
+
.hasChildren(true)
|
|
653
|
+
)
|
|
654
|
+
// Quick lists
|
|
655
|
+
.quickList("vacant")
|
|
656
|
+
)
|
|
657
|
+
.options((o) =>
|
|
658
|
+
o
|
|
659
|
+
.pagination(100, 50)
|
|
660
|
+
.distance(5, 8800, 26400, 8, 8047)
|
|
661
|
+
.bedrooms(3, 5)
|
|
662
|
+
.bathrooms(2, 4)
|
|
663
|
+
.yearBuilt(1990, 2020)
|
|
664
|
+
.skipTrace(true)
|
|
665
|
+
.images(true)
|
|
666
|
+
.sort("totalAssessedValue", "desc", "session-12345")
|
|
667
|
+
.build()
|
|
668
|
+
)
|
|
669
|
+
.build();
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
### Error Handling
|
|
673
|
+
|
|
674
|
+
```typescript
|
|
675
|
+
import { PropertyCountLimitExceededError } from "@land-catalyst/batch-data-sdk";
|
|
676
|
+
|
|
677
|
+
try {
|
|
678
|
+
const response = await client.searchProperties(request);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
if (error instanceof PropertyCountLimitExceededError) {
|
|
681
|
+
console.log(`Limit exceeded: ${error.count} > ${error.limit}`);
|
|
682
|
+
console.log(`Limit type: ${error.limitType}`); // "sync" or "async"
|
|
683
|
+
} else if (error instanceof Error) {
|
|
684
|
+
console.error("API error:", error.message);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
```
|
|
688
|
+
|
|
689
|
+
### Using Builders with Existing Data
|
|
690
|
+
|
|
691
|
+
```typescript
|
|
692
|
+
// Create builder from existing criteria
|
|
693
|
+
const existingCriteria: SearchCriteria = {
|
|
694
|
+
query: "AZ",
|
|
695
|
+
address: {
|
|
696
|
+
city: { equals: "Phoenix" },
|
|
697
|
+
},
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
const builder = SearchCriteriaBuilder.from(existingCriteria);
|
|
701
|
+
builder.address((a) => a.state((s) => s.equals("AZ")));
|
|
702
|
+
|
|
703
|
+
const updatedCriteria = builder.build();
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### GeoLocation Filters
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
import { GeoLocationFactory } from "@land-catalyst/batch-data-sdk";
|
|
710
|
+
|
|
711
|
+
// Distance-based search
|
|
712
|
+
const request = new PropertySearchRequestBuilder()
|
|
713
|
+
.searchCriteria((c) =>
|
|
714
|
+
c
|
|
715
|
+
.query("Phoenix, AZ")
|
|
716
|
+
.address((a) =>
|
|
717
|
+
a.geoLocationDistance(33.4484, -112.074, {
|
|
718
|
+
miles: "5",
|
|
719
|
+
})
|
|
720
|
+
)
|
|
721
|
+
)
|
|
722
|
+
.build();
|
|
723
|
+
|
|
724
|
+
// Bounding box search
|
|
725
|
+
const request2 = new PropertySearchRequestBuilder()
|
|
726
|
+
.searchCriteria((c) =>
|
|
727
|
+
c
|
|
728
|
+
.query("Phoenix, AZ")
|
|
729
|
+
.address((a) =>
|
|
730
|
+
a.geoLocationBoundingBox(
|
|
731
|
+
33.5, // NW latitude
|
|
732
|
+
-112.1, // NW longitude
|
|
733
|
+
33.4, // SE latitude
|
|
734
|
+
-112.0 // SE longitude
|
|
735
|
+
)
|
|
736
|
+
)
|
|
737
|
+
)
|
|
738
|
+
.build();
|
|
739
|
+
|
|
740
|
+
// Polygon search
|
|
741
|
+
const request3 = new PropertySearchRequestBuilder()
|
|
742
|
+
.searchCriteria((c) =>
|
|
743
|
+
c
|
|
744
|
+
.query("Phoenix, AZ")
|
|
745
|
+
.address((a) =>
|
|
746
|
+
a.geoLocationPolygon([
|
|
747
|
+
{ latitude: 33.5, longitude: -112.1 },
|
|
748
|
+
{ latitude: 33.4, longitude: -112.1 },
|
|
749
|
+
{ latitude: 33.4, longitude: -112.0 },
|
|
750
|
+
{ latitude: 33.5, longitude: -112.0 },
|
|
751
|
+
])
|
|
752
|
+
)
|
|
753
|
+
)
|
|
754
|
+
.build();
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
### All Search Criteria Types
|
|
758
|
+
|
|
759
|
+
```typescript
|
|
760
|
+
// Foreclosure search
|
|
761
|
+
.searchCriteria((c) =>
|
|
762
|
+
c
|
|
763
|
+
.query("AZ")
|
|
764
|
+
.foreclosure((f) =>
|
|
765
|
+
f
|
|
766
|
+
.status((s) => s.equals("Active"))
|
|
767
|
+
.recordingDate((d) => d.minDate("2020-01-01"))
|
|
768
|
+
)
|
|
769
|
+
)
|
|
770
|
+
|
|
771
|
+
// Listing search
|
|
772
|
+
.listing((l) =>
|
|
773
|
+
l
|
|
774
|
+
.price((p) => p.min(200000).max(500000))
|
|
775
|
+
.status((s) => s.equals("Active"))
|
|
776
|
+
.daysOnMarket((d) => d.min(0).max(90))
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
// Sale search
|
|
780
|
+
.sale((s) =>
|
|
781
|
+
s
|
|
782
|
+
.lastSalePrice((p) => p.min(100000).max(400000))
|
|
783
|
+
.lastSaleDate((d) => d.minDate("2020-01-01"))
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
// Owner search
|
|
787
|
+
.owner((o) =>
|
|
788
|
+
o
|
|
789
|
+
.firstName((f) => f.equals("John"))
|
|
790
|
+
.lastName((l) => l.equals("Doe"))
|
|
791
|
+
.ownerOccupied(true)
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
// Open lien search
|
|
795
|
+
.openLien((ol) =>
|
|
796
|
+
ol
|
|
797
|
+
.totalOpenLienCount((c) => c.min(1).max(5))
|
|
798
|
+
.totalOpenLienBalance((b) => b.min(50000).max(500000))
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
// Permit search
|
|
802
|
+
.permit((p) =>
|
|
803
|
+
p
|
|
804
|
+
.permitCount((c) => c.min(1).max(10))
|
|
805
|
+
.totalJobValue((v) => v.min(10000).max(100000))
|
|
806
|
+
)
|
|
807
|
+
|
|
808
|
+
// Tax search
|
|
809
|
+
.tax((t) => t.taxDelinquentYear((y) => y.min(2020).max(2023)))
|
|
810
|
+
|
|
811
|
+
// Valuation search
|
|
812
|
+
.valuation((v) =>
|
|
813
|
+
v
|
|
814
|
+
.estimatedValue((ev) => ev.min(200000).max(600000))
|
|
815
|
+
.ltv((ltv) => ltv.min(0).max(80))
|
|
816
|
+
)
|
|
817
|
+
|
|
818
|
+
// Intel search
|
|
819
|
+
.intel((i) =>
|
|
820
|
+
i
|
|
821
|
+
.lastSoldPrice((p) => p.min(150000).max(450000))
|
|
822
|
+
.lastSoldDate((d) => d.minDate("2020-01-01"))
|
|
823
|
+
.salePropensity((sp) => sp.min(50).max(100))
|
|
824
|
+
)
|
|
825
|
+
```
|
|
826
|
+
|
|
827
|
+
### Quick Lists
|
|
828
|
+
|
|
829
|
+
```typescript
|
|
830
|
+
// Single quick list
|
|
831
|
+
.quickList("vacant")
|
|
832
|
+
|
|
833
|
+
// Multiple quick lists (AND)
|
|
834
|
+
.quickLists(["vacant", "owner-occupied", "high-equity"])
|
|
835
|
+
|
|
836
|
+
// Multiple quick lists (OR)
|
|
837
|
+
.orQuickLists(["vacant", "preforeclosure"])
|
|
838
|
+
|
|
839
|
+
// Exclude quick list
|
|
840
|
+
.quickList("not-vacant")
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Sorting and Pagination
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
.options((o) =>
|
|
847
|
+
o
|
|
848
|
+
// Pagination
|
|
849
|
+
.pagination(0, 50) // skip 0, take 50
|
|
850
|
+
.skip(100) // Override skip
|
|
851
|
+
.take(25) // Override take
|
|
852
|
+
// Sorting
|
|
853
|
+
.sort("totalAssessedValue", "desc")
|
|
854
|
+
.sort("yearBuilt", "asc", "session-12345") // With session ID
|
|
855
|
+
.build()
|
|
856
|
+
)
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
### Response Handling
|
|
860
|
+
|
|
861
|
+
```typescript
|
|
862
|
+
const response = await client.searchProperties(request);
|
|
863
|
+
|
|
864
|
+
// Check status
|
|
865
|
+
if (response.status?.code === 200) {
|
|
866
|
+
console.log("Success!");
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Access properties
|
|
870
|
+
if (response.results?.properties) {
|
|
871
|
+
response.results.properties.forEach((property) => {
|
|
872
|
+
console.log(property.address?.street);
|
|
873
|
+
console.log(property.valuation?.estimatedValue);
|
|
874
|
+
console.log(property.demographics?.income);
|
|
875
|
+
console.log(property.building?.yearBuilt);
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
// Access metadata
|
|
880
|
+
if (response.results?.meta) {
|
|
881
|
+
console.log(
|
|
882
|
+
`Found ${response.results.meta.results?.resultsFound} properties`
|
|
883
|
+
);
|
|
884
|
+
console.log(
|
|
885
|
+
`Request took ${response.results.meta.performance?.totalRequestTime}ms`
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
// Quick list counts
|
|
890
|
+
if (response.results?.quicklistCounts) {
|
|
891
|
+
response.results.quicklistCounts.forEach((count) => {
|
|
892
|
+
console.log(`${count.name}: ${count.count}`);
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
## API Documentation
|
|
898
|
+
|
|
899
|
+
### Types
|
|
900
|
+
|
|
901
|
+
- `SearchCriteria` - Complete search criteria structure
|
|
902
|
+
- `PropertySubscriptionRequest` - Subscription creation request
|
|
903
|
+
- `PropertySubscriptionResponse` - Subscription creation response
|
|
904
|
+
- `PropertySearchResponse` - Property search API response
|
|
905
|
+
- `Property` - Individual property object with all nested data
|
|
906
|
+
- `DeliveryConfig` - Webhook or Kinesis delivery configuration
|
|
907
|
+
|
|
908
|
+
### Builders
|
|
909
|
+
|
|
910
|
+
All builders follow a fluent, chainable pattern:
|
|
911
|
+
|
|
912
|
+
- `SearchCriteriaBuilder` - Main search criteria builder
|
|
913
|
+
- `AddressSearchCriteriaBuilder` - Address filters
|
|
914
|
+
- `BuildingSearchCriteriaBuilder` - Building/property filters
|
|
915
|
+
- `StringFilterBuilder` - String comparison filters
|
|
916
|
+
- `NumericRangeFilterBuilder` - Numeric range filters
|
|
917
|
+
- `DateRangeFilterBuilder` - Date range filters
|
|
918
|
+
- And many more...
|
|
919
|
+
|
|
920
|
+
### Errors
|
|
921
|
+
|
|
922
|
+
- `PropertyCountLimitExceededError` - Thrown when search results exceed configured limits
|
|
923
|
+
|
|
924
|
+
## Development
|
|
925
|
+
|
|
926
|
+
```bash
|
|
927
|
+
# Install dependencies
|
|
928
|
+
npm install
|
|
929
|
+
|
|
930
|
+
# Build the package
|
|
931
|
+
npm run build
|
|
932
|
+
|
|
933
|
+
# Run tests
|
|
934
|
+
npm test
|
|
935
|
+
|
|
936
|
+
# Run integration tests (requires BATCHDATA_API_KEY)
|
|
937
|
+
npm run test:integration
|
|
938
|
+
|
|
939
|
+
# The built files will be in ./dist
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
### Running Integration Tests
|
|
943
|
+
|
|
944
|
+
Integration tests make real API calls and require a valid API key:
|
|
945
|
+
|
|
946
|
+
```bash
|
|
947
|
+
export BATCHDATA_API_KEY=your_api_key_here
|
|
948
|
+
npm run test:integration
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
## Publishing
|
|
952
|
+
|
|
953
|
+
This package is published to npm using trusted publishing (OIDC) via GitHub Actions. Publishing happens automatically when a version tag is pushed.
|
|
954
|
+
|
|
955
|
+
### Manual Publishing (if needed)
|
|
956
|
+
|
|
957
|
+
If you need to publish manually, set up `.npmrc` to use your npm token:
|
|
958
|
+
|
|
959
|
+
1. **Create `.npmrc` file** (add to `.gitignore` - already configured):
|
|
960
|
+
```bash
|
|
961
|
+
# Option 1: Use environment variable (recommended)
|
|
962
|
+
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc
|
|
963
|
+
|
|
964
|
+
# Option 2: Use token directly (less secure)
|
|
965
|
+
echo "//registry.npmjs.org/:_authToken=your-token-here" > .npmrc
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
2. **Set your npm token as an environment variable**:
|
|
969
|
+
```bash
|
|
970
|
+
export NPM_TOKEN=your-npm-token-here
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
3. **Publish**:
|
|
974
|
+
```bash
|
|
975
|
+
npm run build
|
|
976
|
+
npm publish --access public
|
|
977
|
+
```
|
|
978
|
+
|
|
979
|
+
**Note:** The `.npmrc` file is already in `.gitignore` so it won't be committed. See `.npmrc.example` for a template.
|
|
980
|
+
|
|
981
|
+
### Setting Up GitHub Actions Publishing (Trusted Publishing)
|
|
982
|
+
|
|
983
|
+
The workflow uses npm trusted publishing (OIDC) - no tokens needed! To set it up:
|
|
984
|
+
|
|
985
|
+
1. **Initial Manual Publish** (one-time, to create the package on npm):
|
|
986
|
+
```bash
|
|
987
|
+
# Set up your .npmrc first (see Manual Publishing section above)
|
|
988
|
+
npm run build
|
|
989
|
+
npm publish --access public
|
|
990
|
+
```
|
|
991
|
+
|
|
992
|
+
2. **Enable Trusted Publishing on npm**:
|
|
993
|
+
- Go to [npmjs.com](https://www.npmjs.com) and log in
|
|
994
|
+
- Navigate to your package: `@land-catalyst/batch-data-sdk`
|
|
995
|
+
- Go to **Package Settings** → **Automation** → **Trusted Publishing**
|
|
996
|
+
- Click **Add GitHub Actions workflow**
|
|
997
|
+
- Select repository: `land-catalyst/batch-data-sdk`
|
|
998
|
+
- Select workflow file: `.github/workflows/cd.yml`
|
|
999
|
+
- Click **Approve**
|
|
1000
|
+
|
|
1001
|
+
3. **Verify Setup**:
|
|
1002
|
+
- After enabling, the workflow will automatically publish to npm when you push a version tag
|
|
1003
|
+
- No authentication tokens needed - OIDC handles it automatically!
|
|
1004
|
+
- The package will show a verified checkmark on npm
|
|
1005
|
+
|
|
1006
|
+
For detailed setup instructions, see [SETUP_NPM_PUBLISHING.md](./SETUP_NPM_PUBLISHING.md).
|
|
1007
|
+
|
|
1008
|
+
### Versioning
|
|
1009
|
+
|
|
1010
|
+
To create a new version:
|
|
1011
|
+
|
|
1012
|
+
```bash
|
|
1013
|
+
# Patch version (1.1.12 -> 1.1.13)
|
|
1014
|
+
npm run v:patch
|
|
1015
|
+
|
|
1016
|
+
# Minor version (1.1.12 -> 1.2.0)
|
|
1017
|
+
npm run v:minor
|
|
1018
|
+
|
|
1019
|
+
# Major version (1.1.12 -> 2.0.0)
|
|
1020
|
+
npm run v:major
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
This will:
|
|
1024
|
+
1. Update the version in `package.json`
|
|
1025
|
+
2. Create a git tag
|
|
1026
|
+
3. Push the tag to GitHub
|
|
1027
|
+
4. Trigger the CD workflow to publish to npm
|
|
1028
|
+
|
|
1029
|
+
## License
|
|
1030
|
+
|
|
1031
|
+
MIT
|
|
1032
|
+
|