bmlt-query-client 1.0.1

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/README.old.md ADDED
@@ -0,0 +1,490 @@
1
+ # BMLT Query Client
2
+
3
+ A comprehensive TypeScript client for querying BMLT (Basic Meeting List Tool) servers with built-in geocoding support using Nominatim. This client provides a modern, type-safe interface to all BMLT API endpoints with automatic rate limiting, retry logic, and geographic search capabilities.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Full TypeScript support** with comprehensive type definitions
8
+ - 🌍 **Built-in geocoding** using Nominatim (replaces broken StringSearchIsAnAddress)
9
+ - 🔄 **Automatic retry logic** with exponential backoff
10
+ - ⚡ **Rate limiting** to respect API limits
11
+ - 🎯 **Fluent query builder** for complex searches
12
+ - 📡 **All BMLT endpoints** supported
13
+ - 🛡️ **Comprehensive error handling**
14
+ - 📖 **Extensive documentation** and examples
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install bmlt-query-client
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { BmltClient, Weekday, VenueType } from 'bmlt-query-client';
26
+
27
+ // Initialize the client with NYC demo server
28
+ const client = new BmltClient({
29
+ rootServerURL: 'https://latest.aws.bmlt.app/main_server'
30
+ });
31
+
32
+ // Search for meetings
33
+ const meetings = await client.searchMeetings({
34
+ weekdays: [Weekday.MONDAY, Weekday.WEDNESDAY],
35
+ venue_types: VenueType.VIRTUAL
36
+ });
37
+
38
+ // Search by address (with automatic geocoding)
39
+ const nearbyMeetings = await client.searchMeetingsByAddress({
40
+ address: 'Times Square, New York, NY',
41
+ radiusMiles: 2,
42
+ sortByDistance: true
43
+ });
44
+ ```
45
+
46
+ ## API Reference
47
+
48
+ ### BmltClient
49
+
50
+ The main client class for interacting with BMLT servers.
51
+
52
+ #### Constructor Options
53
+
54
+ ```typescript
55
+ interface BmltClientOptions {
56
+ rootServerURL: string; // BMLT root server URL
57
+ defaultFormat?: BmltDataFormat; // Default response format (JSON)
58
+ timeout?: number; // Request timeout in ms (30000)
59
+ userAgent?: string; // Custom user agent
60
+ geocodingOptions?: GeocodeOptions; // Nominatim geocoding options
61
+ enableGeocoding?: boolean; // Enable geocoding service (true)
62
+ }
63
+ ```
64
+
65
+ #### Basic Methods
66
+
67
+ ```typescript
68
+ // Search for meetings with parameters
69
+ const meetings = await client.searchMeetings({
70
+ weekdays: [Weekday.SATURDAY, Weekday.SUNDAY],
71
+ venue_types: VenueType.IN_PERSON,
72
+ geo_width: 5,
73
+ lat_val: 40.7580, // Times Square
74
+ long_val: -73.9855
75
+ });
76
+
77
+ // Get meeting formats
78
+ const formats = await client.getFormats({
79
+ lang_enum: Language.ENGLISH
80
+ });
81
+
82
+ // Get service bodies
83
+ const serviceBodies = await client.getServiceBodies({
84
+ recursive: true
85
+ });
86
+
87
+ // Get server information
88
+ const serverInfo = await client.getServerInfo();
89
+ ```
90
+
91
+ #### Geographic Search Methods
92
+
93
+ ```typescript
94
+ // Search by address (with geocoding)
95
+ const meetings = await client.searchMeetingsByAddress({
96
+ address: 'Central Park, New York, NY',
97
+ radiusKm: 3,
98
+ searchParams: {
99
+ venue_types: VenueType.IN_PERSON
100
+ }
101
+ });
102
+
103
+ // Search by coordinates
104
+ const meetings = await client.searchMeetingsByCoordinates(
105
+ { latitude: 40.7614, longitude: -73.9776 }, // Times Square
106
+ 2, // radius in miles
107
+ undefined, // radius in km
108
+ { weekdays: Weekday.MONDAY }
109
+ );
110
+
111
+ // Geocode an address
112
+ const result = await client.geocodeAddress('Brooklyn Bridge, New York, NY');
113
+ console.log(result.coordinates); // { latitude: 40.7061, longitude: -73.9969 }
114
+ ```
115
+
116
+ ### Query Builder
117
+
118
+ Use the fluent query builder for complex searches:
119
+
120
+ ```typescript
121
+ import { MeetingQueryBuilder, QuickSearch } from 'bmlt-query-client';
122
+
123
+ const client = new BmltClient({ rootServerURL: 'https://latest.aws.bmlt.app/main_server' });
124
+
125
+ // Using the query builder
126
+ const meetings = await new MeetingQueryBuilder(client)
127
+ .virtualOnly()
128
+ .onWeekdays(Weekday.MONDAY, Weekday.WEDNESDAY, Weekday.FRIDAY)
129
+ .startingAfter(18, 0) // 6:00 PM
130
+ .endingBefore(21, 0) // 9:00 PM
131
+ .sortByDistance()
132
+ .paginate(20, 1)
133
+ .execute();
134
+
135
+ // Or execute with address geocoding
136
+ const nearbyMeetings = await new MeetingQueryBuilder(client)
137
+ .inPersonOnly()
138
+ .minimumDuration(1, 0) // At least 1 hour
139
+ .executeNearAddress('Central Park, New York, NY', 2); // 2 miles radius
140
+
141
+ // Quick search patterns
142
+ const quickSearch = new QuickSearch(client);
143
+
144
+ const todaysMeetings = await quickSearch.today().execute();
145
+ const weekendMeetings = await quickSearch.weekend().execute();
146
+ const eveningMeetings = await quickSearch.evening().execute();
147
+ const virtualMeetings = await quickSearch.virtual().execute();
148
+ ```
149
+
150
+ ### Available Query Builder Methods
151
+
152
+ ```typescript
153
+ // Meeting IDs
154
+ .meetingIds(123) or .meetingIds([123, 456])
155
+ .meetingIds([123, 456], true) // exclude these IDs
156
+
157
+ // Time-based filters
158
+ .onWeekdays(Weekday.MONDAY, Weekday.FRIDAY)
159
+ .notOnWeekdays(Weekday.SATURDAY, Weekday.SUNDAY)
160
+ .startingAfter(18, 30) // 6:30 PM
161
+ .startingBefore(12, 0) // Before noon
162
+ .endingBefore(21, 0) // Before 9 PM
163
+
164
+ // Duration filters
165
+ .minimumDuration(1, 0) // At least 1 hour
166
+ .maximumDuration(2, 0) // At most 2 hours
167
+
168
+ // Venue types
169
+ .inPersonOnly()
170
+ .virtualOnly()
171
+ .hybridOnly()
172
+ .virtualOrHybrid()
173
+ .venueTypes(VenueType.IN_PERSON, VenueType.HYBRID)
174
+
175
+ // Formats
176
+ .formats([17, 54]) // Include these formats
177
+ .formats([11], true) // Exclude format 11
178
+ .anyFormat() // Use OR logic for formats
179
+
180
+ // Geographic
181
+ .nearCoordinates({ latitude: 40.7128, longitude: -74.0060 }, 10) // 10 miles
182
+ .sortByDistance()
183
+
184
+ // Text search
185
+ .searchText('Step Study')
186
+
187
+ // Service bodies
188
+ .serviceBodies([123, 456])
189
+ .includeChildServiceBodies()
190
+
191
+ // Sorting and pagination
192
+ .sortBy('meeting_name', 'start_time')
193
+ .sortByAlias(SortKey.WEEKDAY_STATE)
194
+ .paginate(25, 2) // 25 per page, page 2
195
+
196
+ // Response format
197
+ .selectFields('meeting_name', 'location_text', 'start_time')
198
+ .includeFormats()
199
+ .language(Language.SPANISH)
200
+
201
+ // Publication status
202
+ .includeUnpublished()
203
+ .unpublishedOnly()
204
+
205
+ // Utilities
206
+ .getParams() // Get current parameters
207
+ .reset() // Reset all parameters
208
+ .clone() // Clone the builder
209
+ ```
210
+
211
+ ## Error Handling
212
+
213
+ The client provides comprehensive error handling with specific error types:
214
+
215
+ ```typescript
216
+ import { BmltQueryError, BmltErrorType } from 'bmlt-query-client';
217
+
218
+ try {
219
+ const meetings = await client.searchMeetings();
220
+ } catch (error) {
221
+ if (error instanceof BmltQueryError) {
222
+ console.log(`Error type: ${error.type}`);
223
+ console.log(`User message: ${error.getUserMessage()}`);
224
+ console.log(`Is retryable: ${error.isRetryable()}`);
225
+
226
+ if (error.isType(BmltErrorType.GEOCODING_ERROR)) {
227
+ console.log('Address could not be geocoded');
228
+ }
229
+
230
+ if (error.isType(BmltErrorType.RATE_LIMIT_ERROR)) {
231
+ console.log('Rate limit exceeded, wait and retry');
232
+ }
233
+ }
234
+ }
235
+ ```
236
+
237
+ ### Error Types
238
+
239
+ - `API_ERROR` - General API errors
240
+ - `NETWORK_ERROR` - Network connectivity issues
241
+ - `VALIDATION_ERROR` - Invalid input parameters
242
+ - `GEOCODING_ERROR` - Address geocoding failures
243
+ - `RATE_LIMIT_ERROR` - Rate limits exceeded
244
+ - `TIMEOUT_ERROR` - Request timeouts
245
+ - `AUTHENTICATION_ERROR` - Authentication failures
246
+ - `SERVER_ERROR` - Server-side errors (5xx)
247
+ - `CLIENT_ERROR` - Client-side errors (4xx)
248
+ - `CONFIGURATION_ERROR` - Invalid configuration
249
+
250
+ ## Configuration
251
+
252
+ ### Geocoding Options
253
+
254
+ ```typescript
255
+ const client = new BmltClient({
256
+ rootServerURL: 'https://example.org',
257
+ geocodingOptions: {
258
+ // Retry options
259
+ retryCount: 3,
260
+ timeout: 10000,
261
+ userAgent: 'MyApp/1.0.0',
262
+
263
+ // Region bias (defaults to 'us')
264
+ countryCode: 'ca', // Bias results to Canada
265
+ viewbox: [-125, 25, -65, 50], // [minLon, minLat, maxLon, maxLat]
266
+ bounded: true, // Restrict results to viewbox
267
+
268
+ // Rate limiting (Nominatim allows 1 request per second)
269
+ intervalCap: 1, // Max requests per interval
270
+ interval: 1000, // Interval in ms
271
+ concurrency: 1 // Max concurrent requests
272
+ }
273
+ });
274
+ ```
275
+
276
+ ### Custom HTTP Configuration
277
+
278
+ ```typescript
279
+ const client = new BmltClient({
280
+ rootServerURL: 'https://example.org',
281
+ timeout: 60000, // 60 second timeout
282
+ userAgent: 'MyApp/2.0.0 (contact@example.com)'
283
+ });
284
+ ```
285
+
286
+ ### Region Bias for Geocoding
287
+
288
+ The geocoding service supports region bias to improve address matching accuracy:
289
+
290
+ ```typescript
291
+ // Default US bias
292
+ const usClient = new BmltClient({
293
+ rootServerURL: 'https://example.org'
294
+ // countryCode defaults to 'us'
295
+ });
296
+
297
+ // Canadian bias
298
+ const caClient = new BmltClient({
299
+ rootServerURL: 'https://example.org',
300
+ geocodingOptions: {
301
+ countryCode: 'ca'
302
+ }
303
+ });
304
+
305
+ // Geographic bounding box (North America)
306
+ const boundedClient = new BmltClient({
307
+ rootServerURL: 'https://example.org',
308
+ geocodingOptions: {
309
+ viewbox: [-140, 25, -50, 70], // [west, south, east, north]
310
+ bounded: true // Restrict results to this area
311
+ }
312
+ });
313
+ ```
314
+
315
+ ## Data Types
316
+
317
+ ### Enums
318
+
319
+ ```typescript
320
+ // Weekdays (1-7)
321
+ enum Weekday {
322
+ SUNDAY = 1, MONDAY = 2, TUESDAY = 3, WEDNESDAY = 4,
323
+ THURSDAY = 5, FRIDAY = 6, SATURDAY = 7
324
+ }
325
+
326
+ // Venue types
327
+ enum VenueType {
328
+ IN_PERSON = 1, VIRTUAL = 2, HYBRID = 3
329
+ }
330
+
331
+ // Response formats
332
+ enum BmltDataFormat {
333
+ JSON = 'json', JSONP = 'jsonp', TSML = 'tsml', CSV = 'csv'
334
+ }
335
+
336
+ // Languages
337
+ enum Language {
338
+ ENGLISH = 'en', GERMAN = 'de', SPANISH = 'es', FRENCH = 'fr',
339
+ ITALIAN = 'it', PORTUGUESE = 'pt', SWEDISH = 'sv', // ... and more
340
+ }
341
+ ```
342
+
343
+ ### Response Interfaces
344
+
345
+ ```typescript
346
+ interface Meeting {
347
+ id_bigint: string;
348
+ meeting_name: string;
349
+ weekday_tinyint: number;
350
+ venue_type: number;
351
+ start_time: string;
352
+ duration_time: string;
353
+ location_text: string;
354
+ latitude: number;
355
+ longitude: number;
356
+ // ... and many more fields
357
+ }
358
+
359
+ interface Format {
360
+ shared_id_bigint: string;
361
+ key_string: string;
362
+ name_string: string;
363
+ description_string: string;
364
+ }
365
+
366
+ interface ServiceBody {
367
+ id: string;
368
+ name: string;
369
+ type: string;
370
+ // ... additional fields
371
+ }
372
+ ```
373
+
374
+ ## Advanced Usage
375
+
376
+ ### Custom Geocoding Service
377
+
378
+ ```typescript
379
+ import { GeocodingService } from 'bmlt-query-client';
380
+
381
+ const geocoder = new GeocodingService({
382
+ retryCount: 5,
383
+ timeout: 15000,
384
+ userAgent: 'MyApp/1.0.0',
385
+ intervalCap: 1,
386
+ interval: 1000
387
+ });
388
+
389
+ // Batch geocode multiple addresses
390
+ const addresses = [
391
+ '123 Main St, Boston, MA',
392
+ '456 Oak Ave, Seattle, WA',
393
+ '789 Pine St, Portland, OR'
394
+ ];
395
+
396
+ const results = await geocoder.batchGeocode(addresses);
397
+ results.forEach(result => {
398
+ if (result) {
399
+ console.log(`${result.display_name}: ${result.coordinates.latitude}, ${result.coordinates.longitude}`);
400
+ }
401
+ });
402
+
403
+ // Reverse geocoding
404
+ const address = await geocoder.reverseGeocode({
405
+ latitude: 40.7128,
406
+ longitude: -74.0060
407
+ });
408
+ console.log(address.display_name); // "New York, NY, USA"
409
+ ```
410
+
411
+ ### Server Information and Capabilities
412
+
413
+ ```typescript
414
+ // Get server info
415
+ const info = await client.getServerInfo();
416
+ console.log(`Server version: ${info.version}`);
417
+ console.log(`Available endpoints: ${info.availableEndpoints.join(', ')}`);
418
+
419
+ // Get coverage area
420
+ const coverage = await client.getCoverageArea();
421
+ console.log(`Coverage: ${coverage.north_latitude}, ${coverage.south_latitude}`);
422
+
423
+ // Get available field keys
424
+ const fieldKeys = await client.getFieldKeys();
425
+ fieldKeys.forEach(field => {
426
+ console.log(`${field.key}: ${field.description}`);
427
+ });
428
+ ```
429
+
430
+ ### Working with Changes
431
+
432
+ ```typescript
433
+ // Get changes in date range
434
+ const changes = await client.getChanges({
435
+ start_date: '2024-01-01',
436
+ end_date: '2024-01-31',
437
+ service_body_id: 123
438
+ });
439
+
440
+ changes.forEach(change => {
441
+ console.log(`${change.change_date}: ${change.change_description}`);
442
+ });
443
+ ```
444
+
445
+ ### Pagination
446
+
447
+ ```typescript
448
+ // Get all meetings with pagination
449
+ async function getAllMeetings() {
450
+ const allMeetings = [];
451
+ let pageNum = 1;
452
+ const pageSize = 100;
453
+
454
+ while (true) {
455
+ const meetings = await client.searchMeetings({
456
+ page_size: pageSize,
457
+ page_num: pageNum
458
+ });
459
+
460
+ if (meetings.length === 0) break;
461
+
462
+ allMeetings.push(...meetings);
463
+ pageNum++;
464
+
465
+ if (meetings.length < pageSize) break; // Last page
466
+ }
467
+
468
+ return allMeetings;
469
+ }
470
+ ```
471
+
472
+ ## Best Practices
473
+
474
+ 1. **Rate Limiting**: The client includes built-in rate limiting for geocoding. For API requests, be mindful of server load.
475
+
476
+ 2. **Error Handling**: Always handle errors appropriately and use the specific error types for better user experience.
477
+
478
+ 3. **Caching**: Consider caching results for formats, service bodies, and server info as they don't change frequently.
479
+
480
+ 4. **Pagination**: Use pagination for large result sets to avoid overwhelming the server.
481
+
482
+ 5. **Geocoding**: The Nominatim service has usage policies. Be respectful and don't abuse it. Consider implementing your own caching layer for frequently geocoded addresses. The geocoding service always returns the first (most relevant) result and defaults to US region bias.
483
+
484
+ ## License
485
+
486
+ MIT
487
+
488
+ ## Contributing
489
+
490
+ Contributions are welcome! Please feel free to submit a Pull Request.