Sign In

@squawk/procedures

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@squawk/procedures - npm Package Compare versions

Comparing version
0.2.4
to
0.4.0
+2
-1
dist/index.d.ts
/**
* @packageDocumentation
* Pure logic library for querying US instrument procedure data (SIDs and STARs).
* Pure logic library for querying US instrument procedure data (SIDs,
* STARs, and Instrument Approach Procedures) sourced from FAA CIFP.
*/

@@ -5,0 +6,0 @@ export { createProcedureResolver } from './resolver.js';

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,eAAe,CAAC"}
/**
* @packageDocumentation
* Pure logic library for querying US instrument procedure data (SIDs and STARs).
* Pure logic library for querying US instrument procedure data (SIDs,
* STARs, and Instrument Approach Procedures) sourced from FAA CIFP.
*/
export { createProcedureResolver } from './resolver.js';

@@ -1,2 +0,2 @@

import type { Procedure, ProcedureType, ProcedureWaypoint } from '@squawk/types';
import type { ApproachType, Procedure, ProcedureLeg, ProcedureType } from '@squawk/types';
/**

@@ -6,7 +6,7 @@ * Options for creating a procedure resolver.

export interface ProcedureResolverOptions {
/** Array of Procedure records to index for queries. */
/** Array of {@link Procedure} records to index for queries. */
data: Procedure[];
}
/**
* Result of expanding a procedure into a waypoint sequence.
* Result of expanding a procedure into an ordered leg sequence.
*/

@@ -16,58 +16,78 @@ export interface ProcedureExpansionResult {

procedure: Procedure;
/** Ordered sequence of waypoints for the selected route. */
waypoints: ProcedureWaypoint[];
/** Ordered legs for the expansion (common route + optional transition). */
legs: ProcedureLeg[];
}
/**
* Options for a text search query against procedure names and computer codes.
* Options for a text search query against procedure names and identifiers.
*/
export interface ProcedureSearchQuery {
/** Case-insensitive substring to match against procedure name or computer code. */
/** Case-insensitive substring matched against name and identifier. */
text: string;
/** Maximum number of results to return. Defaults to 20. */
limit?: number;
/** Optional procedure type to filter by (SID or STAR). */
/** Optional procedure type to filter by. */
type?: ProcedureType;
/** Optional approach type filter (applied only when matching IAPs). */
approachType?: ApproachType;
}
/**
* A stateless resolver providing instrument procedure lookup, filtering, and
* expansion methods.
* A stateless resolver providing instrument procedure lookup, filtering,
* and expansion methods against a pre-indexed dataset.
*/
export interface ProcedureResolver {
/**
* Looks up a procedure by its FAA computer code (e.g. "AALLE4", "ACCRA5").
* Case-insensitive. Returns undefined if no match is found.
* Looks up every procedure matching a CIFP identifier. The identifier
* alone is not globally unique in CIFP data - the same name (for
* example `SARDI1` or `I04L`) is published separately for every
* adapted airport. This method returns all of them.
*/
byName(computerCode: string): Procedure | undefined;
byIdentifier(identifier: string): Procedure[];
/**
* Finds all procedures associated with a given airport identifier.
* Case-insensitive. Returns an empty array if no match is found.
* Looks up a single procedure by (airport, identifier). Returns
* `undefined` when the airport does not adapt the identifier.
*/
byAirportAndIdentifier(airportId: string, identifier: string): Procedure | undefined;
/**
* Returns every procedure adapted at the given airport (SIDs, STARs,
* and IAPs).
*/
byAirport(airportId: string): Procedure[];
/**
* Returns all procedures of a given type (SID or STAR).
* Returns every procedure at an airport that serves a specific runway.
*
* - For IAPs this matches the `runway` field directly.
* - For SIDs and STARs this matches procedures that publish a runway transition named `RW<runway>` (e.g. `RW04L`).
*/
byAirportAndRunway(airportId: string, runway: string): Procedure[];
/**
* Returns every procedure of a given type.
*/
byType(type: ProcedureType): Procedure[];
/**
* Expands a procedure into an ordered waypoint sequence.
* Returns every IAP of a given approach classification (ILS, RNAV, etc.).
*/
byApproachType(approachType: ApproachType): Procedure[];
/**
* Expands a procedure into an ordered leg sequence. When `transitionName`
* is omitted the expansion is the procedure's first common route. When
* `transitionName` is provided, the named transition's legs are merged
* with the common route in flying order:
*
* When called without a transition name, returns the first common route.
* - SID, enroute exit transition - common route first, then transition.
* - SID, runway transition (`RW*` name) - transition first, then common route.
* - STAR, enroute entry transition - transition first, then common route.
* - STAR, runway transition (`RW*` name) - common route first, then transition.
* - IAP, approach transition - transition first, then common route (final approach segment).
*
* When called with a transition name, the transition's waypoints are
* merged with the first common route's waypoints in flying order:
* - For STARs the transition feeds into the common route, so the
* transition waypoints come first followed by the common route.
* - For SIDs the common route departs the airport before joining the
* transition, so the common route comes first followed by the
* transition.
* The connecting fix between transition and common route is
* deduplicated when both segments reference it.
*
* In both cases the connecting fix where the transition meets the common
* route is deduplicated when present.
*
* Returns undefined if the procedure or transition is not found.
* Returns `undefined` when the procedure, airport, or transition is
* not found.
*/
expand(computerCode: string, transitionName?: string): ProcedureExpansionResult | undefined;
expand(airportId: string, identifier: string, transitionName?: string): ProcedureExpansionResult | undefined;
/**
* Searches procedures by name or computer code using case-insensitive
* substring matching. Results are returned in alphabetical order by
* computer code.
* Searches procedures by name or identifier using case-insensitive
* substring matching. Results are returned sorted by airport then
* identifier.
*/

@@ -77,9 +97,9 @@ search(query: ProcedureSearchQuery): Procedure[];

/**
* Creates a stateless procedure resolver. The resolver accepts an array of
* Procedure records at initialization (typically from `@squawk/procedure-data`)
* and returns an object with methods for looking up procedures by computer code,
* airport, type, expanding route segments, and searching by name.
* Creates a stateless procedure resolver. The resolver accepts an array
* of {@link Procedure} records at initialization (typically from
* `@squawk/procedure-data`) and returns an object with query methods.
*
* The resolver builds internal indexes at creation time for fast lookups
* by computer code and by airport identifier.
* The resolver builds internal indexes at creation time for fast lookup
* by identifier, by airport, by (airport, identifier), by type, and by
* approach type.
*

@@ -92,7 +112,8 @@ * ```typescript

*
* const aalle = resolver.byName('AALLE4');
* const denProcedures = resolver.byAirport('DEN');
* const stars = resolver.byType('STAR');
* const expanded = resolver.expand('AALLE4', 'BBOTL');
* const results = resolver.search({ text: 'AALLE' });
* const allSardi = resolver.byIdentifier('SARDI1');
* const denAalle = resolver.byAirportAndIdentifier('KDEN', 'AALLE4');
* const jfkApproaches = resolver.byAirport('KJFK').filter((p) => p.type === 'IAP');
* const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L');
* const allIls = resolver.byApproachType('ILS');
* const expanded = resolver.expand('KDEN', 'AALLE4', 'BBOTL');
* ```

@@ -99,0 +120,0 @@ */

@@ -1,1 +0,1 @@

{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EAElB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,4DAA4D;IAC5D,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAEpD;;;OAGG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE1C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC;IAEzC;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAAC;IAE5F;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC;CAClD;AAOD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CA0G5F"}
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EAET,YAAY,EAEZ,aAAa,EACd,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+DAA+D;IAC/D,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,2EAA2E;IAC3E,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE9C;;;OAGG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAErF;;;OAGG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE1C;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAEnE;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC;IAEzC;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,CAAC;IAExD;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,wBAAwB,GAAG,SAAS,CAAC;IAExC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC;CAClD;AAOD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAkJ5F"}

@@ -6,9 +6,9 @@ /**

/**
* Creates a stateless procedure resolver. The resolver accepts an array of
* Procedure records at initialization (typically from `@squawk/procedure-data`)
* and returns an object with methods for looking up procedures by computer code,
* airport, type, expanding route segments, and searching by name.
* Creates a stateless procedure resolver. The resolver accepts an array
* of {@link Procedure} records at initialization (typically from
* `@squawk/procedure-data`) and returns an object with query methods.
*
* The resolver builds internal indexes at creation time for fast lookups
* by computer code and by airport identifier.
* The resolver builds internal indexes at creation time for fast lookup
* by identifier, by airport, by (airport, identifier), by type, and by
* approach type.
*

@@ -21,7 +21,8 @@ * ```typescript

*
* const aalle = resolver.byName('AALLE4');
* const denProcedures = resolver.byAirport('DEN');
* const stars = resolver.byType('STAR');
* const expanded = resolver.expand('AALLE4', 'BBOTL');
* const results = resolver.search({ text: 'AALLE' });
* const allSardi = resolver.byIdentifier('SARDI1');
* const denAalle = resolver.byAirportAndIdentifier('KDEN', 'AALLE4');
* const jfkApproaches = resolver.byAirport('KJFK').filter((p) => p.type === 'IAP');
* const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L');
* const allIls = resolver.byApproachType('ILS');
* const expanded = resolver.expand('KDEN', 'AALLE4', 'BBOTL');
* ```

@@ -31,16 +32,16 @@ */

const procedures = options.data;
const byCodeMap = new Map();
const byIdentifierMap = new Map();
const byAirportMap = new Map();
const byAirportIdentifierMap = new Map();
const sidList = [];
const starList = [];
const iapList = [];
const byApproachTypeMap = new Map();
for (const proc of procedures) {
byCodeMap.set(proc.computerCode.toUpperCase(), proc);
const identKey = proc.identifier.toUpperCase();
appendToMap(byIdentifierMap, identKey, proc);
for (const airport of proc.airports) {
const key = airport.toUpperCase();
let arr = byAirportMap.get(key);
if (!arr) {
arr = [];
byAirportMap.set(key, arr);
}
arr.push(proc);
const airportKey = airport.toUpperCase();
appendToMap(byAirportMap, airportKey, proc);
byAirportIdentifierMap.set(`${airportKey}::${identKey}`, proc);
}

@@ -50,48 +51,71 @@ if (proc.type === 'SID') {

}
else {
else if (proc.type === 'STAR') {
starList.push(proc);
}
else {
iapList.push(proc);
}
if (proc.approachType !== undefined) {
appendToMap(byApproachTypeMap, proc.approachType, proc);
}
}
return {
byName(computerCode) {
return byCodeMap.get(computerCode.toUpperCase());
byIdentifier(identifier) {
return byIdentifierMap.get(identifier.toUpperCase()) ?? [];
},
byAirportAndIdentifier(airportId, identifier) {
return byAirportIdentifierMap.get(`${airportId.toUpperCase()}::${identifier.toUpperCase()}`);
},
byAirport(airportId) {
return byAirportMap.get(airportId.toUpperCase()) ?? [];
},
byAirportAndRunway(airportId, runway) {
const candidates = byAirportMap.get(airportId.toUpperCase()) ?? [];
const runwayUpper = runway.toUpperCase();
const runwayTransitionName = `RW${runwayUpper}`;
return candidates.filter((proc) => {
if (proc.runway !== undefined && proc.runway.toUpperCase() === runwayUpper) {
return true;
}
for (const transition of proc.transitions) {
if (transition.name.toUpperCase() === runwayTransitionName) {
return true;
}
}
return false;
});
},
byType(type) {
return type === 'SID' ? sidList : starList;
if (type === 'SID') {
return sidList;
}
if (type === 'STAR') {
return starList;
}
return iapList;
},
expand(computerCode, transitionName) {
const proc = byCodeMap.get(computerCode.toUpperCase());
if (!proc) {
byApproachType(approachType) {
return byApproachTypeMap.get(approachType) ?? [];
},
expand(airportId, identifier, transitionName) {
const proc = byAirportIdentifierMap.get(`${airportId.toUpperCase()}::${identifier.toUpperCase()}`);
if (proc === undefined) {
return undefined;
}
const commonRoute = proc.commonRoutes[0];
if (transitionName === undefined) {
const firstRoute = proc.commonRoutes[0];
if (!firstRoute) {
if (commonRoute === undefined) {
return undefined;
}
return {
procedure: proc,
waypoints: firstRoute.waypoints,
};
return { procedure: proc, legs: commonRoute.legs };
}
const upperTransition = transitionName.toUpperCase();
const transition = proc.transitions.find((t) => t.name.toUpperCase() === upperTransition);
if (!transition) {
const transition = proc.transitions.find((t) => t.name.toUpperCase() === transitionName.toUpperCase());
if (transition === undefined) {
return undefined;
}
const firstRoute = proc.commonRoutes[0];
if (!firstRoute) {
return {
procedure: proc,
waypoints: transition.waypoints,
};
if (commonRoute === undefined) {
return { procedure: proc, legs: transition.legs };
}
const waypoints = mergeTransitionAndRoute(transition.waypoints, firstRoute, proc.type);
return {
procedure: proc,
waypoints,
};
const legs = mergeTransitionAndRoute(transition, commonRoute, proc.type);
return { procedure: proc, legs };
},

@@ -106,6 +130,9 @@ search(query) {

for (const proc of procedures) {
if (query.type && proc.type !== query.type) {
if (query.type !== undefined && proc.type !== query.type) {
continue;
}
if (proc.computerCode.toUpperCase().includes(needle) ||
if (query.approachType !== undefined && proc.approachType !== query.approachType) {
continue;
}
if (proc.identifier.toUpperCase().includes(needle) ||
proc.name.toUpperCase().includes(needle)) {

@@ -115,3 +142,9 @@ results.push(proc);

}
results.sort((a, b) => a.computerCode.localeCompare(b.computerCode));
results.sort((a, b) => {
const airportDiff = (a.airports[0] ?? '').localeCompare(b.airports[0] ?? '');
if (airportDiff !== 0) {
return airportDiff;
}
return a.identifier.localeCompare(b.identifier);
});
return results.slice(0, limit);

@@ -122,29 +155,67 @@ },

/**
* Merges a transition's waypoints with a common route's waypoints in the
* order aircraft fly the procedure. SIDs depart along the common route and
* then continue onto the transition; STARs fly the transition into the
* common route. The connecting fix where the two segments meet is
* deduplicated when present.
* Appends a procedure to a keyed array within a map, lazily creating
* the array on first insertion.
*/
function mergeTransitionAndRoute(transitionWaypoints, route, procedureType) {
if (transitionWaypoints.length === 0) {
return route.waypoints;
function appendToMap(map, key, procedure) {
let arr = map.get(key);
if (arr === undefined) {
arr = [];
map.set(key, arr);
}
if (route.waypoints.length === 0) {
return transitionWaypoints;
arr.push(procedure);
}
/**
* Merges a transition's legs with a common route's legs in the order
* an aircraft flies the procedure. The direction depends on both the
* procedure type and whether the transition is a runway transition
* (name prefixed with `RW`).
*
* - SID + enroute transition - common route first, transition last.
* - SID + runway transition - transition first, common route last.
* - STAR + enroute transition - transition first, common route last.
* - STAR + runway transition - common route first, transition last.
* - IAP + approach transition - transition first, common route last.
*
* The connecting fix between the two segments is deduplicated when
* both segments reference it with the same identifier.
*/
function mergeTransitionAndRoute(transition, route, procedureType) {
if (transition.legs.length === 0) {
return route.legs;
}
if (route.legs.length === 0) {
return transition.legs;
}
const isRunwayTransition = transition.name.toUpperCase().startsWith('RW');
let transitionFirst;
if (procedureType === 'SID') {
const lastRoute = route.waypoints[route.waypoints.length - 1];
const firstTransition = transitionWaypoints[0];
if (lastRoute.fixIdentifier.toUpperCase() === firstTransition.fixIdentifier.toUpperCase()) {
return [...route.waypoints, ...transitionWaypoints.slice(1)];
}
return [...route.waypoints, ...transitionWaypoints];
transitionFirst = isRunwayTransition;
}
const lastTransition = transitionWaypoints[transitionWaypoints.length - 1];
const firstRoute = route.waypoints[0];
if (lastTransition.fixIdentifier.toUpperCase() === firstRoute.fixIdentifier.toUpperCase()) {
return [...transitionWaypoints, ...route.waypoints.slice(1)];
else if (procedureType === 'STAR') {
transitionFirst = !isRunwayTransition;
}
return [...transitionWaypoints, ...route.waypoints];
else {
transitionFirst = true;
}
if (transitionFirst) {
return joinSegments(transition.legs, route.legs);
}
return joinSegments(route.legs, transition.legs);
}
/**
* Joins two leg segments, deduplicating the connecting fix when the
* last leg of `first` and the first leg of `second` terminate at the
* same fix identifier.
*/
function joinSegments(first, second) {
const last = first[first.length - 1];
const next = second[0];
if (last !== undefined &&
next !== undefined &&
last.fixIdentifier !== undefined &&
next.fixIdentifier !== undefined &&
last.fixIdentifier.toUpperCase() === next.fixIdentifier.toUpperCase()) {
return [...first, ...second.slice(1)];
}
return [...first, ...second];
}
{
"name": "@squawk/procedures",
"version": "0.2.4",
"version": "0.4.0",
"type": "module",
"description": "Instrument procedure lookup and expansion for SIDs and STARs",
"description": "Instrument procedure lookup and expansion for SIDs, STARs, and Instrument Approach Procedures (IAPs)",
"author": "Neil Cochran",

@@ -37,6 +37,6 @@ "license": "MIT",

"dependencies": {
"@squawk/types": "^0.3.1"
"@squawk/types": "^0.5.0"
},
"devDependencies": {
"@squawk/procedure-data": "^0.3.3",
"@squawk/procedure-data": "^0.5.0",
"@types/node": "^25.6.0"

@@ -49,2 +49,4 @@ },

"star",
"iap",
"approach",
"instrument-procedure",

@@ -54,3 +56,4 @@ "departure",

"faa",
"nasr"
"cifp",
"arinc-424"
],

@@ -57,0 +60,0 @@ "publishConfig": {

+81
-42

@@ -5,7 +5,9 @@ <h1><img src="../../assets/squawk-logo.svg" alt="squawk logo" width="48" height="48" style="vertical-align: middle">&nbsp; @squawk/procedures</h1>

Pure logic library for querying US instrument procedure data. Look up SIDs
and STARs by computer code, find procedures by airport, filter by type, expand
route segments with transitions, or search by name. Contains no bundled data -
accepts an array of Procedure records at initialization. For zero-config use,
pair with `@squawk/procedure-data`.
Pure logic library for querying US instrument procedure data sourced from FAA
CIFP (Coded Instrument Flight Procedures). Covers SIDs, STARs, and Instrument
Approach Procedures (IAPs) in a unified ARINC 424 leg model. Look up by
identifier, by airport, by runway, by approach type; expand a procedure into
an ordered leg sequence; or search by name. Contains no bundled data - accepts
an array of `Procedure` records at initialization. For zero-config use, pair
with `@squawk/procedure-data`.

@@ -22,24 +24,32 @@ Part of the [@squawk](https://www.npmjs.com/org/squawk) aviation library suite. See all packages on npm.

// Look up by computer code
const aalle = resolver.byName('AALLE4');
// Look up every adaptation of an identifier across airports
const allSardi = resolver.byIdentifier('SARDI1');
// Find all procedures for an airport
const denProcedures = resolver.byAirport('DEN');
// Resolve a specific procedure at an airport
const aalleAtDen = resolver.byAirportAndIdentifier('KDEN', 'AALLE4');
const ilsAtJfk = resolver.byAirportAndIdentifier('KJFK', 'I04L');
// Get all STARs
const stars = resolver.byType('STAR');
// Find every procedure for an airport
const jfkProcedures = resolver.byAirport('KJFK');
// Expand a procedure (common route only)
const route = resolver.expand('AALLE4');
if (route) {
for (const wp of route.waypoints) {
console.log(wp.fixIdentifier, wp.lat, wp.lon);
// Find procedures that serve a specific runway
const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L');
// Filter by type or approach classification
const allStars = resolver.byType('STAR');
const allIls = resolver.byApproachType('ILS');
// Expand a procedure into an ordered leg sequence (common route only)
const expansion = resolver.expand('KDEN', 'AALLE4');
if (expansion) {
for (const leg of expansion.legs) {
console.log(leg.pathTerminator, leg.fixIdentifier ?? '(no fix)');
}
}
// Expand with a named transition
const withTransition = resolver.expand('AALLE4', 'BBOTL');
// Expand with a named transition (transition + common route merged in flying order)
const withTransition = resolver.expand('KDEN', 'AALLE4', 'BBOTL');
// Search by name or code
const results = resolver.search({ text: 'AALLE' });
// Search by name or identifier
const results = resolver.search({ text: 'AALLE', type: 'STAR' });
```

@@ -59,49 +69,78 @@

Creates a resolver object from an array of Procedure records.
Creates a resolver object from an array of `Procedure` records.
**Parameters:**
- `options.data` - an array of `Procedure` objects (from `@squawk/types`)
- `options.data` - an array of `Procedure` objects (from `@squawk/types`).
**Returns:** `ProcedureResolver` - an object with the lookup methods described below.
### `resolver.byName(computerCode)`
### `resolver.byIdentifier(identifier)`
Looks up a procedure by its FAA computer code (e.g. "AALLE4", "ACCRA5").
Case-insensitive. Returns `Procedure | undefined`.
Looks up every procedure matching a CIFP identifier (case-insensitive). CIFP
identifiers are not globally unique - the same identifier (for example
`SARDI1` or `I04L`) is published separately for each adapted airport, so this
returns all matches. Returns `Procedure[]`.
### `resolver.byAirportAndIdentifier(airportId, identifier)`
Resolves a single procedure by (airport, identifier). Case-insensitive for
both arguments. Returns `Procedure | undefined`.
### `resolver.byAirport(airportId)`
Finds all procedures associated with a given airport identifier.
Returns every procedure (SID, STAR, or IAP) adapted at the given airport.
Case-insensitive. Returns `Procedure[]`.
### `resolver.byAirportAndRunway(airportId, runway)`
Returns procedures at an airport that serve a specific runway. For IAPs, the
match is on the `runway` field directly. For SIDs and STARs, the match is on a
runway transition named `RW<runway>` (for example `RW04L`). Case-insensitive.
Returns `Procedure[]`.
### `resolver.byType(type)`
Returns all procedures of a given type. Pass `'SID'` or `'STAR'`.
Returns every procedure of a given type. Pass `'SID'`, `'STAR'`, or `'IAP'`.
Returns `Procedure[]`.
### `resolver.expand(computerCode, transitionName?)`
### `resolver.byApproachType(approachType)`
Expands a procedure into an ordered waypoint sequence.
Returns every IAP of a given approach classification (`'ILS'`, `'LOC'`,
`'LOC_BC'`, `'RNAV'`, `'RNAV_RNP'`, `'VOR'`, `'VOR_DME'`, `'NDB'`, `'NDB_DME'`,
`'TACAN'`, `'GLS'`, `'IGS'`, `'LDA'`, `'SDF'`, `'GPS'`, `'FMS'`, `'MLS'`).
Returns `Procedure[]`.
- Without a transition: returns the first common route's waypoints
- With a transition: returns the transition waypoints merged with the first
common route, deduplicating the connecting fix
### `resolver.expand(airportId, identifier, transitionName?)`
Returns `ProcedureExpansionResult | undefined`. The result contains:
Expands a procedure into an ordered leg sequence. Without a transition name,
returns the procedure's first common route. With a transition name, merges the
named transition's legs with the common route in flying order:
- `procedure` - the full Procedure record
- `waypoints` - the ordered waypoint sequence
- **SID + enroute exit transition** - common route first, then transition.
- **SID + runway transition** (`RW*` name) - transition first, then common route.
- **STAR + enroute entry transition** - transition first, then common route.
- **STAR + runway transition** - common route first, then transition.
- **IAP + approach transition** - transition first, then final approach segment.
The connecting fix between transition and common route is deduplicated when
both segments reference it.
Returns `ProcedureExpansionResult | undefined`, containing:
- `procedure` - the full `Procedure` record.
- `legs` - the ordered `ProcedureLeg` sequence.
### `resolver.search(query)`
Searches procedures by name or computer code using case-insensitive substring
matching. Results are returned in alphabetical order by computer code.
Searches procedures by name or identifier using case-insensitive substring
matching. Results are sorted by airport then identifier.
| Property | Type | Description |
| -------- | ------------- | ----------------------------------------------------------------- |
| `text` | string | Case-insensitive substring to match against name or computer code |
| `limit` | number | Optional. Maximum number of results. Defaults to 20 |
| `type` | ProcedureType | Optional. When provided, only this procedure type is returned |
| Property | Type | Description |
| -------------- | ------------- | -------------------------------------------------------------- |
| `text` | string | Case-insensitive substring to match against name or identifier |
| `limit` | number | Optional. Maximum number of results. Defaults to 20 |
| `type` | ProcedureType | Optional. Restrict to `'SID'`, `'STAR'`, or `'IAP'` only |
| `approachType` | ApproachType | Optional. Restrict to IAPs of a given approach classification |
Returns `Procedure[]`.