bmlt-query-client 1.0.7 → 1.0.9

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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(wc -l /Users/pjoyce/workspace/git/bmlt-enabled/bmlt-query-client/src/**/*.ts /Users/pjoyce/workspace/git/bmlt-enabled/bmlt-query-client/test/*.ts)"
5
+ ]
6
+ },
7
+ "disabledMcpjsonServers": [
8
+ "laravel-boost"
9
+ ]
10
+ }
@@ -87,7 +87,7 @@ import { BmltClient, Weekday, VenueType } from 'bmlt-query-client';
87
87
 
88
88
  // Initialize client with NYC demo server
89
89
  const client = new BmltClient({
90
- rootServerURL: 'https://latest.aws.bmlt.app/main_server',
90
+ serverURL: 'https://latest.aws.bmlt.app/main_server',
91
91
  });
92
92
 
93
93
  // Search by address with automatic geocoding
package/README.md CHANGED
@@ -38,7 +38,7 @@ The easiest way to use the BMLT Query Client in the browser is via ES modules:
38
38
 
39
39
  // Initialize the client
40
40
  const client = new BmltClient({
41
- rootServerURL: 'https://latest.aws.bmlt.app/main_server', // NYC demo server
41
+ serverURL: 'https://latest.aws.bmlt.app/main_server', // NYC demo server
42
42
  });
43
43
 
44
44
  // Search for virtual meetings
@@ -72,7 +72,7 @@ npm install bmlt-query-client
72
72
  import { BmltClient, VenueType, MeetingQueryBuilder } from 'bmlt-query-client';
73
73
 
74
74
  const client = new BmltClient({
75
- rootServerURL: 'https://your-bmlt-server.org/main_server',
75
+ serverURL: 'https://your-bmlt-server.org/main_server',
76
76
  });
77
77
 
78
78
  // Search for meetings
@@ -183,6 +183,50 @@ const todaysVirtualMeetings = await quickSearch
183
183
  .execute();
184
184
  ```
185
185
 
186
+ ### Finding Duplicate Meetings
187
+
188
+ When querying meetings from multiple service bodies or servers, use `findDuplicateMeetings` to identify the same meeting appearing in more than one list:
189
+
190
+ ```javascript
191
+ import { findDuplicateMeetings } from 'bmlt-query-client';
192
+
193
+ const [listA, listB] = await Promise.all([
194
+ client.searchMeetings({ services: [1] }),
195
+ client.searchMeetings({ services: [2] }),
196
+ ]);
197
+
198
+ const duplicates = findDuplicateMeetings([listA, listB]);
199
+
200
+ for (const group of duplicates) {
201
+ console.log(`Duplicate: ${group.key}`);
202
+ for (const { meeting, listIndex } of group.entries) {
203
+ console.log(` List ${listIndex}: id=${meeting.id_bigint}`);
204
+ }
205
+ }
206
+ ```
207
+
208
+ By default, meetings are compared on `weekday_tinyint`, `start_time`, `meeting_name`, and `location_text` (case-insensitive). You can customize the fields:
209
+
210
+ ```javascript
211
+ // Compare only on name and street address
212
+ const duplicates = findDuplicateMeetings([listA, listB], {
213
+ fields: ['meeting_name', 'location_street'],
214
+ normalize: true, // lowercase + trim (default)
215
+ });
216
+ ```
217
+
218
+ ### Counting Unique Groups
219
+
220
+ Use `countUniqueGroups` to get the total number of distinct groups in a meeting list. A group is identified by its service body plus meeting name (case-insensitive, trimmed), so multiple weekly occurrences of the same group count once:
221
+
222
+ ```javascript
223
+ import { countUniqueGroups } from 'bmlt-query-client';
224
+
225
+ const meetings = await client.searchMeetings();
226
+ const total = countUniqueGroups(meetings);
227
+ console.log(`Total unique groups: ${total}`);
228
+ ```
229
+
186
230
  ## Error Handling
187
231
 
188
232
  The client provides comprehensive error handling with specific error types:
@@ -253,9 +297,9 @@ console.log(client.getUserAgent()); // 'my-updated-app/2.0.0'
253
297
  client.setTimeout(60000); // 60 seconds
254
298
  console.log(client.getTimeout()); // 60000
255
299
 
256
- // Update root server URL
257
- client.setRootServerURL('https://new-server.org/main_server');
258
- console.log(client.getRootServerURL());
300
+ // Update server URL
301
+ client.setServerURL('https://new-server.org/main_server');
302
+ console.log(client.getServerURL());
259
303
 
260
304
  // Update default data format
261
305
  client.setDefaultFormat(BmltDataFormat.JSONP);
@@ -266,7 +310,7 @@ console.log(client.getDefaultFormat());
266
310
 
267
311
  ```javascript
268
312
  const client = new BmltClient({
269
- rootServerURL: 'https://your-server.org/main_server', // Required
313
+ serverURL: 'https://your-server.org/main_server', // Required
270
314
  defaultFormat: BmltDataFormat.JSON, // Optional
271
315
  timeout: 30000, // 30 seconds
272
316
  userAgent: 'my-app/1.0.0', // Custom user agent
@@ -283,7 +327,7 @@ const client = new BmltClient({
283
327
 
284
328
  ```javascript
285
329
  const client = new BmltClient({
286
- rootServerURL: 'https://your-server.org/main_server',
330
+ serverURL: 'https://your-server.org/main_server',
287
331
  geocodingOptions: {
288
332
  countryCode: 'us', // ISO country code bias
289
333
  viewbox: [-74.2, 40.4, -73.7, 40.9], // Geographic bounding box [w,s,e,n]
package/dist/app.d.ts CHANGED
@@ -11,7 +11,7 @@ export declare class BmltClient {
11
11
  private timeout;
12
12
  private userAgent;
13
13
  private readonly geocodingService?;
14
- private rootServerURL;
14
+ private serverURL;
15
15
  private defaultFormat;
16
16
  constructor(options: BmltClientOptions);
17
17
  /**
@@ -81,13 +81,13 @@ export declare class BmltClient {
81
81
  */
82
82
  reverseGeocode(coordinates: Coordinates, options?: Partial<GeocodeOptions>): Promise<GeocodeResult>;
83
83
  /**
84
- * Get the root server URL
84
+ * Get the server URL
85
85
  */
86
- getRootServerURL(): string;
86
+ getServerURL(): string;
87
87
  /**
88
- * Update the root server URL
88
+ * Update the server URL
89
89
  */
90
- setRootServerURL(url: string): void;
90
+ setServerURL(url: string): void;
91
91
  /**
92
92
  * Get the default data format
93
93
  */
@@ -126,8 +126,8 @@ export declare class BmltClient {
126
126
  }
127
127
 
128
128
  export declare interface BmltClientOptions {
129
- /** Root server URL */
130
- rootServerURL: string;
129
+ /** Server URL */
130
+ serverURL: string;
131
131
  /** Default data format */
132
132
  defaultFormat?: BmltDataFormat;
133
133
  /** HTTP request timeout in milliseconds */
@@ -284,6 +284,15 @@ export declare interface Coordinates {
284
284
  longitude: number;
285
285
  }
286
286
 
287
+ /**
288
+ * Count unique groups across a list of meetings.
289
+ *
290
+ * A "group" is identified by the combination of service body and meeting name
291
+ * (case-insensitive, trimmed), so multiple weekly meetings of the same group
292
+ * count once.
293
+ */
294
+ export declare function countUniqueGroups(meetings: Meeting[]): number;
295
+
287
296
  export declare interface CoverageArea {
288
297
  /** North boundary */
289
298
  north_latitude: number;
@@ -295,6 +304,21 @@ export declare interface CoverageArea {
295
304
  west_longitude: number;
296
305
  }
297
306
 
307
+ /** A single meeting paired with the index of the list it came from */
308
+ export declare interface DuplicateMeetingEntry {
309
+ meeting: Meeting;
310
+ /** Index of the source list in the array passed to findDuplicateMeetings */
311
+ listIndex: number;
312
+ }
313
+
314
+ /** A group of meetings from different lists that match on the comparison key */
315
+ export declare interface DuplicateMeetingGroup {
316
+ /** Composite key used for matching */
317
+ key: string;
318
+ /** All matching entries, one per occurrence across the lists */
319
+ entries: DuplicateMeetingEntry[];
320
+ }
321
+
298
322
  /**
299
323
  * Factory class for creating specific error types
300
324
  */
@@ -372,6 +396,37 @@ export declare interface FieldValuesParams extends BaseSearchParams {
372
396
  all_formats?: boolean;
373
397
  }
374
398
 
399
+ /**
400
+ * Find meetings that appear in more than one of the provided lists.
401
+ *
402
+ * Comparison is done via a composite key built from the specified fields
403
+ * (default: weekday, start time, meeting name, location). Only groups that
404
+ * span at least two different lists are returned.
405
+ *
406
+ * @example
407
+ * const duplicates = findDuplicateMeetings([listA, listB]);
408
+ * for (const group of duplicates) {
409
+ * console.log(`Duplicate: ${group.key}`);
410
+ * for (const { meeting, listIndex } of group.entries) {
411
+ * console.log(` List ${listIndex}: id=${meeting.id_bigint}`);
412
+ * }
413
+ * }
414
+ */
415
+ export declare function findDuplicateMeetings(meetingLists: Meeting[][], options?: FindDuplicatesOptions): DuplicateMeetingGroup[];
416
+
417
+ export declare interface FindDuplicatesOptions {
418
+ /**
419
+ * Meeting fields used to build the composite comparison key.
420
+ * Defaults to weekday, start time, meeting name, and location.
421
+ */
422
+ fields?: Array<keyof Meeting>;
423
+ /**
424
+ * Normalize string values before comparing (lowercase + trim).
425
+ * Defaults to true.
426
+ */
427
+ normalize?: boolean;
428
+ }
429
+
375
430
  export declare interface Format {
376
431
  /** Format ID */
377
432
  id: string;
@@ -640,6 +695,13 @@ export declare class MeetingQueryBuilder {
640
695
  * Search for specific text
641
696
  */
642
697
  searchText(text: string): this;
698
+ /**
699
+ * Search by address using server-side geocoding (StringSearchIsAnAddress=1).
700
+ * Note: typically broken on most BMLT servers because their Google API key uses
701
+ * HTTP referer restrictions rather than server IP allowlisting. Prefer
702
+ * BmltClient.searchMeetingsByAddress() which uses Nominatim client-side.
703
+ */
704
+ searchAddress(address: string, radius?: number): this;
643
705
  /**
644
706
  * Meetings starting after specific time
645
707
  */
@@ -881,6 +943,8 @@ export declare interface SearchResultsParams extends BaseSearchParams {
881
943
  SearchString?: string;
882
944
  /** Search radius for geographic searches */
883
945
  SearchStringRadius?: number;
946
+ /** Treat SearchString as an address and have the server geocode it. Note: typically broken on most servers because the Google API key uses HTTP referer restrictions rather than server IP allowlisting. Prefer searchMeetingsByAddress() which uses Nominatim client-side. */
947
+ StringSearchIsAnAddress?: boolean;
884
948
  /** Meetings starting after hour (0-23) */
885
949
  StartsAfterH?: number;
886
950
  /** Meetings starting after minute (0-59) */
@@ -989,7 +1053,7 @@ export declare enum SortKey {
989
1053
  }
990
1054
 
991
1055
  export declare interface URLBuilderOptions {
992
- rootServerURL: string;
1056
+ serverURL: string;
993
1057
  format: BmltDataFormat;
994
1058
  endpoint: BmltEndpoint;
995
1059
  parameters?: Record<string, unknown>;
@@ -1011,9 +1075,9 @@ export declare function validateEndpointFormat(endpoint: BmltEndpoint, format: B
1011
1075
  export declare function validateRadius(radius: number): void;
1012
1076
 
1013
1077
  /**
1014
- * Clean and validate a root server URL
1078
+ * Clean and validate a server URL
1015
1079
  */
1016
- export declare function validateRootServerURL(url: string): string;
1080
+ export declare function validateServerURL(url: string): string;
1017
1081
 
1018
1082
  export declare enum VenueType {
1019
1083
  IN_PERSON = 1,