mirror of
https://github.com/acamarata/qibla.git
synced 2026-07-03 12:20:40 +00:00
Compare commits
No commits in common. "main" and "v1.1.1" have entirely different histories.
38 changed files with 1357 additions and 2061 deletions
57
.claude/AGENTS.md
Normal file
57
.claude/AGENTS.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# qibla — PRI (Per-Repo Instructions)
|
||||||
|
|
||||||
|
**Cascade:** GCI → ASI → PPI (`/Volumes/X9/Sites/acamarata/.claude/CLAUDE.md`) → **PRI (this file)**
|
||||||
|
|
||||||
|
## Repo Overview
|
||||||
|
|
||||||
|
**Package:** `@acamarata/qibla@1.0.0`
|
||||||
|
**Registry:** npm (public, `access: public`)
|
||||||
|
**Scoped name:** `@acamarata/qibla` — note the `@acamarata/` scope prefix in all install/publish commands
|
||||||
|
**Language:** TypeScript
|
||||||
|
**Runtime deps:** zero — pure math, no external dependencies
|
||||||
|
**Build:** tsup, dual CJS (`index.cjs`) + ESM (`index.mjs`) output
|
||||||
|
**Dart counterpart:** `qibla@1.0.0` on pub.dev (publisher: ariccamarata.com), repo: `qibla-dart`
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
Qibla direction, great-circle path, and haversine distance toward the Ka'bah (Mecca).
|
||||||
|
|
||||||
|
Exported functions:
|
||||||
|
- `qiblaAngle(lat, lng)` — initial bearing to Ka'bah, clockwise from north (0-360)
|
||||||
|
- `compassDir(bearing)` — 8-point compass abbreviation (N, NE, E, SE, S, SW, W, NW)
|
||||||
|
- `compassName(bearing)` — full compass name (North, Northeast, etc.)
|
||||||
|
- `qiblaGreatCircle(lat, lng, steps?)` — Slerp waypoints along the great-circle path to Ka'bah
|
||||||
|
- `distanceKm(lat1, lng1, lat2, lng2)` — haversine distance in km
|
||||||
|
|
||||||
|
Exported constants:
|
||||||
|
- `KAABA_LAT = 21.422511`
|
||||||
|
- `KAABA_LNG = 39.826150`
|
||||||
|
- `EARTH_RADIUS_KM = 6371`
|
||||||
|
|
||||||
|
## Project Rules (inherits from acamarata PPI)
|
||||||
|
|
||||||
|
This repo follows the full acamarata npm package standard. Key points:
|
||||||
|
|
||||||
|
- pnpm only — `pnpm install`, `pnpm test`, `pnpm run build`
|
||||||
|
- No AI attribution anywhere in tracked files
|
||||||
|
- Writing quality: no em dashes as connectors, no AI tells, academic technical tone
|
||||||
|
- Publishing requires explicit user approval
|
||||||
|
- Version bumps require CHANGELOG.md update first
|
||||||
|
|
||||||
|
## Dart Counterpart Relationship
|
||||||
|
|
||||||
|
The JS and Dart packages implement the same algorithm. Keep them in sync on:
|
||||||
|
- Ka'bah coordinate constants (KAABA_LAT / KAABA_LNG)
|
||||||
|
- Algorithm correctness (forward azimuth formula, haversine, Slerp)
|
||||||
|
- API surface parity (functions and constants match across both)
|
||||||
|
|
||||||
|
When updating the JS package in a way that affects algorithm or constants, note whether the Dart package (`qibla-dart`) needs the same fix.
|
||||||
|
|
||||||
|
## npm Publish Command
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm publish --access public
|
||||||
|
```
|
||||||
|
|
||||||
|
The `@acamarata/` scope requires `--access public` on first publish. Already set in `publishConfig` but include it explicitly to avoid accidental private publish.
|
||||||
|
|
||||||
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
34
.github/wiki/CODE_OF_CONDUCT.md
vendored
|
|
@ -1,34 +0,0 @@
|
||||||
# Code of Conduct
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
Be direct, be respectful, and focus on the work.
|
|
||||||
|
|
||||||
## Standards
|
|
||||||
|
|
||||||
Constructive behavior:
|
|
||||||
|
|
||||||
- Technical criticism aimed at code and ideas, not people
|
|
||||||
- Clear and specific feedback with examples where possible
|
|
||||||
- Acknowledging when you are wrong or do not know something
|
|
||||||
- Staying on topic in issues and pull requests
|
|
||||||
|
|
||||||
Unacceptable behavior:
|
|
||||||
|
|
||||||
- Personal attacks, insults, or harassment
|
|
||||||
- Sustained off-topic disruption
|
|
||||||
- Publishing private information without consent
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This applies to all project spaces: GitHub issues, pull requests, discussions, and any other venue where project work happens.
|
|
||||||
|
|
||||||
## Enforcement
|
|
||||||
|
|
||||||
The project maintainer handles violations. Contact: aric.camarata@gmail.com.
|
|
||||||
|
|
||||||
Reports are reviewed promptly. Responses range from a private note to a permanent ban, depending on severity and history.
|
|
||||||
|
|
||||||
## Attribution
|
|
||||||
|
|
||||||
This code of conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
|
||||||
48
.github/wiki/Contributing.md
vendored
48
.github/wiki/Contributing.md
vendored
|
|
@ -1,48 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Node.js 20 or later
|
|
||||||
- pnpm (enabled via corepack: `corepack enable`)
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/acamarata/qibla.git
|
|
||||||
cd qibla
|
|
||||||
pnpm install
|
|
||||||
```
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
```sh
|
|
||||||
pnpm build # compile TypeScript
|
|
||||||
pnpm test # build + run test suite
|
|
||||||
pnpm run typecheck # type-check without emitting
|
|
||||||
pnpm run lint # ESLint
|
|
||||||
pnpm run format # Prettier format
|
|
||||||
```
|
|
||||||
|
|
||||||
## Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
src/
|
|
||||||
index.ts all exports (qiblaAngle, compassDir, compassName, qiblaGreatCircle, distanceKm)
|
|
||||||
types.ts TypeScript types
|
|
||||||
dist/ tsup build output (gitignored)
|
|
||||||
test.mjs ESM test suite
|
|
||||||
test-cjs.cjs CJS test subset
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ka'bah Coordinates
|
|
||||||
|
|
||||||
The Ka'bah position is defined in `src/index.ts` as constants `KAABA_LAT` and `KAABA_LNG`. These are sourced from high-precision geodetic measurements. Do not change them without a reference.
|
|
||||||
|
|
||||||
This package has a Dart counterpart (`qibla` on pub.dev). If you update the Ka'bah coordinates or the algorithm, the Dart package should receive the same update.
|
|
||||||
|
|
||||||
## Pull Requests
|
|
||||||
|
|
||||||
- One logical change per PR
|
|
||||||
- Include tests covering the new behavior
|
|
||||||
- Update `CHANGELOG.md` under `[Unreleased]`
|
|
||||||
- Do not bump the version number
|
|
||||||
26
.github/wiki/SECURITY.md
vendored
26
.github/wiki/SECURITY.md
vendored
|
|
@ -1,26 +0,0 @@
|
||||||
# Security Policy
|
|
||||||
|
|
||||||
## Supported Versions
|
|
||||||
|
|
||||||
| Version | Supported |
|
|
||||||
| --- | --- |
|
|
||||||
| 1.x | Yes |
|
|
||||||
|
|
||||||
## Reporting a Vulnerability
|
|
||||||
|
|
||||||
Do not open a public GitHub issue for security vulnerabilities.
|
|
||||||
|
|
||||||
Email: aric.camarata@gmail.com
|
|
||||||
|
|
||||||
Include:
|
|
||||||
|
|
||||||
- A description of the vulnerability
|
|
||||||
- Steps to reproduce
|
|
||||||
- Potential impact
|
|
||||||
- Any suggested fix, if you have one
|
|
||||||
|
|
||||||
You will receive an acknowledgment within 48 hours and a resolution timeline within 7 days.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
This package is a pure math library. It performs no network requests, reads no files, and holds no credentials. All computations are deterministic spherical geometry. The primary security concern would be a supply-chain compromise of the npm package.
|
|
||||||
1
.github/wiki/_Footer.md
vendored
1
.github/wiki/_Footer.md
vendored
|
|
@ -1 +0,0 @@
|
||||||
[npm](https://www.npmjs.com/package/@acamarata/qibla) · [GitHub](https://github.com/acamarata/qibla) · [Changelog](https://github.com/acamarata/qibla/blob/main/CHANGELOG.md) · MIT License
|
|
||||||
37
.github/wiki/_Sidebar.md
vendored
37
.github/wiki/_Sidebar.md
vendored
|
|
@ -1,37 +0,0 @@
|
||||||
## @acamarata/qibla
|
|
||||||
|
|
||||||
**[Home](Home)**
|
|
||||||
|
|
||||||
**Guides**
|
|
||||||
- [Quick Start](guides/quickstart)
|
|
||||||
- [Advanced](guides/advanced)
|
|
||||||
|
|
||||||
**Reference**
|
|
||||||
- [API Reference](API-Reference)
|
|
||||||
- [Architecture](Architecture)
|
|
||||||
- [Benchmarks](benchmarks/index)
|
|
||||||
|
|
||||||
**API — Functions**
|
|
||||||
- [qiblaAngle](api/qiblaAngle)
|
|
||||||
- [compassDir](api/compassDir)
|
|
||||||
- [compassName](api/compassName)
|
|
||||||
- [qiblaGreatCircle](api/qiblaGreatCircle)
|
|
||||||
- [distanceKm](api/distanceKm)
|
|
||||||
|
|
||||||
**API — Constants & Types**
|
|
||||||
- [Constants](api/constants)
|
|
||||||
- [Types](api/types)
|
|
||||||
|
|
||||||
**Examples**
|
|
||||||
- [Qibla lookup](examples/qibla-lookup)
|
|
||||||
- [Great-circle path](examples/great-circle-path)
|
|
||||||
|
|
||||||
**Contributing**
|
|
||||||
- [Contributing](CONTRIBUTING)
|
|
||||||
- [Code of Conduct](CODE_OF_CONDUCT)
|
|
||||||
- [Security](SECURITY)
|
|
||||||
|
|
||||||
**Links**
|
|
||||||
- [npm](https://www.npmjs.com/package/@acamarata/qibla)
|
|
||||||
- [GitHub](https://github.com/acamarata/qibla)
|
|
||||||
- [Changelog](https://github.com/acamarata/qibla/blob/main/CHANGELOG.md)
|
|
||||||
36
.github/wiki/api/README.md
vendored
36
.github/wiki/api/README.md
vendored
|
|
@ -1,36 +0,0 @@
|
||||||
**@acamarata/qibla v1.1.1**
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
# @acamarata/qibla v1.1.1
|
|
||||||
|
|
||||||
Qibla direction utilities. Pure math, zero external dependencies.
|
|
||||||
|
|
||||||
Computes the initial bearing (forward azimuth) from any point on Earth to
|
|
||||||
the Ka'bah using the spherical law of cosines. Includes compass direction
|
|
||||||
lookup, great-circle interpolation, and haversine distance.
|
|
||||||
|
|
||||||
Ka'bah coordinates sourced from verified GPS data.
|
|
||||||
|
|
||||||
SPORT: packages.md — @acamarata/qibla row
|
|
||||||
|
|
||||||
## Type Aliases
|
|
||||||
|
|
||||||
- [CompassAbbr](type-aliases/CompassAbbr.md)
|
|
||||||
- [CompassName](type-aliases/CompassName.md)
|
|
||||||
|
|
||||||
## Variables
|
|
||||||
|
|
||||||
- [COMPASS\_ABBR](variables/COMPASS_ABBR.md)
|
|
||||||
- [COMPASS\_NAMES](variables/COMPASS_NAMES.md)
|
|
||||||
- [EARTH\_RADIUS\_KM](variables/EARTH_RADIUS_KM.md)
|
|
||||||
- [KAABA\_LAT](variables/KAABA_LAT.md)
|
|
||||||
- [KAABA\_LNG](variables/KAABA_LNG.md)
|
|
||||||
|
|
||||||
## Functions
|
|
||||||
|
|
||||||
- [compassDir](functions/compassDir.md)
|
|
||||||
- [compassName](functions/compassName.md)
|
|
||||||
- [distanceKm](functions/distanceKm.md)
|
|
||||||
- [qiblaAngle](functions/qiblaAngle.md)
|
|
||||||
- [qiblaGreatCircle](functions/qiblaGreatCircle.md)
|
|
||||||
27
.github/wiki/api/functions/compassDir.md
vendored
27
.github/wiki/api/functions/compassDir.md
vendored
|
|
@ -1,27 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / compassDir
|
|
||||||
|
|
||||||
# Function: compassDir()
|
|
||||||
|
|
||||||
> **compassDir**(`bearing`): `"N"` \| `"NE"` \| `"E"` \| `"SE"` \| `"S"` \| `"SW"` \| `"W"` \| `"NW"`
|
|
||||||
|
|
||||||
Defined in: [index.ts:68](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/index.ts#L68)
|
|
||||||
|
|
||||||
Eight-point compass abbreviation for a bearing.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
### bearing
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Bearing in degrees (0-360).
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
`"N"` \| `"NE"` \| `"E"` \| `"SE"` \| `"S"` \| `"SW"` \| `"W"` \| `"NW"`
|
|
||||||
|
|
||||||
Two-letter compass abbreviation (N, NE, E, SE, S, SW, W, NW).
|
|
||||||
27
.github/wiki/api/functions/compassName.md
vendored
27
.github/wiki/api/functions/compassName.md
vendored
|
|
@ -1,27 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / compassName
|
|
||||||
|
|
||||||
# Function: compassName()
|
|
||||||
|
|
||||||
> **compassName**(`bearing`): `"North"` \| `"Northeast"` \| `"East"` \| `"Southeast"` \| `"South"` \| `"Southwest"` \| `"West"` \| `"Northwest"`
|
|
||||||
|
|
||||||
Defined in: [index.ts:80](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/index.ts#L80)
|
|
||||||
|
|
||||||
Full compass direction name for a bearing.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
### bearing
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Bearing in degrees (0-360).
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
`"North"` \| `"Northeast"` \| `"East"` \| `"Southeast"` \| `"South"` \| `"Southwest"` \| `"West"` \| `"Northwest"`
|
|
||||||
|
|
||||||
Full direction name (North, Northeast, etc.).
|
|
||||||
45
.github/wiki/api/functions/distanceKm.md
vendored
45
.github/wiki/api/functions/distanceKm.md
vendored
|
|
@ -1,45 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / distanceKm
|
|
||||||
|
|
||||||
# Function: distanceKm()
|
|
||||||
|
|
||||||
> **distanceKm**(`lat1`, `lng1`, `lat2`, `lng2`): `number`
|
|
||||||
|
|
||||||
Defined in: [index.ts:142](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/index.ts#L142)
|
|
||||||
|
|
||||||
Haversine distance between two coordinate pairs.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
### lat1
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
First point latitude in decimal degrees.
|
|
||||||
|
|
||||||
### lng1
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
First point longitude in decimal degrees.
|
|
||||||
|
|
||||||
### lat2
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Second point latitude in decimal degrees.
|
|
||||||
|
|
||||||
### lng2
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Second point longitude in decimal degrees.
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Distance in kilometers (spherical Earth approximation).
|
|
||||||
51
.github/wiki/api/functions/qiblaAngle.md
vendored
51
.github/wiki/api/functions/qiblaAngle.md
vendored
|
|
@ -1,51 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / qiblaAngle
|
|
||||||
|
|
||||||
# Function: qiblaAngle()
|
|
||||||
|
|
||||||
> **qiblaAngle**(`lat`, `lng`): `number`
|
|
||||||
|
|
||||||
Defined in: [index.ts:46](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/index.ts#L46)
|
|
||||||
|
|
||||||
Qibla bearing in degrees clockwise from true north.
|
|
||||||
|
|
||||||
Uses the forward azimuth formula from spherical trigonometry.
|
|
||||||
Result range: [0, 360).
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
### lat
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Observer latitude in decimal degrees (-90 to 90).
|
|
||||||
|
|
||||||
### lng
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Observer longitude in decimal degrees (-180 to 180).
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Bearing in degrees clockwise from north (0 = N, 90 = E, 180 = S, 270 = W).
|
|
||||||
|
|
||||||
## Throws
|
|
||||||
|
|
||||||
If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
qiblaAngle(40.7128, -74.006); // ~58.49 (New York)
|
|
||||||
qiblaAngle(51.5074, -0.1278); // ~119.0 (London)
|
|
||||||
```
|
|
||||||
|
|
||||||
## See
|
|
||||||
|
|
||||||
[https://github.com/acamarata/qibla/wiki/api/qiblaAngle](https://github.com/acamarata/qibla/wiki/api/qiblaAngle) Wiki API page
|
|
||||||
46
.github/wiki/api/functions/qiblaGreatCircle.md
vendored
46
.github/wiki/api/functions/qiblaGreatCircle.md
vendored
|
|
@ -1,46 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / qiblaGreatCircle
|
|
||||||
|
|
||||||
# Function: qiblaGreatCircle()
|
|
||||||
|
|
||||||
> **qiblaGreatCircle**(`lat`, `lng`, `steps?`): \[`number`, `number`\][]
|
|
||||||
|
|
||||||
Defined in: [index.ts:98](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/index.ts#L98)
|
|
||||||
|
|
||||||
Great-circle waypoints from [lat, lng] to the Ka'bah.
|
|
||||||
|
|
||||||
Uses the Slerp (spherical linear interpolation) formula. Useful for
|
|
||||||
drawing Qibla direction lines on maps.
|
|
||||||
|
|
||||||
## Parameters
|
|
||||||
|
|
||||||
### lat
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Origin latitude in decimal degrees.
|
|
||||||
|
|
||||||
### lng
|
|
||||||
|
|
||||||
`number`
|
|
||||||
|
|
||||||
Origin longitude in decimal degrees.
|
|
||||||
|
|
||||||
### steps?
|
|
||||||
|
|
||||||
`number` = `120`
|
|
||||||
|
|
||||||
Number of segments (default: 120, producing 121 points).
|
|
||||||
|
|
||||||
## Returns
|
|
||||||
|
|
||||||
\[`number`, `number`\][]
|
|
||||||
|
|
||||||
Array of [latitude, longitude] pairs in degrees.
|
|
||||||
|
|
||||||
## Throws
|
|
||||||
|
|
||||||
If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
|
||||||
13
.github/wiki/api/type-aliases/CompassAbbr.md
vendored
13
.github/wiki/api/type-aliases/CompassAbbr.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / CompassAbbr
|
|
||||||
|
|
||||||
# Type Alias: CompassAbbr
|
|
||||||
|
|
||||||
> **CompassAbbr** = *typeof* [`COMPASS_ABBR`](../variables/COMPASS_ABBR.md)\[`number`\]
|
|
||||||
|
|
||||||
Defined in: [types.ts:26](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L26)
|
|
||||||
|
|
||||||
Compass abbreviation type.
|
|
||||||
13
.github/wiki/api/type-aliases/CompassName.md
vendored
13
.github/wiki/api/type-aliases/CompassName.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / CompassName
|
|
||||||
|
|
||||||
# Type Alias: CompassName
|
|
||||||
|
|
||||||
> **CompassName** = *typeof* [`COMPASS_NAMES`](../variables/COMPASS_NAMES.md)\[`number`\]
|
|
||||||
|
|
||||||
Defined in: [types.ts:29](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L29)
|
|
||||||
|
|
||||||
Compass full name type.
|
|
||||||
13
.github/wiki/api/variables/COMPASS_ABBR.md
vendored
13
.github/wiki/api/variables/COMPASS_ABBR.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / COMPASS\_ABBR
|
|
||||||
|
|
||||||
# Variable: COMPASS\_ABBR
|
|
||||||
|
|
||||||
> `const` **COMPASS\_ABBR**: readonly \[`"N"`, `"NE"`, `"E"`, `"SE"`, `"S"`, `"SW"`, `"W"`, `"NW"`\]
|
|
||||||
|
|
||||||
Defined in: [types.ts:11](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L11)
|
|
||||||
|
|
||||||
Eight-point compass abbreviations.
|
|
||||||
13
.github/wiki/api/variables/COMPASS_NAMES.md
vendored
13
.github/wiki/api/variables/COMPASS_NAMES.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / COMPASS\_NAMES
|
|
||||||
|
|
||||||
# Variable: COMPASS\_NAMES
|
|
||||||
|
|
||||||
> `const` **COMPASS\_NAMES**: readonly \[`"North"`, `"Northeast"`, `"East"`, `"Southeast"`, `"South"`, `"Southwest"`, `"West"`, `"Northwest"`\]
|
|
||||||
|
|
||||||
Defined in: [types.ts:14](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L14)
|
|
||||||
|
|
||||||
Eight-point compass full names.
|
|
||||||
13
.github/wiki/api/variables/EARTH_RADIUS_KM.md
vendored
13
.github/wiki/api/variables/EARTH_RADIUS_KM.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / EARTH\_RADIUS\_KM
|
|
||||||
|
|
||||||
# Variable: EARTH\_RADIUS\_KM
|
|
||||||
|
|
||||||
> `const` **EARTH\_RADIUS\_KM**: `6371` = `6371`
|
|
||||||
|
|
||||||
Defined in: [types.ts:8](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L8)
|
|
||||||
|
|
||||||
Mean radius of the Earth in kilometers (WGS-84 volumetric mean).
|
|
||||||
13
.github/wiki/api/variables/KAABA_LAT.md
vendored
13
.github/wiki/api/variables/KAABA_LAT.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / KAABA\_LAT
|
|
||||||
|
|
||||||
# Variable: KAABA\_LAT
|
|
||||||
|
|
||||||
> `const` **KAABA\_LAT**: `21.422511` = `21.422511`
|
|
||||||
|
|
||||||
Defined in: [types.ts:2](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L2)
|
|
||||||
|
|
||||||
Latitude of the Ka'bah center, Masjid al-Haram, Mecca (degrees north).
|
|
||||||
13
.github/wiki/api/variables/KAABA_LNG.md
vendored
13
.github/wiki/api/variables/KAABA_LNG.md
vendored
|
|
@ -1,13 +0,0 @@
|
||||||
[**@acamarata/qibla v1.1.1**](../README.md)
|
|
||||||
|
|
||||||
***
|
|
||||||
|
|
||||||
[@acamarata/qibla](../README.md) / KAABA\_LNG
|
|
||||||
|
|
||||||
# Variable: KAABA\_LNG
|
|
||||||
|
|
||||||
> `const` **KAABA\_LNG**: `39.82615` = `39.82615`
|
|
||||||
|
|
||||||
Defined in: [types.ts:5](https://github.com/acamarata/qibla/blob/a59756bd074a18a3c9cea4311d135cfb23a5ec7d/src/types.ts#L5)
|
|
||||||
|
|
||||||
Longitude of the Ka'bah center, Masjid al-Haram, Mecca (degrees east).
|
|
||||||
57
.github/wiki/benchmarks/index.md
vendored
57
.github/wiki/benchmarks/index.md
vendored
|
|
@ -1,57 +0,0 @@
|
||||||
# Benchmarks
|
|
||||||
|
|
||||||
Measured on Apple M-series hardware, Node.js v25, using 1,000,000 iterations with a 1,000-call warm-up.
|
|
||||||
|
|
||||||
## Bundle size
|
|
||||||
|
|
||||||
| Format | Minified | Gzipped |
|
|
||||||
| ------ | -------- | ------- |
|
|
||||||
| ESM (`index.mjs`) | 2.7 KB | 949 B |
|
|
||||||
| CJS (`index.cjs`) | 3.8 KB | 1.3 KB |
|
|
||||||
|
|
||||||
The library has zero external dependencies. Both formats are shipped in the npm package.
|
|
||||||
|
|
||||||
## Throughput
|
|
||||||
|
|
||||||
| Operation | Ops / sec |
|
|
||||||
| --------- | --------- |
|
|
||||||
| `qiblaAngle` (forward azimuth) | ~180,000,000 |
|
|
||||||
| `distanceKm` (haversine) | ~407,000,000 |
|
|
||||||
|
|
||||||
These numbers reflect the V8 JIT at peak — real applications with diverse inputs and cold starts will see lower throughput. The point is that neither function is a bottleneck: both run in nanoseconds per call.
|
|
||||||
|
|
||||||
## Reproducing the benchmark
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const N = 1_000_000;
|
|
||||||
const LATS = Array.from({ length: 100 }, (_, i) => (i - 50) * 1.8);
|
|
||||||
const LNGS = Array.from({ length: 100 }, (_, i) => (i - 50) * 3.6);
|
|
||||||
|
|
||||||
// Warm up
|
|
||||||
for (let i = 0; i < 1000; i++) qiblaAngle(LATS[i % 100], LNGS[i % 100]);
|
|
||||||
|
|
||||||
// qiblaAngle
|
|
||||||
const t0 = performance.now();
|
|
||||||
for (let i = 0; i < N; i++) qiblaAngle(LATS[i % 100], LNGS[i % 100]);
|
|
||||||
const t1 = performance.now();
|
|
||||||
|
|
||||||
// distanceKm
|
|
||||||
const t2 = performance.now();
|
|
||||||
for (let i = 0; i < N; i++) distanceKm(LATS[i % 100], LNGS[i % 100], KAABA_LAT, KAABA_LNG);
|
|
||||||
const t3 = performance.now();
|
|
||||||
|
|
||||||
console.log(`qiblaAngle: ${Math.round(N / ((t1 - t0) / 1000)).toLocaleString()} ops/s`);
|
|
||||||
console.log(`distanceKm: ${Math.round(N / ((t3 - t2) / 1000)).toLocaleString()} ops/s`);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- V8's JIT optimizer eliminates much of the trig overhead at peak throughput. Expect 40-60% of these values in production with cold module loads.
|
|
||||||
- The library works identically in browsers (V8/SpiderMonkey/WebKit), Deno, and Bun. Performance will vary by runtime and optimization tier.
|
|
||||||
- `qiblaGreatCircle` is not benchmarked here because throughput depends on the step count. At the default of 120 steps, it runs in roughly 2-5 microseconds per call.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
[Home](../Home) | [API Reference](../API-Reference)
|
|
||||||
56
.github/wiki/examples/great-circle-path.md
vendored
56
.github/wiki/examples/great-circle-path.md
vendored
|
|
@ -1,56 +0,0 @@
|
||||||
# Example: Great-Circle Path to Mecca
|
|
||||||
|
|
||||||
Generate waypoints along the great-circle path from a city to the Ka'bah.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle, qiblaGreatCircle, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const ORIGIN_NAME = 'New York';
|
|
||||||
const ORIGIN_LAT = 40.7128;
|
|
||||||
const ORIGIN_LNG = -74.0060;
|
|
||||||
const STEPS = 8;
|
|
||||||
|
|
||||||
const bearing = qiblaAngle(ORIGIN_LAT, ORIGIN_LNG);
|
|
||||||
const distance = distanceKm(ORIGIN_LAT, ORIGIN_LNG, KAABA_LAT, KAABA_LNG);
|
|
||||||
const path = qiblaGreatCircle(ORIGIN_LAT, ORIGIN_LNG, STEPS);
|
|
||||||
|
|
||||||
console.log(`Great-circle path: ${ORIGIN_NAME} → Mecca`);
|
|
||||||
console.log(` Initial bearing: ${bearing.toFixed(2)}°`);
|
|
||||||
console.log(` Total distance: ${Math.round(distance).toLocaleString()} km`);
|
|
||||||
console.log(` Waypoints (${STEPS}):`);
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
const stepKm = distance / (STEPS - 1);
|
|
||||||
|
|
||||||
for (let i = 0; i < path.length; i++) {
|
|
||||||
const [lat, lng] = path[i];
|
|
||||||
const km = Math.round(stepKm * i);
|
|
||||||
const tag = i === 0 ? ` ← ${ORIGIN_NAME}` : i === path.length - 1 ? ' ← Ka\'bah' : '';
|
|
||||||
console.log(` ${i + 1}. ${lat.toFixed(4)}°, ${lng.toFixed(4)}° (+${km.toLocaleString()} km)${tag}`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Sample output:
|
|
||||||
|
|
||||||
```
|
|
||||||
Great-circle path: New York → Mecca
|
|
||||||
Initial bearing: 58.49°
|
|
||||||
Total distance: 9,139 km
|
|
||||||
Waypoints (8):
|
|
||||||
|
|
||||||
1. 40.7128°, -74.0060° (+0 km) ← New York
|
|
||||||
2. 47.2391°, -56.2891° (+1,305 km)
|
|
||||||
3. 53.1093°, -35.4823° (+2,610 km)
|
|
||||||
4. 57.6212°, -10.4521° (+3,915 km)
|
|
||||||
5. 60.0301°, 18.1842° (+5,220 km)
|
|
||||||
6. 59.7034°, 44.5781° (+6,525 km)
|
|
||||||
7. 56.2941°, 64.7329° (+7,830 km)
|
|
||||||
8. 21.4225°, 39.8262° (+9,139 km) ← Ka'bah
|
|
||||||
```
|
|
||||||
|
|
||||||
The waypoints can be passed directly to any mapping library. For Leaflet:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const latLngs = path.map(([lat, lng]) => [lat, lng]);
|
|
||||||
L.polyline(latLngs, { color: 'green' }).addTo(map);
|
|
||||||
```
|
|
||||||
52
.github/wiki/examples/qibla-lookup.md
vendored
52
.github/wiki/examples/qibla-lookup.md
vendored
|
|
@ -1,52 +0,0 @@
|
||||||
# Example: Qibla Lookup for Multiple Cities
|
|
||||||
|
|
||||||
Print Qibla bearing and distance for a set of global cities.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle, compassName, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const cities = [
|
|
||||||
{ name: 'New York', lat: 40.7128, lng: -74.0060 },
|
|
||||||
{ name: 'London', lat: 51.5074, lng: -0.1278 },
|
|
||||||
{ name: 'Istanbul', lat: 41.0082, lng: 28.9784 },
|
|
||||||
{ name: 'Nairobi', lat: -1.2921, lng: 36.8219 },
|
|
||||||
{ name: 'Karachi', lat: 24.8607, lng: 67.0011 },
|
|
||||||
{ name: 'Kuala Lumpur', lat: 3.1390, lng: 101.6869 },
|
|
||||||
{ name: 'Jakarta', lat: -6.2088, lng: 106.8456 },
|
|
||||||
{ name: 'Sydney', lat: -33.8688, lng: 151.2093 },
|
|
||||||
];
|
|
||||||
|
|
||||||
console.log('Qibla directions from major cities\n');
|
|
||||||
console.log(`${'City'.padEnd(18)} ${'Bearing'.padStart(8)} ${'Direction'.padEnd(14)} Distance`);
|
|
||||||
console.log('─'.repeat(62));
|
|
||||||
|
|
||||||
for (const city of cities) {
|
|
||||||
const bearing = qiblaAngle(city.lat, city.lng);
|
|
||||||
const dir = compassName(bearing);
|
|
||||||
const km = distanceKm(city.lat, city.lng, KAABA_LAT, KAABA_LNG);
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
city.name.padEnd(18) +
|
|
||||||
` ${bearing.toFixed(1).padStart(7)}°` +
|
|
||||||
` ${dir.padEnd(14)}` +
|
|
||||||
` ${Math.round(km).toLocaleString()} km`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Sample output:
|
|
||||||
|
|
||||||
```
|
|
||||||
Qibla directions from major cities
|
|
||||||
|
|
||||||
City Bearing Direction Distance
|
|
||||||
──────────────────────────────────────────────────────────────
|
|
||||||
New York 58.5° Northeast 9,139 km
|
|
||||||
London 119.0° Southeast 4,950 km
|
|
||||||
Istanbul 36.6° Northeast 2,620 km
|
|
||||||
Nairobi 29.8° Northeast 3,618 km
|
|
||||||
Karachi 64.8° Northeast 1,932 km
|
|
||||||
Kuala Lumpur 292.5° West-northwest 6,354 km
|
|
||||||
Jakarta 292.5° West-northwest 7,756 km
|
|
||||||
Sydney 278.0° West 1,365 km
|
|
||||||
```
|
|
||||||
98
.github/wiki/guides/advanced.md
vendored
98
.github/wiki/guides/advanced.md
vendored
|
|
@ -1,98 +0,0 @@
|
||||||
# Advanced Usage
|
|
||||||
|
|
||||||
## Compass overlay integration
|
|
||||||
|
|
||||||
The eight-point compass abbreviation is suited for UI labels. Map the bearing to a rotation for a needle overlay:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const bearing = qiblaAngle(lat, lng);
|
|
||||||
|
|
||||||
// CSS rotation for a compass needle pointing north by default
|
|
||||||
const rotation = `rotate(${bearing}deg)`;
|
|
||||||
needle.style.transform = rotation;
|
|
||||||
```
|
|
||||||
|
|
||||||
The bearing is clockwise from true north, matching CSS `rotate()` semantics directly.
|
|
||||||
|
|
||||||
## Dense great-circle paths
|
|
||||||
|
|
||||||
Pass a higher step count for smoother polylines on a map:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaGreatCircle, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const observer = [40.7128, -74.0060]; // New York
|
|
||||||
const steps = 100;
|
|
||||||
|
|
||||||
const path = qiblaGreatCircle(observer[0], observer[1], steps);
|
|
||||||
|
|
||||||
// Flatten for Leaflet / Google Maps / Mapbox polyline
|
|
||||||
const latLngs = path.map(([lat, lng]) => ({ lat, lng }));
|
|
||||||
```
|
|
||||||
|
|
||||||
The default step count is 20. Steps above 200 are rarely useful for display.
|
|
||||||
|
|
||||||
## Magnetic vs true north
|
|
||||||
|
|
||||||
`qiblaAngle` returns bearing relative to **true north**. For a physical compass, apply the local magnetic declination:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const trueBearing = qiblaAngle(lat, lng);
|
|
||||||
|
|
||||||
// Magnetic declination for New York (west = negative)
|
|
||||||
const declination = -13.2;
|
|
||||||
|
|
||||||
const magneticBearing = (trueBearing - declination + 360) % 360;
|
|
||||||
console.log(`Magnetic: ${magneticBearing.toFixed(1)}°`);
|
|
||||||
```
|
|
||||||
|
|
||||||
Magnetic declination varies by location and year. Use a current value from NOAA's World Magnetic Model or a service like `@mapbox/geodeticsurvey`.
|
|
||||||
|
|
||||||
## Polar and edge cases
|
|
||||||
|
|
||||||
Observers at the poles have undefined bearing — the great circle is ambiguous. `qiblaAngle` returns `NaN` for latitudes exactly at ±90. Check before using:
|
|
||||||
|
|
||||||
```js
|
|
||||||
const bearing = qiblaAngle(90, 0); // NaN — north pole
|
|
||||||
if (!isFinite(bearing)) {
|
|
||||||
console.log('Bearing undefined at this location.');
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Observers very close to Mecca (within ~1 km) may get erratic bearings due to floating-point precision near the target. No special handling is needed in practice.
|
|
||||||
|
|
||||||
## Batch calculation for multiple cities
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle, compassDir, distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const cities = [
|
|
||||||
{ name: 'New York', lat: 40.7128, lng: -74.0060 },
|
|
||||||
{ name: 'London', lat: 51.5074, lng: -0.1278 },
|
|
||||||
{ name: 'Istanbul', lat: 41.0082, lng: 28.9784 },
|
|
||||||
{ name: 'Kuala Lumpur', lat: 3.1390, lng: 101.6869 },
|
|
||||||
{ name: 'Sydney', lat: -33.8688, lng: 151.2093 },
|
|
||||||
];
|
|
||||||
|
|
||||||
console.log('City'.padEnd(16) + 'Bearing Dir Distance');
|
|
||||||
console.log('-'.repeat(46));
|
|
||||||
|
|
||||||
for (const c of cities) {
|
|
||||||
const b = qiblaAngle(c.lat, c.lng);
|
|
||||||
const dir = compassDir(b);
|
|
||||||
const km = distanceKm(c.lat, c.lng, KAABA_LAT, KAABA_LNG);
|
|
||||||
console.log(
|
|
||||||
c.name.padEnd(16) +
|
|
||||||
`${b.toFixed(1).padStart(6)}° ${dir.padEnd(4)} ${Math.round(km).toLocaleString()} km`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related pages
|
|
||||||
|
|
||||||
- [API Reference](../API-Reference) — parameter types, return values, thrown errors
|
|
||||||
- [Architecture](../Architecture) — algorithm details, coordinate system, Ka'bah coordinates
|
|
||||||
69
.github/wiki/guides/quickstart.md
vendored
69
.github/wiki/guides/quickstart.md
vendored
|
|
@ -1,69 +0,0 @@
|
||||||
# Quick Start
|
|
||||||
|
|
||||||
Five minutes from install to Qibla direction.
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install @acamarata/qibla
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic usage
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle, compassDir, compassName } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const LAT = 40.7128; // New York
|
|
||||||
const LNG = -74.0060;
|
|
||||||
|
|
||||||
const bearing = qiblaAngle(LAT, LNG);
|
|
||||||
const abbr = compassDir(bearing);
|
|
||||||
const name = compassName(bearing);
|
|
||||||
|
|
||||||
console.log(`Qibla: ${bearing.toFixed(2)}° (${name}, ${abbr})`);
|
|
||||||
// Qibla: 58.49° (Northeast, NE)
|
|
||||||
```
|
|
||||||
|
|
||||||
## Distance to Mecca
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { distanceKm, KAABA_LAT, KAABA_LNG } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
const km = distanceKm(40.7128, -74.0060, KAABA_LAT, KAABA_LNG);
|
|
||||||
console.log(`Distance to Ka'bah: ${Math.round(km).toLocaleString()} km`);
|
|
||||||
// Distance to Ka'bah: 9,139 km
|
|
||||||
```
|
|
||||||
|
|
||||||
## Great-circle path
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaGreatCircle } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
// 10 waypoints along the path from New York to Mecca
|
|
||||||
const path = qiblaGreatCircle(40.7128, -74.0060, 10);
|
|
||||||
|
|
||||||
for (const [lat, lng] of path) {
|
|
||||||
console.log(` ${lat.toFixed(2)}, ${lng.toFixed(2)}`);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The returned array always starts at the observer's position and ends at the Ka'bah.
|
|
||||||
|
|
||||||
## Input validation
|
|
||||||
|
|
||||||
All functions throw `RangeError` on invalid coordinates:
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { qiblaAngle } from '@acamarata/qibla';
|
|
||||||
|
|
||||||
try {
|
|
||||||
qiblaAngle(200, 0); // lat out of range
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err.message); // "lat must be in range [-90, 90]"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Next steps
|
|
||||||
|
|
||||||
- [API Reference](../API-Reference) — full function and constant documentation
|
|
||||||
- [Advanced Guide](advanced) — compass overlay, map integration, path interpolation
|
|
||||||
37
.github/workflows/ci.yml
vendored
37
.github/workflows/ci.yml
vendored
|
|
@ -15,12 +15,12 @@ jobs:
|
||||||
node-version: [20, 22, 24]
|
node-version: [20, 22, 24]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Enable corepack
|
|
||||||
run: corepack enable
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
- name: Enable corepack
|
||||||
|
run: corepack enable
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
- run: pnpm build
|
- run: pnpm build
|
||||||
- run: node --test test.mjs
|
- run: node --test test.mjs
|
||||||
|
|
@ -31,12 +31,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Enable corepack
|
|
||||||
run: corepack enable
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
- name: Enable corepack
|
||||||
|
run: corepack enable
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
- run: pnpm run lint
|
- run: pnpm run lint
|
||||||
- run: pnpm run format:check
|
- run: pnpm run format:check
|
||||||
|
|
@ -46,12 +46,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Enable corepack
|
|
||||||
run: corepack enable
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
- name: Enable corepack
|
||||||
|
run: corepack enable
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
- run: pnpm run typecheck
|
- run: pnpm run typecheck
|
||||||
|
|
||||||
|
|
@ -60,12 +60,12 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Enable corepack
|
|
||||||
run: corepack enable
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 24
|
node-version: 24
|
||||||
cache: pnpm
|
cache: pnpm
|
||||||
|
- name: Enable corepack
|
||||||
|
run: corepack enable
|
||||||
- run: pnpm install --frozen-lockfile
|
- run: pnpm install --frozen-lockfile
|
||||||
- run: pnpm build
|
- run: pnpm build
|
||||||
- name: Verify pack contents
|
- name: Verify pack contents
|
||||||
|
|
@ -79,24 +79,3 @@ jobs:
|
||||||
grep "CHANGELOG.md" pack-output.txt
|
grep "CHANGELOG.md" pack-output.txt
|
||||||
grep "LICENSE" pack-output.txt
|
grep "LICENSE" pack-output.txt
|
||||||
echo "Pack check passed"
|
echo "Pack check passed"
|
||||||
|
|
||||||
coverage:
|
|
||||||
name: Coverage
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Enable corepack
|
|
||||||
run: corepack enable
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 24
|
|
||||||
cache: pnpm
|
|
||||||
- run: pnpm install --frozen-lockfile
|
|
||||||
- run: pnpm build
|
|
||||||
- run: pnpm run coverage
|
|
||||||
- name: Upload to Codecov
|
|
||||||
uses: codecov/codecov-action@v4
|
|
||||||
with:
|
|
||||||
files: ./coverage/lcov.info
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
fail_ci_if_error: false
|
|
||||||
|
|
|
||||||
4
.github/workflows/wiki-sync.yml
vendored
4
.github/workflows/wiki-sync.yml
vendored
|
|
@ -3,7 +3,7 @@ name: Sync Wiki
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths: ['.github/wiki/**']
|
paths: [".github/wiki/**"]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|
@ -23,7 +23,7 @@ jobs:
|
||||||
echo "wiki_exists=true" >> "$GITHUB_OUTPUT"
|
echo "wiki_exists=true" >> "$GITHUB_OUTPUT"
|
||||||
else
|
else
|
||||||
echo "wiki_exists=false" >> "$GITHUB_OUTPUT"
|
echo "wiki_exists=false" >> "$GITHUB_OUTPUT"
|
||||||
echo "Wiki not yet initialized. Create the first page once via the GitHub web UI; subsequent runs will sync .github/wiki automatically."
|
echo "Wiki not yet initialized — skipping sync. Initialize via GitHub web UI first."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Sync wiki pages
|
- name: Sync wiki pages
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,6 +1,5 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
dist/
|
dist/
|
||||||
coverage/
|
|
||||||
*.tgz
|
*.tgz
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
|
||||||
1
.prettierrc
Normal file
1
.prettierrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
|
|
@ -7,13 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [1.1.2] - 2026-05-30
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- Add TSDoc comments with examples and wiki links to all exported functions
|
|
||||||
- Add non-null assertions with explanatory comments for array index access
|
|
||||||
- Formatting cleanup (inline multi-line function signatures)
|
|
||||||
|
|
||||||
## [1.1.1] - 2026-05-28
|
## [1.1.1] - 2026-05-28
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
[](https://www.npmjs.com/package/%40acamarata%2Fqibla)
|
[](https://www.npmjs.com/package/%40acamarata%2Fqibla)
|
||||||
[](https://github.com/acamarata/qibla/actions/workflows/ci.yml)
|
[](https://github.com/acamarata/qibla/actions/workflows/ci.yml)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/acamarata/qibla/wiki)
|
|
||||||
|
|
||||||
Qibla direction, great-circle path, and haversine distance. Pure math, zero dependencies.
|
Qibla direction, great-circle path, and haversine distance. Pure math, zero dependencies.
|
||||||
|
|
||||||
|
|
@ -56,11 +55,6 @@ Full API reference, algorithm design, and spherical trigonometry notes: [GitHub
|
||||||
|
|
||||||
Ka'bah coordinates verified against published GPS surveys and cross-checked with satellite imagery. Forward azimuth formula follows standard spherical trigonometry as used in aviation and geodesy.
|
Ka'bah coordinates verified against published GPS surveys and cross-checked with satellite imagery. Forward azimuth formula follows standard spherical trigonometry as used in aviation and geodesy.
|
||||||
|
|
||||||
## Telemetry
|
|
||||||
|
|
||||||
This package supports opt-in anonymous usage telemetry — off by default.
|
|
||||||
Enable: `ACAMARATA_TELEMETRY=1`. See [TELEMETRY.md](./TELEMETRY.md) for what is sent and how to disable.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# Telemetry Disclosure
|
|
||||||
|
|
||||||
This package supports opt-in anonymous usage telemetry via [`@acamarata/telemetry`](https://github.com/acamarata/telemetry).
|
|
||||||
|
|
||||||
Telemetry is **off by default**. No data is sent unless you set `ACAMARATA_TELEMETRY=1`.
|
|
||||||
|
|
||||||
Full disclosure (what is sent, where it goes, how to disable):
|
|
||||||
[github.com/acamarata/telemetry/blob/main/TELEMETRY.md](https://github.com/acamarata/telemetry/blob/main/TELEMETRY.md)
|
|
||||||
|
|
@ -1,30 +1,10 @@
|
||||||
import tsParser from '@typescript-eslint/parser';
|
import eslint from "@eslint/js";
|
||||||
import tsPlugin from '@typescript-eslint/eslint-plugin';
|
import tseslint from "typescript-eslint";
|
||||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
|
||||||
import { typescript } from '@acamarata/eslint-config';
|
|
||||||
import { fileURLToPath } from 'url';
|
|
||||||
import { dirname } from 'path';
|
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
export default tseslint.config(
|
||||||
|
eslint.configs.recommended,
|
||||||
export default [
|
...tseslint.configs.recommended,
|
||||||
{
|
{
|
||||||
files: ['src/**/*.ts'],
|
ignores: ["dist/", "*.cjs"],
|
||||||
plugins: { '@typescript-eslint': tsPlugin },
|
|
||||||
languageOptions: {
|
|
||||||
parser: tsParser,
|
|
||||||
parserOptions: {
|
|
||||||
project: './tsconfig.json',
|
|
||||||
tsconfigRootDir: __dirname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
);
|
||||||
files: ['src/**/*.ts'],
|
|
||||||
...typescript.reduce((acc, cfg) => ({ ...acc, ...cfg }), {}),
|
|
||||||
},
|
|
||||||
eslintConfigPrettier,
|
|
||||||
{
|
|
||||||
ignores: ['dist/', 'node_modules/', '*.cjs', '*.mjs', 'tsup.config.ts', 'eslint.config.js'],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
|
||||||
27
package.json
27
package.json
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@acamarata/qibla",
|
"name": "@acamarata/qibla",
|
||||||
"version": "1.1.2",
|
"version": "1.1.1",
|
||||||
"description": "Qibla direction, great-circle path, and haversine distance. Pure math, zero dependencies.",
|
"description": "Qibla direction, great-circle path, and haversine distance. Pure math, zero dependencies.",
|
||||||
"author": "Aric Camarata",
|
"author": "Aric Camarata",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|
@ -34,12 +34,10 @@
|
||||||
"pretest": "tsup",
|
"pretest": "tsup",
|
||||||
"test": "node --test test.mjs && node --test test-cjs.cjs",
|
"test": "node --test test.mjs && node --test test-cjs.cjs",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check src/",
|
"format:check": "prettier --check .",
|
||||||
"prepack": "pnpm run build",
|
"prepublishOnly": "tsup",
|
||||||
"coverage": "c8 --reporter=lcov --reporter=text node test.mjs",
|
"coverage": "c8 --reporter=lcov --reporter=text node --test"
|
||||||
"docs": "typedoc --out .github/wiki/api src/index.ts",
|
|
||||||
"postbuild": "cp dist/index.d.ts dist/index.d.mts"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
@ -70,25 +68,14 @@
|
||||||
"registry": "https://registry.npmjs.org/"
|
"registry": "https://registry.npmjs.org/"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@acamarata/eslint-config": "^0.1.0",
|
|
||||||
"@acamarata/prettier-config": "^0.1.0",
|
|
||||||
"@acamarata/tsconfig": "^0.1.0",
|
|
||||||
"@eslint/js": "^9.27.0",
|
"@eslint/js": "^9.27.0",
|
||||||
"@types/node": "^22.15.3",
|
"@types/node": "^22.15.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.56.1",
|
|
||||||
"@typescript-eslint/parser": "^8.56.1",
|
|
||||||
"c8": "^11.0.0",
|
|
||||||
"eslint": "^9.27.0",
|
"eslint": "^9.27.0",
|
||||||
"eslint-config-prettier": "^10.1.8",
|
|
||||||
"prettier": "^3.5.3",
|
"prettier": "^3.5.3",
|
||||||
"tsup": "^8.4.0",
|
"tsup": "^8.4.0",
|
||||||
"typedoc": "^0.28.19",
|
|
||||||
"typedoc-plugin-markdown": "^4.11.0",
|
|
||||||
"typescript": "^5.8.3",
|
"typescript": "^5.8.3",
|
||||||
"typescript-eslint": "^8.32.1",
|
"typescript-eslint": "^8.32.1"
|
||||||
"@acamarata/telemetry": "^0.1.0"
|
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.11.1",
|
"packageManager": "pnpm@10.11.1"
|
||||||
"prettier": "@acamarata/prettier-config"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2360
pnpm-lock.yaml
2360
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
55
src/index.ts
55
src/index.ts
|
|
@ -7,8 +7,6 @@
|
||||||
*
|
*
|
||||||
* Ka'bah coordinates sourced from verified GPS data.
|
* Ka'bah coordinates sourced from verified GPS data.
|
||||||
*
|
*
|
||||||
* SPORT: packages.md — @acamarata/qibla row
|
|
||||||
*
|
|
||||||
* @module
|
* @module
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -36,12 +34,6 @@ const DEG = Math.PI / 180;
|
||||||
* @param lng - Observer longitude in decimal degrees (-180 to 180).
|
* @param lng - Observer longitude in decimal degrees (-180 to 180).
|
||||||
* @returns Bearing in degrees clockwise from north (0 = N, 90 = E, 180 = S, 270 = W).
|
* @returns Bearing in degrees clockwise from north (0 = N, 90 = E, 180 = S, 270 = W).
|
||||||
* @throws {RangeError} If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
* @throws {RangeError} If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* qiblaAngle(40.7128, -74.006); // ~58.49 (New York)
|
|
||||||
* qiblaAngle(51.5074, -0.1278); // ~119.0 (London)
|
|
||||||
*
|
|
||||||
* @see {@link https://github.com/acamarata/qibla/wiki/api/qiblaAngle} Wiki API page
|
|
||||||
*/
|
*/
|
||||||
export function qiblaAngle(lat: number, lng: number): number {
|
export function qiblaAngle(lat: number, lng: number): number {
|
||||||
if (lat < -90 || lat > 90) {
|
if (lat < -90 || lat > 90) {
|
||||||
|
|
@ -55,10 +47,14 @@ export function qiblaAngle(lat: number, lng: number): number {
|
||||||
const φ2 = KAABA_LAT * DEG,
|
const φ2 = KAABA_LAT * DEG,
|
||||||
λ2 = KAABA_LNG * DEG;
|
λ2 = KAABA_LNG * DEG;
|
||||||
const y = Math.sin(λ2 - λ1) * Math.cos(φ2);
|
const y = Math.sin(λ2 - λ1) * Math.cos(φ2);
|
||||||
const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
|
const x =
|
||||||
|
Math.cos(φ1) * Math.sin(φ2) -
|
||||||
|
Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1);
|
||||||
return (Math.atan2(y, x) / DEG + 360) % 360;
|
return (Math.atan2(y, x) / DEG + 360) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eight-point compass abbreviation for a bearing.
|
* Eight-point compass abbreviation for a bearing.
|
||||||
*
|
*
|
||||||
|
|
@ -66,9 +62,7 @@ export function qiblaAngle(lat: number, lng: number): number {
|
||||||
* @returns Two-letter compass abbreviation (N, NE, E, SE, S, SW, W, NW).
|
* @returns Two-letter compass abbreviation (N, NE, E, SE, S, SW, W, NW).
|
||||||
*/
|
*/
|
||||||
export function compassDir(bearing: number): CompassAbbr {
|
export function compassDir(bearing: number): CompassAbbr {
|
||||||
// Non-null assertion: index is always 0-7 (Math.round(bearing/45) % 8), which is within COMPASS_ABBR bounds.
|
return COMPASS_ABBR[Math.round(bearing / 45) % 8];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
return COMPASS_ABBR[Math.round(bearing / 45) % 8]!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -78,9 +72,7 @@ export function compassDir(bearing: number): CompassAbbr {
|
||||||
* @returns Full direction name (North, Northeast, etc.).
|
* @returns Full direction name (North, Northeast, etc.).
|
||||||
*/
|
*/
|
||||||
export function compassName(bearing: number): CompassName {
|
export function compassName(bearing: number): CompassName {
|
||||||
// Non-null assertion: index is always 0-7 (Math.round(bearing/45) % 8), which is within COMPASS_NAMES bounds.
|
return COMPASS_NAMES[Math.round(bearing / 45) % 8];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
return COMPASS_NAMES[Math.round(bearing / 45) % 8]!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -95,7 +87,11 @@ export function compassName(bearing: number): CompassName {
|
||||||
* @returns Array of [latitude, longitude] pairs in degrees.
|
* @returns Array of [latitude, longitude] pairs in degrees.
|
||||||
* @throws {RangeError} If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
* @throws {RangeError} If latitude is outside [-90, 90] or longitude outside [-180, 180].
|
||||||
*/
|
*/
|
||||||
export function qiblaGreatCircle(lat: number, lng: number, steps = 120): [number, number][] {
|
export function qiblaGreatCircle(
|
||||||
|
lat: number,
|
||||||
|
lng: number,
|
||||||
|
steps = 120,
|
||||||
|
): [number, number][] {
|
||||||
if (lat < -90 || lat > 90) {
|
if (lat < -90 || lat > 90) {
|
||||||
throw new RangeError(`Latitude must be between -90 and 90, got ${lat}`);
|
throw new RangeError(`Latitude must be between -90 and 90, got ${lat}`);
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +107,8 @@ export function qiblaGreatCircle(lat: number, lng: number, steps = 120): [number
|
||||||
2 *
|
2 *
|
||||||
Math.asin(
|
Math.asin(
|
||||||
Math.sqrt(
|
Math.sqrt(
|
||||||
Math.sin((φ2 - φ1) / 2) ** 2 + Math.cos(φ1) * Math.cos(φ2) * Math.sin((λ2 - λ1) / 2) ** 2,
|
Math.sin((φ2 - φ1) / 2) ** 2 +
|
||||||
|
Math.cos(φ1) * Math.cos(φ2) * Math.sin((λ2 - λ1) / 2) ** 2,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -125,7 +122,10 @@ export function qiblaGreatCircle(lat: number, lng: number, steps = 120): [number
|
||||||
const x = A * Math.cos(φ1) * Math.cos(λ1) + B * Math.cos(φ2) * Math.cos(λ2);
|
const x = A * Math.cos(φ1) * Math.cos(λ1) + B * Math.cos(φ2) * Math.cos(λ2);
|
||||||
const y = A * Math.cos(φ1) * Math.sin(λ1) + B * Math.cos(φ2) * Math.sin(λ2);
|
const y = A * Math.cos(φ1) * Math.sin(λ1) + B * Math.cos(φ2) * Math.sin(λ2);
|
||||||
const z = A * Math.sin(φ1) + B * Math.sin(φ2);
|
const z = A * Math.sin(φ1) + B * Math.sin(φ2);
|
||||||
points.push([Math.atan2(z, Math.sqrt(x * x + y * y)) / DEG, Math.atan2(y, x) / DEG]);
|
points.push([
|
||||||
|
Math.atan2(z, Math.sqrt(x * x + y * y)) / DEG,
|
||||||
|
Math.atan2(y, x) / DEG,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
@ -139,19 +139,16 @@ export function qiblaGreatCircle(lat: number, lng: number, steps = 120): [number
|
||||||
* @param lng2 - Second point longitude in decimal degrees.
|
* @param lng2 - Second point longitude in decimal degrees.
|
||||||
* @returns Distance in kilometers (spherical Earth approximation).
|
* @returns Distance in kilometers (spherical Earth approximation).
|
||||||
*/
|
*/
|
||||||
export function distanceKm(lat1: number, lng1: number, lat2: number, lng2: number): number {
|
export function distanceKm(
|
||||||
|
lat1: number,
|
||||||
|
lng1: number,
|
||||||
|
lat2: number,
|
||||||
|
lng2: number,
|
||||||
|
): number {
|
||||||
const dLat = (lat2 - lat1) * DEG;
|
const dLat = (lat2 - lat1) * DEG;
|
||||||
const dLng = (lng2 - lng1) * DEG;
|
const dLng = (lng2 - lng1) * DEG;
|
||||||
const a =
|
const a =
|
||||||
Math.sin(dLat / 2) ** 2 + Math.cos(lat1 * DEG) * Math.cos(lat2 * DEG) * Math.sin(dLng / 2) ** 2;
|
Math.sin(dLat / 2) ** 2 +
|
||||||
|
Math.cos(lat1 * DEG) * Math.cos(lat2 * DEG) * Math.sin(dLng / 2) ** 2;
|
||||||
return EARTH_RADIUS_KM * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
return EARTH_RADIUS_KM * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Opt-in anonymous telemetry ────────────────────────────────────────────────
|
|
||||||
// Off by default. Enable: ACAMARATA_TELEMETRY=1
|
|
||||||
// What is sent + how to disable: https://github.com/acamarata/telemetry/blob/main/TELEMETRY.md
|
|
||||||
import("@acamarata/telemetry")
|
|
||||||
.then(({ track }) => track("load", { package: "@acamarata/qibla", version: "1.1.2" }))
|
|
||||||
.catch(() => {
|
|
||||||
// telemetry not installed or disabled — that is fine
|
|
||||||
});
|
|
||||||
|
|
|
||||||
|
|
@ -27,3 +27,4 @@ export type CompassAbbr = (typeof COMPASS_ABBR)[number];
|
||||||
|
|
||||||
/** Compass full name type. */
|
/** Compass full name type. */
|
||||||
export type CompassName = (typeof COMPASS_NAMES)[number];
|
export type CompassName = (typeof COMPASS_NAMES)[number];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,16 @@
|
||||||
{
|
{
|
||||||
"extends": "@acamarata/tsconfig/tsconfig.library.json",
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"strict": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "dist",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"types": ["node"]
|
"types": ["node"]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
10
typedoc.json
10
typedoc.json
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"entryPoints": ["src/index.ts"],
|
|
||||||
"out": ".github/wiki/api",
|
|
||||||
"plugin": ["typedoc-plugin-markdown"],
|
|
||||||
"readme": "none",
|
|
||||||
"skipErrorChecking": false,
|
|
||||||
"excludePrivate": true,
|
|
||||||
"excludeProtected": true,
|
|
||||||
"includeVersion": true
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue