diff --git a/.claude/AGENTS.md b/.claude/AGENTS.md deleted file mode 120000 index 681311e..0000000 --- a/.claude/AGENTS.md +++ /dev/null @@ -1 +0,0 @@ -CLAUDE.md \ No newline at end of file diff --git a/.claude/AGENTS.md b/.claude/AGENTS.md new file mode 100644 index 0000000..c187de6 --- /dev/null +++ b/.claude/AGENTS.md @@ -0,0 +1,41 @@ +# hijri-core — PRI (Per-Repo Instructions) + +**PPI:** `~/Sites/acamarata/.claude/CLAUDE.md` + +## What This Is + +Core Hijri calendar engine for JavaScript/TypeScript. Provides Hijri/Gregorian date conversion with a pluggable calendar registry supporting multiple calculation methods. + +**npm:** `hijri-core@1.0.0` +**Language:** TypeScript +**License:** MIT + +## Key Technical Details + +- Zero runtime dependencies +- Pluggable engine architecture: UAQ (Umm al-Qura, 1318-1500 AH) and FCNA engines built in +- Foundation package: used by luxon-hijri, date-fns-hijri, dayjs-hijri-plus, moment-hijri-plus, temporal-hijri +- Dart counterpart: hijri-core-dart (hijri_core@1.0.0 on pub.dev) + +## API Surface + +- `toHijri(gregorianDate, engine?)` — Gregorian to Hijri +- `toGregorian(hijriYear, hijriMonth, hijriDay, engine?)` — Hijri to Gregorian +- `registerCalendar(name, engine)` — add custom calendar engine +- `getCalendar(name)` — retrieve registered engine +- `isValidHijriDate(year, month, day, engine?)` — validation +- `daysInHijriMonth(year, month, engine?)` — month length +- Month name arrays: long/medium/short (Arabic + English) +- Weekday name arrays + +## Important Notes + +- This is a FOUNDATION package. Breaking changes here affect 5 downstream plugin packages. +- When making API changes: update all 5 plugin packages (luxon-hijri, date-fns-hijri, dayjs-hijri-plus, moment-hijri-plus, temporal-hijri) and bump their versions too. +- Test all 5 plugins against any hijri-core change before publishing. + +## Commands + +- `pnpm test` — run test.mjs + test-cjs.cjs +- `pnpm run typecheck` — tsc --noEmit +- `pnpm build` — tsup build diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..6c44b81 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,21 @@ +# 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] + +## [1.0.1] - 2026-05-28 + +### Changed +- Flatten exports map to ADR-015 standard (import/require/types at top level) +- Add "./package.json" export condition +- Add coverage script (c8 --reporter=lcov) +- Migrate CI from pnpm/action-setup to corepack enable + +## [1.0.0] - 2026-05-28 + +### Added +- Initial release diff --git a/README.md b/README.md index afc1cc8..9b07743 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,12 @@ [![CI](https://github.com/acamarata/hijri-core/actions/workflows/ci.yml/badge.svg)](https://github.com/acamarata/hijri-core/actions/workflows/ci.yml) [![license](https://img.shields.io/npm/l/hijri-core.svg)](LICENSE) -Zero-dependency Hijri calendar engine. Supports the Umm al-Qura (UAQ) and FCNA/ISNA calendars out of the box. Extensible via a calendar registry for custom implementations. +Zero-dependency Hijri calendar engine for JavaScript and TypeScript. Supports the Umm al-Qura (UAQ) and FCNA/ISNA calendars out of the box. A pluggable registry lets you add custom calendar implementations at runtime. ## Installation ```bash npm install hijri-core -# or -pnpm add hijri-core ``` ## Quick Start @@ -23,101 +21,22 @@ import { toHijri, toGregorian, isValidHijriDate, daysInHijriMonth } from 'hijri- const hijri = toHijri(new Date(2025, 2, 1)); // { hy: 1446, hm: 9, hd: 1 } -// Hijri to Gregorian (UAQ) +// Hijri to Gregorian const greg = toGregorian(1446, 9, 1); // Date: 2025-03-01 // FCNA/ISNA calendar -const hijriFcna = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); -const gregFcna = toGregorian(1446, 9, 1, { calendar: 'fcna' }); +toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); +toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Validation and month length isValidHijriDate(1444, 9, 1); // true -daysInHijriMonth(1444, 9); // 29 +daysInHijriMonth(1444, 9); // 29 ``` -### Custom Calendar - -```typescript -import { registerCalendar, toHijri, type CalendarEngine } from 'hijri-core'; - -const myEngine: CalendarEngine = { - id: 'my-calendar', - toHijri: (date) => { - /* your logic */ return { hy: 1446, hm: 1, hd: 1 }; - }, - toGregorian: (hy, hm, hd) => { - /* your logic */ return new Date(); - }, - isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1, - daysInMonth: (hy, hm) => 30, -}; - -registerCalendar('my-calendar', myEngine); - -const result = toHijri(new Date(), { calendar: 'my-calendar' }); -``` - -## API - -### Conversion functions - -| Function | Parameters | Returns | Notes | -| ------------------------------------- | -------------------------------------------- | ------------------- | ----------------------------- | -| `toHijri(date, opts?)` | `Date`, `ConversionOptions?` | `HijriDate \| null` | Throws on invalid Date | -| `toGregorian(hy, hm, hd, opts?)` | `number, number, number, ConversionOptions?` | `Date \| null` | Returns null on invalid input | -| `isValidHijriDate(hy, hm, hd, opts?)` | `number, number, number, ConversionOptions?` | `boolean` | | -| `daysInHijriMonth(hy, hm, opts?)` | `number, number, ConversionOptions?` | `number` | | - -`ConversionOptions.calendar` defaults to `'uaq'`. Pass `'fcna'` or any registered calendar name. - -### Registry functions - -| Function | Parameters | Returns | -| -------------------------------- | ------------------------ | ---------------- | -| `registerCalendar(name, engine)` | `string, CalendarEngine` | `void` | -| `getCalendar(name)` | `string` | `CalendarEngine` | -| `listCalendars()` | none | `string[]` | - -### Data exports - -| Export | Type | Description | -| ------------- | ------------------- | ---------------------------------------------- | -| `hDatesTable` | `HijriYearRecord[]` | Full Umm al-Qura reference table (184 entries) | -| `hmLong` | `string[]` | Long month names (e.g., "Ramadan") | -| `hmMedium` | `string[]` | Medium month names (e.g., "Ramadan") | -| `hmShort` | `string[]` | Short month names (e.g., "Ram") | -| `hwLong` | `string[]` | Long weekday names | -| `hwShort` | `string[]` | Short weekday names | -| `hwNumeric` | `number[]` | Weekday numbers (1 = Sunday) | - ## Custom Calendars -Implement the `CalendarEngine` interface to add any calendar system: - -```typescript -interface CalendarEngine { - readonly id: string; - toHijri(date: Date): HijriDate | null; - toGregorian(hy: number, hm: number, hd: number): Date | null; - isValid(hy: number, hm: number, hd: number): boolean; - daysInMonth(hy: number, hm: number): number; -} -``` - -Register with `registerCalendar('my-id', myEngine)`. Then pass `{ calendar: 'my-id' }` to any conversion function. - -## Architecture - -The UAQ engine performs a binary search over the 184-entry table: O(log 183) per conversion. The FCNA engine computes new moon times using the Meeus Ch. 49 algorithm (3 to 5 trigonometric evaluations per call). The registry pattern lets any consumer add custom calendar systems at runtime without modifying the core. - -For more detail see the [Architecture wiki page](https://github.com/acamarata/hijri-core/wiki/Architecture). - -## Compatibility - -- Node.js 20, 22, 24 -- Modern browsers (ESM build) -- CommonJS and ESM +Implement `CalendarEngine` and call `registerCalendar('my-id', engine)`. Pass `{ calendar: 'my-id' }` to any conversion function. ## TypeScript @@ -127,19 +46,19 @@ import type { HijriDate, HijriYearRecord, CalendarEngine, ConversionOptions } fr ## Documentation -Full API reference and architecture notes: [GitHub Wiki](https://github.com/acamarata/hijri-core/wiki) +Full API reference, architecture notes, and calendar background: [GitHub Wiki](https://github.com/acamarata/hijri-core/wiki) ## Related -- [luxon-hijri](https://github.com/acamarata/luxon-hijri) - Hijri formatting with Luxon -- [dayjs-hijri-plus](https://github.com/acamarata/dayjs-hijri-plus) - Day.js Hijri plugin -- [date-fns-hijri](https://github.com/acamarata/date-fns-hijri) - date-fns Hijri helpers -- [moment-hijri-plus](https://github.com/acamarata/moment-hijri-plus) - Moment.js Hijri plugin -- [temporal-hijri](https://github.com/acamarata/temporal-hijri) - Temporal API Hijri support +- [luxon-hijri](https://github.com/acamarata/luxon-hijri): Hijri formatting with Luxon +- [dayjs-hijri-plus](https://github.com/acamarata/dayjs-hijri-plus): Day.js Hijri plugin +- [date-fns-hijri](https://github.com/acamarata/date-fns-hijri): date-fns Hijri helpers +- [moment-hijri-plus](https://github.com/acamarata/moment-hijri-plus): Moment.js Hijri plugin +- [temporal-hijri](https://github.com/acamarata/temporal-hijri): Temporal API Hijri support ## Acknowledgments -The Umm al-Qura calendar table is derived from data published by the King Abdulaziz City for Science and Technology (KACST), Saudi Arabia. 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 the King Abdulaziz City for Science and Technology (KACST). The FCNA new moon algorithm follows Jean Meeus, "Astronomical Algorithms," 2nd ed., Chapter 49. ## License diff --git a/package.json b/package.json index 96032e3..3e54259 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hijri-core", - "version": "1.0.0", + "version": "1.0.1", "description": "Zero-dependency Hijri calendar engine with pluggable calendar support. Includes Umm al-Qura (UAQ) and FCNA/ISNA calendars. Extensible registry for custom calendars.", "author": "Aric Camarata", "license": "MIT", @@ -9,15 +9,11 @@ "types": "./dist/index.d.ts", "exports": { ".": { - "import": { - "types": "./dist/index.d.mts", - "default": "./dist/index.mjs" - }, - "require": { - "types": "./dist/index.d.ts", - "default": "./dist/index.cjs" - } - } + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.cjs" + }, + "./package.json": "./package.json" }, "sideEffects": false, "files": [ @@ -41,7 +37,8 @@ "prepublishOnly": "tsup", "lint": "eslint .", "format": "prettier --write .", - "format:check": "prettier --check ." + "format:check": "prettier --check .", + "coverage": "c8 --reporter=lcov --reporter=text node --test" }, "keywords": [ "hijri", @@ -76,5 +73,6 @@ "homepage": "https://github.com/acamarata/hijri-core#readme", "bugs": { "url": "https://github.com/acamarata/hijri-core/issues" - } + }, + "type": "module" }