@squawk/fixes 0.3.1 → 0.4.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/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](../../../LICENSE.md) [![npm](https://img.shields.io/npm/v/@squawk/fixes)](https://www.npmjs.com/package/@squawk/fixes) ![TypeScript](https://img.shields.io/badge/TypeScript-blue?logo=typescript&logoColor=white)
4
4
 
5
5
  Pure logic library for querying US fix/waypoint data. Look up fixes by
6
- identifier, geographic proximity, or identifier search. Contains no bundled
6
+ identifier, geographic proximity, or fuzzy identifier search. Contains no bundled
7
7
  data - accepts an array of Fix records at initialization. For zero-config use,
8
8
  pair with `@squawk/fix-data`.
9
9
 
@@ -26,8 +26,9 @@ for (const result of nearby) {
26
26
  console.log(result.fix.identifier, result.distanceNm, 'nm');
27
27
  }
28
28
 
29
- // Search by identifier
29
+ // Fuzzy-search by identifier (scored, best match first)
30
30
  const results = resolver.search({ text: 'BOS' });
31
+ console.log(results[0]?.fix.identifier, results[0]?.score);
31
32
  ```
32
33
 
33
34
  Consumers who have their own fix data can use this package standalone:
@@ -100,13 +101,27 @@ const nearby = resolver.nearest({
100
101
 
101
102
  ### `resolver.search(query)`
102
103
 
103
- Searches fixes by identifier using case-insensitive substring matching.
104
- Results are returned in alphabetical order by identifier.
104
+ Fuzzy-searches fixes by identifier. Matching is case-insensitive and tolerant of
105
+ prefixes, substrings, subsequences, and small typos. Results are scored and
106
+ returned best-match first.
105
107
 
106
- | Property | Type | Description |
107
- | ---------- | ------------------------ | --------------------------------------------------------------------- |
108
- | `text` | string | Case-insensitive substring to match against fix identifier |
109
- | `limit` | number | Optional. Maximum number of results. Defaults to 20 |
110
- | `useCodes` | ReadonlySet\<FixUseCode> | Optional. When provided, only fixes with these use codes are returned |
108
+ | Property | Type | Description |
109
+ | ---------- | ------------------------ | ---------------------------------------------------------------------------------------- |
110
+ | `text` | string | Search text, matched fuzzily against each fix's identifier |
111
+ | `limit` | number | Optional. Maximum number of results. Defaults to 20 |
112
+ | `useCodes` | ReadonlySet\<FixUseCode> | Optional. When provided, only fixes with these use codes are returned |
113
+ | `minScore` | number | Optional. Minimum match score (exclusive) in `[0, 1]` a result must reach. Defaults to 0 |
111
114
 
112
- Returns `Fix[]`.
115
+ Returns `FixSearchResult[]`, sorted by descending score, each containing:
116
+
117
+ - `fix` - the matched Fix record
118
+ - `score` - match strength in `[0, 1]`, where 1 is an exact identifier match
119
+ - `matchedField` - which field produced the best match: `'identifier'`
120
+ - `ranges` - matched character ranges within the best-matching field's text, for highlighting
121
+
122
+ ```typescript
123
+ const results = resolver.search({ text: 'BOS', limit: 10 });
124
+ for (const { fix, score } of results) {
125
+ console.log(fix.identifier, score);
126
+ }
127
+ ```
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  * @packageDocumentation
3
3
  * Pure logic library for querying US fix/waypoint data.
4
4
  */
5
+ export type { MatchRange } from '@squawk/search';
5
6
  export { createFixResolver } from './resolver.js';
6
- export type { FixResolver, FixResolverOptions, NearestFixQuery, NearestFixResult, FixSearchQuery, } from './resolver.js';
7
+ export type { FixResolver, FixResolverOptions, NearestFixQuery, NearestFixResult, FixSearchQuery, FixSearchField, FixSearchResult, } from './resolver.js';
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,cAAc,GACf,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,YAAY,EACV,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,eAAe,GAChB,MAAM,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1 @@
1
- /**
2
- * @packageDocumentation
3
- * Pure logic library for querying US fix/waypoint data.
4
- */
5
1
  export { createFixResolver } from './resolver.js';
@@ -1,3 +1,4 @@
1
+ import type { MatchRange } from '@squawk/search';
1
2
  import type { Fix, FixUseCode } from '@squawk/types';
2
3
  /**
3
4
  * Options for creating a fix resolver.
@@ -31,15 +32,34 @@ export interface NearestFixResult {
31
32
  distanceNm: number;
32
33
  }
33
34
  /**
34
- * Options for a text search query against fix identifiers.
35
+ * The searchable field a {@link FixSearchResult} can match on.
36
+ */
37
+ export type FixSearchField = 'identifier';
38
+ /**
39
+ * Options for a fuzzy text search query against fix identifiers.
35
40
  */
36
41
  export interface FixSearchQuery {
37
- /** Case-insensitive substring to match against fix identifier. */
42
+ /** Search text, matched fuzzily and case-insensitively against each fix's identifier. */
38
43
  text: string;
39
44
  /** Maximum number of results to return. Defaults to 20. */
40
45
  limit?: number;
41
46
  /** Optional set of use codes to include. When omitted, all use codes are included. */
42
47
  useCodes?: ReadonlySet<FixUseCode>;
48
+ /** Minimum match score (exclusive) in `[0, 1]` a result must reach. Defaults to 0, which keeps every match. Raise it to drop weak fuzzy matches. */
49
+ minScore?: number;
50
+ }
51
+ /**
52
+ * A scored fix result from a fuzzy {@link FixResolver.search}.
53
+ */
54
+ export interface FixSearchResult {
55
+ /** The matched fix record. */
56
+ fix: Fix;
57
+ /** Match strength in `[0, 1]`, where 1 is an exact identifier match. */
58
+ score: number;
59
+ /** Which field produced the best match, identifying what {@link FixSearchResult.ranges} index into. */
60
+ matchedField: FixSearchField;
61
+ /** Matched character ranges within the best-matching field's text, for highlighting. */
62
+ ranges: MatchRange[];
43
63
  }
44
64
  /**
45
65
  * A stateless resolver providing fix lookup methods.
@@ -57,10 +77,11 @@ export interface FixResolver {
57
77
  */
58
78
  nearest(query: NearestFixQuery): NearestFixResult[];
59
79
  /**
60
- * Searches fixes by identifier using case-insensitive substring matching.
61
- * Results are returned in alphabetical order by identifier.
80
+ * Fuzzy-searches fixes by identifier. Results are scored and returned
81
+ * best-match first, each carrying the matched field and character ranges
82
+ * for highlighting.
62
83
  */
63
- search(query: FixSearchQuery): Fix[];
84
+ search(query: FixSearchQuery): FixSearchResult[];
64
85
  }
65
86
  /**
66
87
  * Creates a stateless fix resolver. The resolver accepts an array of
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;IAE9B;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,EAAE,CAAC;IAEpD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,GAAG,EAAE,CAAC;CACtC;AAiBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAgE1E"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACrE,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,IAAI,EAAE,GAAG,EAAE,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8BAA8B;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,0DAA0D;IAC1D,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,YAAY,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,QAAQ,CAAC,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IACnC,oJAAoJ;IACpJ,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8BAA8B;IAC9B,GAAG,EAAE,GAAG,CAAC;IACT,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,uGAAuG;IACvG,YAAY,EAAE,cAAc,CAAC;IAC7B,wFAAwF;IACxF,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;OAIG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;IAE9B;;;OAGG;IACH,OAAO,CAAC,KAAK,EAAE,eAAe,GAAG,gBAAgB,EAAE,CAAC;IAEpD;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe,EAAE,CAAC;CAClD;AAiBD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA4D1E"}
package/dist/resolver.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { greatCircle } from '@squawk/geo';
2
+ import { fuzzySearch } from '@squawk/search';
2
3
  /**
3
4
  * Default maximum distance in nautical miles for nearest-fix queries.
4
5
  */
@@ -64,22 +65,21 @@ export function createFixResolver(options) {
64
65
  return results.slice(0, limit);
65
66
  },
66
67
  search(query) {
67
- const limit = query.limit ?? DEFAULT_SEARCH_LIMIT;
68
- const needle = query.text.toUpperCase();
69
- if (needle.length === 0) {
70
- return [];
68
+ const options = {
69
+ keys: (fix) => [{ name: 'identifier', text: fix.identifier }],
70
+ limit: query.limit ?? DEFAULT_SEARCH_LIMIT,
71
+ minScore: query.minScore ?? 0,
72
+ };
73
+ const useCodes = query.useCodes;
74
+ if (useCodes) {
75
+ options.filter = (fix) => useCodes.has(fix.useCode);
71
76
  }
72
- const results = [];
73
- for (const fix of fixes) {
74
- if (query.useCodes && !query.useCodes.has(fix.useCode)) {
75
- continue;
76
- }
77
- if (fix.identifier.toUpperCase().includes(needle)) {
78
- results.push(fix);
79
- }
80
- }
81
- results.sort((a, b) => a.identifier.localeCompare(b.identifier));
82
- return results.slice(0, limit);
77
+ return fuzzySearch(fixes, query.text, options).map((match) => ({
78
+ fix: match.item,
79
+ score: match.score,
80
+ matchedField: match.field,
81
+ ranges: match.ranges,
82
+ }));
83
83
  },
84
84
  };
85
85
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squawk/fixes",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "type": "module",
5
5
  "description": "Fix/waypoint queries by identifier, location, or name search",
6
6
  "author": "Neil Cochran",
@@ -36,15 +36,18 @@
36
36
  "test": "vitest run",
37
37
  "test:coverage": "vitest run --coverage",
38
38
  "lint": "tsc --noEmit && eslint src",
39
- "lint:pack": "publint && attw --pack . --profile esm-only"
39
+ "lint:pack": "publint && attw --pack . --profile esm-only",
40
+ "api:check": "api-extractor run --verbose",
41
+ "api:report": "api-extractor run --local --verbose"
40
42
  },
41
43
  "dependencies": {
42
44
  "@squawk/geo": "^0.4.3",
45
+ "@squawk/search": "^0.1.0",
43
46
  "@squawk/types": "^0.8.0"
44
47
  },
45
48
  "devDependencies": {
46
49
  "@squawk/fix-data": "^0.6.3",
47
- "@types/node": "^25.6.0"
50
+ "@types/node": "^25.9.3"
48
51
  },
49
52
  "keywords": [
50
53
  "aviation",