@squawk/fixes 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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # @squawk/fixes
2
+
3
+ Pure logic library for querying US fix/waypoint data. Look up fixes by
4
+ identifier, geographic proximity, or identifier search. Contains no bundled
5
+ data - accepts an array of Fix records at initialization. For zero-config use,
6
+ pair with `@squawk/fix-data`.
7
+
8
+ ## Usage
9
+
10
+ ```typescript
11
+ import { usBundledFixes } from '@squawk/fix-data';
12
+ import { createFixResolver } from '@squawk/fixes';
13
+
14
+ const resolver = createFixResolver({ data: usBundledFixes.records });
15
+
16
+ // Look up by identifier
17
+ const merit = resolver.byIdent('MERIT');
18
+
19
+ // Find nearest fixes to a position
20
+ const nearby = resolver.nearest({ lat: 40.6413, lon: -73.7781 });
21
+ for (const result of nearby) {
22
+ console.log(result.fix.identifier, result.distanceNm, 'nm');
23
+ }
24
+
25
+ // Search by identifier
26
+ const results = resolver.search({ text: 'BOS' });
27
+ ```
28
+
29
+ Consumers who have their own fix data can use this package standalone:
30
+
31
+ ```typescript
32
+ import { createFixResolver } from '@squawk/fixes';
33
+
34
+ const resolver = createFixResolver({ data: myFixes });
35
+ ```
36
+
37
+ ## API
38
+
39
+ ### `createFixResolver(options)`
40
+
41
+ Creates a resolver object from an array of Fix records.
42
+
43
+ **Parameters:**
44
+
45
+ - `options.data` - an array of `Fix` objects (from `@squawk/types`)
46
+
47
+ **Returns:** `FixResolver` - an object with the lookup methods described below.
48
+
49
+ ### `resolver.byIdent(ident)`
50
+
51
+ Looks up fixes by identifier (e.g. "MERIT", "BOSCO"). Multiple fixes can share
52
+ the same identifier in different ICAO regions. Case-insensitive.
53
+ Returns `Fix[]`.
54
+
55
+ ### `resolver.nearest(query)`
56
+
57
+ Finds fixes nearest to a geographic position, sorted by distance ascending.
58
+
59
+ | Property | Type | Description |
60
+ | --------------- | ------------------------ | --------------------------------------------------------------------- |
61
+ | `lat` | number | Latitude in decimal degrees (WGS84) |
62
+ | `lon` | number | Longitude in decimal degrees (WGS84) |
63
+ | `maxDistanceNm` | number | Optional. Maximum distance in nautical miles. Defaults to 30 |
64
+ | `limit` | number | Optional. Maximum number of results. Defaults to 10 |
65
+ | `useCodes` | ReadonlySet\<FixUseCode> | Optional. When provided, only fixes with these use codes are returned |
66
+
67
+ Returns `NearestFixResult[]`, each containing:
68
+
69
+ - `fix` - the matched Fix record
70
+ - `distanceNm` - great-circle distance in nautical miles (rounded to 2 decimal places)
71
+
72
+ ```typescript
73
+ // Find the 5 nearest waypoints within 50 nm
74
+ const nearby = resolver.nearest({
75
+ lat: 40.6413,
76
+ lon: -73.7781,
77
+ maxDistanceNm: 50,
78
+ limit: 5,
79
+ useCodes: new Set(['WP']),
80
+ });
81
+ ```
82
+
83
+ ### `resolver.search(query)`
84
+
85
+ Searches fixes by identifier using case-insensitive substring matching.
86
+ Results are returned in alphabetical order by identifier.
87
+
88
+ | Property | Type | Description |
89
+ | ---------- | ------------------------ | --------------------------------------------------------------------- |
90
+ | `text` | string | Case-insensitive substring to match against fix identifier |
91
+ | `limit` | number | Optional. Maximum number of results. Defaults to 20 |
92
+ | `useCodes` | ReadonlySet\<FixUseCode> | Optional. When provided, only fixes with these use codes are returned |
93
+
94
+ Returns `Fix[]`.
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Pure logic library for querying US fix/waypoint data.
4
+ */
5
+ export { createFixResolver } from './resolver.js';
6
+ export type { FixResolver, FixResolverOptions, NearestFixQuery, NearestFixResult, FixSearchQuery, } from './resolver.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * Pure logic library for querying US fix/waypoint data.
4
+ */
5
+ export { createFixResolver } from './resolver.js';
@@ -0,0 +1,86 @@
1
+ import type { Fix, FixUseCode } from '@squawk/types';
2
+ /**
3
+ * Options for creating a fix resolver.
4
+ */
5
+ export interface FixResolverOptions {
6
+ /** Array of Fix records to index for queries. */
7
+ data: Fix[];
8
+ }
9
+ /**
10
+ * A query to find fixes near a geographic position.
11
+ */
12
+ export interface NearestFixQuery {
13
+ /** Latitude in decimal degrees (WGS84). */
14
+ lat: number;
15
+ /** Longitude in decimal degrees (WGS84). */
16
+ lon: number;
17
+ /** Maximum distance in nautical miles. Defaults to 30. */
18
+ maxDistanceNm?: number;
19
+ /** Maximum number of results to return. Defaults to 10. */
20
+ limit?: number;
21
+ /** Optional set of use codes to include. When omitted, all use codes are included. */
22
+ useCodes?: ReadonlySet<FixUseCode>;
23
+ }
24
+ /**
25
+ * A fix result with distance information from a nearest-fix query.
26
+ */
27
+ export interface NearestFixResult {
28
+ /** The matched fix record. */
29
+ fix: Fix;
30
+ /** Distance in nautical miles from the query position. */
31
+ distanceNm: number;
32
+ }
33
+ /**
34
+ * Options for a text search query against fix identifiers.
35
+ */
36
+ export interface FixSearchQuery {
37
+ /** Case-insensitive substring to match against fix identifier. */
38
+ text: string;
39
+ /** Maximum number of results to return. Defaults to 20. */
40
+ limit?: number;
41
+ /** Optional set of use codes to include. When omitted, all use codes are included. */
42
+ useCodes?: ReadonlySet<FixUseCode>;
43
+ }
44
+ /**
45
+ * A stateless resolver providing fix lookup methods.
46
+ */
47
+ export interface FixResolver {
48
+ /**
49
+ * Looks up fixes by identifier (e.g. "MERIT", "BOSCO").
50
+ * Multiple fixes can share the same identifier in different ICAO regions.
51
+ * Returns an empty array if no match is found.
52
+ */
53
+ byIdent(ident: string): Fix[];
54
+ /**
55
+ * Finds fixes nearest to a geographic position, sorted by distance.
56
+ * Results are filtered by max distance and limited to the requested count.
57
+ */
58
+ nearest(query: NearestFixQuery): NearestFixResult[];
59
+ /**
60
+ * Searches fixes by identifier using case-insensitive substring matching.
61
+ * Results are returned in alphabetical order by identifier.
62
+ */
63
+ search(query: FixSearchQuery): Fix[];
64
+ }
65
+ /**
66
+ * Creates a stateless fix resolver. The resolver accepts an array of
67
+ * Fix records at initialization (typically from `@squawk/fix-data`)
68
+ * and returns an object with methods for looking up fixes by identifier,
69
+ * proximity, or identifier search.
70
+ *
71
+ * The resolver builds internal indexes at creation time for fast lookups
72
+ * by identifier. Proximity and text searches iterate over the full dataset.
73
+ *
74
+ * ```typescript
75
+ * import { usBundledFixes } from '@squawk/fix-data';
76
+ * import { createFixResolver } from '@squawk/fixes';
77
+ *
78
+ * const resolver = createFixResolver({ data: usBundledFixes.records });
79
+ *
80
+ * const merit = resolver.byIdent('MERIT');
81
+ * const nearby = resolver.nearest({ lat: 40.6413, lon: -73.7781 });
82
+ * const results = resolver.search({ text: 'BOS' });
83
+ * ```
84
+ */
85
+ export declare function createFixResolver(options: FixResolverOptions): FixResolver;
86
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAKrD;;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"}
@@ -0,0 +1,86 @@
1
+ import { distance } from '@squawk/units';
2
+ const { greatCircleDistanceNm } = distance;
3
+ /**
4
+ * Default maximum distance in nautical miles for nearest-fix queries.
5
+ */
6
+ const DEFAULT_MAX_DISTANCE_NM = 30;
7
+ /**
8
+ * Default maximum number of results for nearest-fix queries.
9
+ */
10
+ const DEFAULT_NEAREST_LIMIT = 10;
11
+ /**
12
+ * Default maximum number of results for text search queries.
13
+ */
14
+ const DEFAULT_SEARCH_LIMIT = 20;
15
+ /**
16
+ * Creates a stateless fix resolver. The resolver accepts an array of
17
+ * Fix records at initialization (typically from `@squawk/fix-data`)
18
+ * and returns an object with methods for looking up fixes by identifier,
19
+ * proximity, or identifier search.
20
+ *
21
+ * The resolver builds internal indexes at creation time for fast lookups
22
+ * by identifier. Proximity and text searches iterate over the full dataset.
23
+ *
24
+ * ```typescript
25
+ * import { usBundledFixes } from '@squawk/fix-data';
26
+ * import { createFixResolver } from '@squawk/fixes';
27
+ *
28
+ * const resolver = createFixResolver({ data: usBundledFixes.records });
29
+ *
30
+ * const merit = resolver.byIdent('MERIT');
31
+ * const nearby = resolver.nearest({ lat: 40.6413, lon: -73.7781 });
32
+ * const results = resolver.search({ text: 'BOS' });
33
+ * ```
34
+ */
35
+ export function createFixResolver(options) {
36
+ const fixes = options.data;
37
+ const byIdentMap = new Map();
38
+ for (const fix of fixes) {
39
+ const key = fix.identifier.toUpperCase();
40
+ let arr = byIdentMap.get(key);
41
+ if (!arr) {
42
+ arr = [];
43
+ byIdentMap.set(key, arr);
44
+ }
45
+ arr.push(fix);
46
+ }
47
+ return {
48
+ byIdent(ident) {
49
+ return byIdentMap.get(ident.toUpperCase()) ?? [];
50
+ },
51
+ nearest(query) {
52
+ const maxDist = query.maxDistanceNm ?? DEFAULT_MAX_DISTANCE_NM;
53
+ const limit = query.limit ?? DEFAULT_NEAREST_LIMIT;
54
+ const results = [];
55
+ for (const fix of fixes) {
56
+ if (query.useCodes && !query.useCodes.has(fix.useCode)) {
57
+ continue;
58
+ }
59
+ const dist = greatCircleDistanceNm(query.lat, query.lon, fix.lat, fix.lon);
60
+ if (dist <= maxDist) {
61
+ results.push({ fix, distanceNm: Math.round(dist * 100) / 100 });
62
+ }
63
+ }
64
+ results.sort((a, b) => a.distanceNm - b.distanceNm);
65
+ return results.slice(0, limit);
66
+ },
67
+ search(query) {
68
+ const limit = query.limit ?? DEFAULT_SEARCH_LIMIT;
69
+ const needle = query.text.toUpperCase();
70
+ if (needle.length === 0) {
71
+ return [];
72
+ }
73
+ const results = [];
74
+ for (const fix of fixes) {
75
+ if (query.useCodes && !query.useCodes.has(fix.useCode)) {
76
+ continue;
77
+ }
78
+ if (fix.identifier.toUpperCase().includes(needle)) {
79
+ results.push(fix);
80
+ }
81
+ }
82
+ results.sort((a, b) => a.identifier.localeCompare(b.identifier));
83
+ return results.slice(0, limit);
84
+ },
85
+ };
86
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@squawk/fixes",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Fix/waypoint queries by identifier, location, or name search",
6
+ "author": "Neil Cochran",
7
+ "license": "MIT",
8
+ "homepage": "https://github.com/neilcochran/squawk",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/neilcochran/squawk.git",
12
+ "directory": "packages/fixes"
13
+ },
14
+ "engines": {
15
+ "node": ">=22"
16
+ },
17
+ "typedocMain": "src/index.ts",
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "!dist/**/*.spec.*"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "test": "node --test 'dist/**/*.spec.js'",
33
+ "lint": "tsc --noEmit && eslint src"
34
+ },
35
+ "dependencies": {
36
+ "@squawk/types": "*",
37
+ "@squawk/units": "*"
38
+ },
39
+ "devDependencies": {
40
+ "@squawk/fix-data": "*",
41
+ "@types/node": "^25.5.2"
42
+ },
43
+ "keywords": [
44
+ "aviation",
45
+ "typescript",
46
+ "fix",
47
+ "waypoint",
48
+ "intersection",
49
+ "faa",
50
+ "nasr"
51
+ ],
52
+ "publishConfig": {
53
+ "access": "public"
54
+ }
55
+ }