From 7f09544fbc42aceeeae567f04693d7a53c8361b3 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Thu, 28 May 2026 14:00:16 -0400 Subject: [PATCH] =?UTF-8?q?docs(e6):=20portfolio=20polish=20=E2=80=94=20RE?= =?UTF-8?q?ADME=20trim,=20CHANGELOG,=20wiki=20pages?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Trim README to ≤80 lines with wiki link for full docs - Add CHANGELOG.md documenting v3.0.0 breaking change (peer-dep migration) - Add .github/wiki pages: _Sidebar, _Footer, Contributing, SECURITY, CODE_OF_CONDUCT --- .github/wiki/CODE_OF_CONDUCT.md | 34 +++++++ .github/wiki/Contributing.md | 43 ++++++++ .github/wiki/SECURITY.md | 27 +++++ .github/wiki/_Footer.md | 1 + .github/wiki/_Sidebar.md | 18 ++++ CHANGELOG.md | 30 ++++++ README.md | 168 +++----------------------------- 7 files changed, 164 insertions(+), 157 deletions(-) create mode 100644 .github/wiki/CODE_OF_CONDUCT.md create mode 100644 .github/wiki/Contributing.md create mode 100644 .github/wiki/SECURITY.md create mode 100644 .github/wiki/_Footer.md create mode 100644 .github/wiki/_Sidebar.md create mode 100644 CHANGELOG.md diff --git a/.github/wiki/CODE_OF_CONDUCT.md b/.github/wiki/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..c880c1a --- /dev/null +++ b/.github/wiki/CODE_OF_CONDUCT.md @@ -0,0 +1,34 @@ +# 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. diff --git a/.github/wiki/Contributing.md b/.github/wiki/Contributing.md new file mode 100644 index 0000000..d8a25ac --- /dev/null +++ b/.github/wiki/Contributing.md @@ -0,0 +1,43 @@ +# Contributing + +## Prerequisites + +- Node.js 20 or later +- pnpm (enabled via corepack: `corepack enable`) + +## Setup + +```sh +git clone https://github.com/acamarata/luxon-hijri.git +cd luxon-hijri +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 +``` + +## Architecture + +luxon-hijri is a thin adapter layer. All calendar conversion logic lives in `hijri-core`. This package's responsibility is mapping Luxon's API surface to hijri-core's conversion functions. + +When adding features, ask first whether the logic belongs in `hijri-core` (shared across all wrappers) or in this package (Luxon-specific adapter code). + +## Test Cross-Validation + +The test suite validates against known UAQ table dates and ICOP Ramadan moon sighting dates. When modifying conversion logic, run the cross-validation tests and verify all pass. + +See [Architecture](Architecture) for the expected date ranges. + +## Pull Requests + +- One logical change per PR +- Include cross-validation tests for any new date logic +- Update `CHANGELOG.md` under `[Unreleased]` +- Do not bump the version number diff --git a/.github/wiki/SECURITY.md b/.github/wiki/SECURITY.md new file mode 100644 index 0000000..8b8ee71 --- /dev/null +++ b/.github/wiki/SECURITY.md @@ -0,0 +1,27 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| --- | --- | +| 2.x | Yes | +| 1.x | No | + +## 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 Luxon plugin providing Hijri calendar support. It performs no network requests, reads no files, and holds no credentials. It depends on `hijri-core` for calendar conversions and `luxon` for date handling. Vulnerabilities in those packages should be reported to their respective maintainers. diff --git a/.github/wiki/_Footer.md b/.github/wiki/_Footer.md new file mode 100644 index 0000000..5b2e6fb --- /dev/null +++ b/.github/wiki/_Footer.md @@ -0,0 +1 @@ +[npm](https://www.npmjs.com/package/luxon-hijri) · [GitHub](https://github.com/acamarata/luxon-hijri) · [Changelog](https://github.com/acamarata/luxon-hijri/blob/main/CHANGELOG.md) · MIT License diff --git a/.github/wiki/_Sidebar.md b/.github/wiki/_Sidebar.md new file mode 100644 index 0000000..15d70db --- /dev/null +++ b/.github/wiki/_Sidebar.md @@ -0,0 +1,18 @@ +## luxon-hijri + +**[Home](Home)** + +**Reference** +- [API Reference](API-Reference) +- [Architecture](Architecture) +- [Hijri Calendar](Hijri-Calendar) + +**Contributing** +- [Contributing](Contributing) +- [Code of Conduct](CODE_OF_CONDUCT) +- [Security](SECURITY) + +**Links** +- [npm](https://www.npmjs.com/package/luxon-hijri) +- [GitHub](https://github.com/acamarata/luxon-hijri) +- [Changelog](https://github.com/acamarata/luxon-hijri/blob/main/CHANGELOG.md) diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6496088 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,30 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [3.0.0] - 2026-05-28 + +### Changed + +- BREAKING: `luxon` and `hijri-core` moved from `dependencies` to `peerDependencies`. Consumers must now install both alongside `luxon-hijri`. See the migration note below. +- Peer range for `luxon` widened from `^3.5.0` to `^3.0.0` — any Luxon 3.x release is compatible. + +### Migration from v2.x + +```bash +pnpm add luxon-hijri luxon hijri-core +# or +npm install luxon-hijri luxon hijri-core +``` + +Prior to v3.0.0, `luxon` and `hijri-core` were bundled as runtime dependencies. This caused Luxon to appear twice in bundled applications where it was already installed. v3.0.0 aligns with the peer-dependency pattern used by all other hijri wrapper packages (`date-fns-hijri`, `dayjs-hijri-plus`, `moment-hijri-plus`, `temporal-hijri`). + +## [2.1.0] - 2026-05-28 + +### Added +- Initial release diff --git a/README.md b/README.md index 6b633db..644ce3d 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,10 @@ [![CI](https://github.com/acamarata/luxon-hijri/actions/workflows/ci.yml/badge.svg)](https://github.com/acamarata/luxon-hijri/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE) -Hijri/Gregorian date conversion and formatting. Supports two calendar systems: Umm al-Qura (default, table-based) and FCNA/ISNA (astronomical, all Hijri years). Built on Luxon. +Hijri/Gregorian date conversion and formatting for Luxon users. Thin adapter over [hijri-core](https://github.com/acamarata/hijri-core). Supports the Umm al-Qura calendar (1318-1500 AH, table-based) and the FCNA/ISNA calendar (astronomical, all Hijri years). ## Installation -`luxon-hijri` requires `luxon` and `hijri-core` as peer dependencies. Install all three: - ```bash pnpm add luxon-hijri luxon hijri-core # or @@ -22,186 +20,42 @@ npm install luxon-hijri luxon hijri-core import { toHijri, toGregorian, formatHijriDate } from 'luxon-hijri'; // Gregorian to Hijri (Umm al-Qura, default) -const h = toHijri(new Date(2023, 2, 23, 12)); // March 23, 2023 +const h = toHijri(new Date(2023, 2, 23, 12)); // { hy: 1444, hm: 9, hd: 1 } // Hijri to Gregorian -const g = toGregorian(1444, 9, 1); // 1 Ramadan 1444 +const g = toGregorian(1444, 9, 1); // Date: 2023-03-23T00:00:00.000Z // Format a Hijri date formatHijriDate({ hy: 1444, hm: 9, hd: 1 }, 'iEEEE, iD iMMMM iYYYY ioooo'); // "Yawm al-Khamis, 1 Ramadan 1444 AH" -// FCNA/ISNA calendar (astronomical, works for all Hijri years) -toHijri(new Date(2025, 2, 1, 12), { calendar: 'fcna' }); // { hy: 1446, hm: 9, hd: 1 } -toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Date: 2025-03-01T00:00:00.000Z +// FCNA/ISNA calendar +toHijri(new Date(2025, 2, 1, 12), { calendar: 'fcna' }); ``` -## API - -### `toHijri(date, options?)` - -Converts a Gregorian `Date` to a Hijri date object. - -```typescript -function toHijri(date: Date, options?: ConversionOptions): HijriDate | null; -``` - -For `'uaq'` (default): returns `null` if the date falls outside the table range (before 1 Muharram 1318 H / 1900-04-30, or at/after 1 Muharram 1501 H / 2077-11-17). Uses local date components. - -For `'fcna'`: returns `null` only for dates before 1 AH. Uses UTC date components (FCNA boundaries are defined in UTC). - -Throws `Error("Invalid Gregorian date")` if `date` is not a valid `Date`. - -```javascript -toHijri(new Date(2024, 6, 7, 12)); // { hy: 1446, hm: 1, hd: 1 } (UAQ) -toHijri(new Date(2025, 2, 1, 12), { calendar: 'fcna' }); // { hy: 1446, hm: 9, hd: 1 } (FCNA) -toHijri(new Date(1800, 0, 1)); // null - before UAQ table range -``` - -### `toGregorian(hy, hm, hd, options?)` - -Converts a Hijri date to a Gregorian `Date` at UTC midnight. - -```typescript -function toGregorian(hy: number, hm: number, hd: number, options?: ConversionOptions): Date; -``` - -Throws `Error("Invalid Hijri date")` if the date is invalid for the selected calendar. - -```javascript -toGregorian(1446, 1, 1); // Date: 2024-07-07T00:00:00.000Z (UAQ) -toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Date: 2025-03-01T00:00:00.000Z (FCNA) -toGregorian(1, 1, 1, { calendar: 'fcna' }); // Date: 0622-07-18T00:00:00.000Z (Islamic epoch) -``` - -### `formatHijriDate(date, format)` - -Formats a Hijri date using the token patterns below. Tokens not listed pass through unchanged. - -```typescript -function formatHijriDate(date: HijriDate, format: string): string; -``` - -| Token | Output | Example | -| -------------------- | ------------------------ | --------------------- | -| `iYYYY` | Year, 4 digits | `1444` | -| `iYY` | Year, last 2 digits | `44` | -| `iMMMM` | Month, full name | `Ramadan` | -| `iMMM` | Month, medium name | `Ramadan` | -| `iMM` | Month, zero-padded | `09` | -| `iM` | Month, no padding | `9` | -| `iDD` | Day, zero-padded | `01` | -| `iD` | Day, no padding | `1` | -| `iEEEE` | Weekday, full name | `Yawm al-Khamis` | -| `iEEE` | Weekday, abbreviated | `Kham` | -| `iE` | Weekday, numeric (Sun=1) | `5` | -| `ioooo` | Era, full | `AH` | -| `iooo` | Era, abbreviated | `AH` | -| `HH`, `H`, `hh`, `h` | Hour (via Luxon) | `14`, `14`, `02`, `2` | -| `mm`, `m` | Minute (via Luxon) | `05`, `5` | -| `ss`, `s` | Second (via Luxon) | `30`, `30` | -| `a` | AM/PM | `AM` | -| `z`, `zz`, `zzz` | Timezone | `UTC` | -| `Z`, `ZZ` | Timezone offset | `+00:00` | - -### `isValidHijriDate(hy, hm, hd, options?)` - -Returns `true` if the Hijri date is valid for the selected calendar. - -```typescript -function isValidHijriDate(hy: number, hm: number, hd: number, options?: ConversionOptions): boolean; -``` - -For `'uaq'` (default): year must be 1318–1500, month 1–12, day must not exceed the actual month length from the UAQ table. - -For `'fcna'`: year must be ≥ 1, month 1–12, day must not exceed the computed FCNA month length. - -### Types - -```typescript -interface HijriDate { - hy: number; // Hijri year - hm: number; // Hijri month (1–12) - hd: number; // Hijri day (1–30) -} - -type CalendarSystem = 'uaq' | 'fcna'; - -interface ConversionOptions { - calendar?: CalendarSystem; // default: 'uaq' -} - -interface HijriYearRecord { - hy: number; // Hijri year - dpm: number; // days-per-month bitmask (bit 0 = month 1, 1 = 30 days, 0 = 29 days) - gy: number; // Gregorian year of 1 Muharram - gm: number; // Gregorian month of 1 Muharram - gd: number; // Gregorian day of 1 Muharram -} -``` - -### Additional exports - -```javascript -import { - hDatesTable, // HijriYearRecord[] - the full Umm al-Qura table (184 entries) - hmLong, // string[12] - full month names - hmMedium, // string[12] - medium month names - hmShort, // string[12] - abbreviated month names - hwLong, // string[7] - full weekday names (Sunday first) - hwShort, // string[7] - abbreviated weekday names - hwNumeric, // number[7] - weekday numbers (1-7, Sunday=1) - formatPatterns, // Record - token reference -} from 'luxon-hijri'; -``` - -## Calendar Systems - -**Umm al-Qura (`'uaq'`, default):** Official Saudi calendar, table-based, covers Hijri years 1318–1500 (April 1900 to November 2076). Authoritative for Saudi Arabia and widely used across the Arab world. - -**FCNA/ISNA (`'fcna'`):** Used by the Fiqh Council of North America and ISNA. Astronomical criterion: if the new moon conjunction occurs before 12:00 UTC on day D, the month begins at midnight of D+1; otherwise D+2. Works for all Hijri years (no range limit). New moon times use the full Meeus Chapter 49 algorithm, accurate to within a few minutes for 1000–3000 CE. - -## Architecture - -The UAQ engine is a pure table lookup with binary search (O(log 183)). The FCNA engine computes new moon times astronomically using the Meeus Ch.49 formula: 3 to 5 trigonometric evaluations per call, sub-millisecond on any modern JS engine. - -For more detail see the [Architecture wiki page](https://github.com/acamarata/luxon-hijri/wiki/Architecture). - -## Compatibility - -- Node.js 20+ (ESM and CJS) -- Bundlers: webpack, Rollup, Vite, esbuild (tree-shakeable, `sideEffects: false`) -- TypeScript: full type definitions included - ## TypeScript ```typescript import { toHijri, toGregorian, formatHijriDate, isValidHijriDate } from 'luxon-hijri'; -import type { HijriDate, HijriYearRecord, CalendarSystem, ConversionOptions } from 'luxon-hijri'; - -const h: HijriDate | null = toHijri(new Date()); -const g: Date = toGregorian(1444, 9, 1, { calendar: 'fcna' }); +import type { HijriDate, CalendarSystem, ConversionOptions } from 'luxon-hijri'; ``` ## Documentation -Full API reference, architecture notes, calendar background, and format token guide: -[https://github.com/acamarata/luxon-hijri/wiki](https://github.com/acamarata/luxon-hijri/wiki) +Full API reference, format token guide, calendar background, and architecture notes: [GitHub Wiki](https://github.com/acamarata/luxon-hijri/wiki) ## Related -- [nrel-spa](https://www.npmjs.com/package/nrel-spa): NREL Solar Position Algorithm (pure JS) -- [pray-calc](https://www.npmjs.com/package/pray-calc): Islamic prayer times, depends on nrel-spa -- [solar-spa](https://www.npmjs.com/package/solar-spa): NREL SPA compiled to WebAssembly +- [hijri-core](https://github.com/acamarata/hijri-core): The underlying calendar engine +- [pray-calc](https://www.npmjs.com/package/pray-calc): Islamic prayer times +- [nrel-spa](https://www.npmjs.com/package/nrel-spa): NREL Solar Position Algorithm ## Acknowledgments -The Umm al-Qura table is derived from data published by the King Abdulaziz City for Science and Technology (KACST). The FCNA new moon algorithm follows Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49. +The Umm al-Qura table is derived from data published by KACST (King Abdulaziz City for Science and Technology). The FCNA new moon algorithm follows Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49. ## License MIT. Copyright (c) 2024-2026 Aric Camarata. - -See [LICENSE](./LICENSE) for the full text.