diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
index a9aea63..ba89af3 100644
--- a/.github/workflows/python-package.yml
+++ b/.github/workflows/python-package.yml
@@ -34,11 +34,11 @@ jobs:
pip install -e "./python[tests]"
pip install flake8 mypy build pytest-cov pip-audit
- name: Lint
- run: cd python && python -m flake8 FlightRadar24 tests
+ run: cd python && python -m flake8 FlightRadarAPI FlightRadar24 tests
- name: Type check
- run: cd python && python -m mypy FlightRadar24 --ignore-missing-imports
+ run: cd python && python -m mypy FlightRadarAPI --ignore-missing-imports
- name: Offline tests (PR gate)
- run: cd python && pytest -m "not integration" --cov=FlightRadar24 --cov-report=term --cov-report=xml -v
+ run: cd python && pytest -m "not integration" --cov=FlightRadarAPI --cov-report=term --cov-report=xml -v
- name: Integration tests (live FR24)
if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
uses: nick-fields/retry@v3
@@ -58,4 +58,4 @@ jobs:
run: |
python -m build ./python
pip install python/dist/*.whl --force-reinstall
- python -c "from FlightRadar24 import FlightRadar24API; api = FlightRadar24API(); print('Install OK')"
+ python -c "from FlightRadarAPI import FlightRadar24API; api = FlightRadar24API(); print('Install OK')"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..63c6d46
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,65 @@
+# Contributing to FlightRadarAPI
+
+Thanks for your interest. This repo ships two SDKs in parallel — Python and
+Node.js — that must stay behavior-aligned, so most non-trivial changes touch
+both sides.
+
+## Development setup
+
+### Python
+```bash
+cd python
+make dev-setup # creates venv, installs package + test extras + tooling
+source venv/bin/activate
+make test # runs offline + integration
+make lint # flake8
+make type-check # mypy
+```
+
+### Node.js
+```bash
+cd nodejs
+make install
+make test # mocha (all tiers)
+make lint # eslint
+make test-types # tsd
+```
+
+## Keeping Python and Node aligned
+
+When you change behavior, change it in both SDKs in the same PR unless there
+is a documented reason not to. Common targets that must stay in sync:
+
+- Error taxonomy (`AirportNotFoundError`, `LoginError`, `CloudflareError`,
+ `FlightRadarError`).
+- `RetryPolicy` semantics (which exceptions are transient, backoff math).
+- Cloudflare detection rules.
+- The public surface — `FlightRadar24API` methods, the `Countries` enum,
+ `FlightTrackerConfig` fields, and the `Entity` / `Airport` / `Flight`
+ attributes consumers depend on.
+
+## Style
+
+- Python: flake8 + mypy.
+- Node: eslint + tsd.
+- Comments must explain **why**, not **what**. The codebase has a few exemplars in
+ `request.py`/`request.js` — read those before adding new comments.
+
+## Commits and PRs
+
+- Use a descriptive title with a conventional-commits prefix (`fix:`, `feat:`,
+ `refactor:`, `docs:`, `ci:`, `test:`).
+- For new endpoints or behavior tweaks, add a regression test alongside.
+
+## Releases
+
+Before publishing a new release, the version **must be bumped**. The version lives in two places:
+
+- `python/FlightRadarAPI/__init__.py` (`__version__`)
+- `nodejs/package.json` (`version`)
+
+## Reporting bugs and asking questions
+
+- Bugs: open a GitHub issue with the bug report template.
+- Security: see [`SECURITY.md`](SECURITY.md). **Do not** report via GitHub
+ issues.
diff --git a/README.md b/README.md
index bb11631..887612c 100644
--- a/README.md
+++ b/README.md
@@ -25,4 +25,9 @@ npm install flightradarapi
```
## Documentation
-Explore the documentation of FlightRadarAPI package, for Python or NodeJS, through [this site](https://JeanExtreme002.github.io/FlightRadarAPI/).
+Explore the docs of FlightRadarAPI package, for Python or NodeJS, through [FlightRadarAPI Documentation](https://JeanExtreme002.github.io/FlightRadarAPI/) page.
+
+## Project resources
+**Contributing**: [`CONTRIBUTING.md`](CONTRIBUTING.md)
+**Security policy**: [`SECURITY.md`](SECURITY.md)
+
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..9dc913c
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+## Supported Versions
+
+Security fixes are applied to the latest minor release on PyPI and npm. Older
+versions do not receive backports.
+
+## Reporting a Vulnerability
+
+**Do not open a public GitHub issue for security reports.**
+
+Please email the maintainer with:
+
+- A description of the issue and the affected component (Python SDK, Node SDK,
+ CI, or documentation).
+- The minimum reproducer you have (a short script is ideal).
+- The package version (`pip show FlightRadarAPI` / `npm ls flightradarapi`).
+- The impact you believe the issue has.
+
+## Scope
+
+This project is an **unofficial SDK** that consumes endpoints from
+flightradar24. The following are explicitly **out of scope** for security
+reports:
+
+- The FR24 site or upstream API itself — contact FR24 directly.
+- TLS impersonation behavior (intentional; see `request.py` / `request.js`).
+- The fact that the SDK can be blocked by Cloudflare under heavy use.
+- Anything that requires the user to feed the SDK adversarial input *to their
+ own credentials or filesystem*.
+
+## In Scope
+
+- Code execution, injection, or filesystem traversal triggered by a normal
+ call surface.
+- Credential leakage in logs, error messages, or exception payloads.
+- Known-vulnerable transitive dependencies that the SDK actually exercises.
+- Improper validation that turns a remote response into local code paths.
diff --git a/docs/python.md b/docs/python.md
index f345002..e7a3fc7 100644
--- a/docs/python.md
+++ b/docs/python.md
@@ -16,7 +16,7 @@ pip install FlightRadarAPI
Start by importing the `FlightRadar24API` class and creating an instance of it:
```python
-from FlightRadar24 import FlightRadar24API
+from FlightRadarAPI import FlightRadar24API
fr_api = FlightRadar24API()
```
diff --git a/nodejs/FlightRadar24/api.js b/nodejs/FlightRadarAPI/api.js
similarity index 91%
rename from nodejs/FlightRadar24/api.js
rename to nodejs/FlightRadarAPI/api.js
index d1676d2..3a27623 100644
--- a/nodejs/FlightRadar24/api.js
+++ b/nodejs/FlightRadarAPI/api.js
@@ -108,7 +108,7 @@ class FlightRadar24API {
*/
async getAirport(code, details = false) {
if (code.length < 3 || code.length > 4) {
- throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport.");
+ throw new TypeError("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport.");
}
if (details) {
@@ -138,7 +138,7 @@ class FlightRadar24API {
*/
async getAirportDetails(code, flightLimit = 100, page = 1) {
if (code.length < 3 || code.length > 4) {
- throw new Error("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport.");
+ throw new TypeError("The code '" + code + "' is invalid. It must be the IATA or ICAO of the airport.");
}
const params = { "format": "json", "code": code, "limit": flightLimit, "page": page };
@@ -159,7 +159,7 @@ class FlightRadar24API {
const limit = errors?.["limit"];
if (limit !== undefined) {
- throw new Error(limit["notBetween"]);
+ throw new RangeError(limit["notBetween"]);
}
throw new AirportNotFoundError("Could not find an airport by the code '" + code + "'.", errors);
}
@@ -198,9 +198,13 @@ class FlightRadar24API {
*/
async getAirports(countries) {
const airports = [];
+ // Use stateless requests for the fan-out so per-country `Set-Cookie`
+ // responses do not race onto the shared session jar.
await mapConcurrent(countries, this.maxWorkers, async (countryName) => {
const countryHref = Core.airportsDataUrl + "/" + countryName;
- const { content } = await this.__client.request(countryHref, { headers: Core.htmlHeaders, timeout: this.timeout });
+ const { content } = await this.__client.requestStandalone(
+ countryHref, { headers: Core.htmlHeaders, timeout: this.timeout },
+ );
airports.push(...parseAirportsHtml(content, countryHref));
});
return airports;
@@ -250,30 +254,38 @@ class FlightRadar24API {
const lon = radians(longitude);
const approxEarthRadius = 6371;
+
+ // Distance from the centre to a corner of the bounding square.
const hypotenuseDistance = Math.sqrt(2 * (Math.pow(halfSideInKm, 2)));
+ // Diagonal bearings: 225° (SW corner, min lat/lon) and 45° (NE corner, max lat/lon).
+ const bearingSw = radians(225);
+ const bearingNe = radians(45);
+
+ // Destination-point formula along the SW bearing → south-west corner.
const latMin = Math.asin(
Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) +
Math.cos(lat) *
Math.sin(hypotenuseDistance / approxEarthRadius) *
- Math.cos(225 * (Math.PI / 180)),
+ Math.cos(bearingSw),
);
const lonMin = lon + Math.atan2(
- Math.sin(225 * (Math.PI / 180)) *
+ Math.sin(bearingSw) *
Math.sin(hypotenuseDistance / approxEarthRadius) *
Math.cos(lat),
Math.cos(hypotenuseDistance / approxEarthRadius) -
Math.sin(lat) * Math.sin(latMin),
);
+ // Same formula along the NE bearing → north-east corner.
const latMax = Math.asin(
Math.sin(lat) * Math.cos(hypotenuseDistance / approxEarthRadius) +
Math.cos(lat) *
Math.sin(hypotenuseDistance / approxEarthRadius) *
- Math.cos(45 * (Math.PI / 180)),
+ Math.cos(bearingNe),
);
const lonMax = lon + Math.atan2(
- Math.sin(45 * (Math.PI / 180)) *
+ Math.sin(bearingNe) *
Math.sin(hypotenuseDistance / approxEarthRadius) *
Math.cos(lat),
Math.cos(hypotenuseDistance / approxEarthRadius) -
@@ -296,7 +308,8 @@ class FlightRadar24API {
* @return {Promise<[object, string] | null>}
*/
async getCountryFlag(country) {
- const flagUrl = Core.countryFlagUrl(country.toLowerCase().replaceAll(" ", "-"));
+ const slug = country.toLowerCase().replaceAll(" ", "-");
+ const flagUrl = Core.countryFlagUrl(slug);
const headers = { ...Core.imageHeaders };
delete headers["origin"];
@@ -321,7 +334,9 @@ class FlightRadar24API {
* @return {Promise