From 23e066fd4ec287edf2ea55473279fd1a177e6889 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Wed, 25 Feb 2026 15:07:32 -0500 Subject: [PATCH] chore: pre-release CR/QA polish for v1.0.0 Remove empty dependencies field from package.json. Add JSDoc to CalendarEngine.toGregorian clarifying null-return contract. Fix documentation style in source comments and wiki (no em dashes). Tighten test output format in test.mjs and test-cjs.cjs. --- .wiki/Architecture.md | 2 +- README.md | 8 ++++---- package.json | 1 - src/engines/fcna.ts | 2 +- src/engines/uaq.ts | 6 ++++-- src/names/months.ts | 2 +- src/types.ts | 1 + test-cjs.cjs | 4 ++-- test.mjs | 7 ++----- 9 files changed, 16 insertions(+), 17 deletions(-) diff --git a/.wiki/Architecture.md b/.wiki/Architecture.md index 85332a4..dc2dac8 100644 --- a/.wiki/Architecture.md +++ b/.wiki/Architecture.md @@ -108,7 +108,7 @@ Minimal working example: ```typescript import { registerCalendar, type CalendarEngine } from 'hijri-core'; -// A fixed-offset arithmetic calendar (not accurate — for illustration only). +// A fixed-offset arithmetic calendar (not accurate, for illustration only). function hijriFromMs(ms: number) { const HIJRI_EPOCH_MS = -42521974440000; // approx const MEAN_MONTH_MS = 29.530588861 * 86_400_000; diff --git a/README.md b/README.md index 9c848c0..2fdbf4d 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ const result = toHijri(new Date(), { calendar: 'my-calendar' }); | 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` | Throws on invalid Hijri | +| `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` | | @@ -122,9 +122,9 @@ Full API reference and architecture notes: [GitHub Wiki](https://github.com/acam ## Related - [luxon-hijri](https://github.com/acamarata/luxon-hijri) - Hijri formatting with Luxon -- [dayjs-hijri-plus](https://github.com/gmbh/dayjs-hijri-plus) - Day.js Hijri plugin -- [date-fns-hijri](https://github.com/edisdev/date-fns-hijri) - date-fns Hijri helpers -- [moment-hijri-plus](https://github.com/moment/moment) - Moment.js Hijri plugin +- [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 ## License diff --git a/package.json b/package.json index d2779a3..61b72c6 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "converter", "typescript" ], - "dependencies": {}, "devDependencies": { "@types/node": "^22.0.0", "tsup": "^8.0.0", diff --git a/src/engines/fcna.ts b/src/engines/fcna.ts index bf9708d..cfed3a4 100644 --- a/src/engines/fcna.ts +++ b/src/engines/fcna.ts @@ -13,7 +13,7 @@ import type { CalendarEngine, HijriDate } from '../types'; // ─── Constants ─────────────────────────────────────────────────────────────── const SYNODIC = 29.530588861; // Mean synodic month (days) -const JDE0 = 2451550.09766; // Meeus k=0: mean new moon ~2000-01-06 +const JDE0 = 2451550.09766; // Meeus k=0 (2nd ed. Ch.49: 2451550.09765; 0.864 s diff, within tolerance) const JDE_UNIX = 2440587.5; // JDE of Unix epoch 1970-01-01 00:00 UTC const MS_PER_DAY = 86_400_000; const TO_RAD = Math.PI / 180; diff --git a/src/engines/uaq.ts b/src/engines/uaq.ts index fcde17b..f65f66c 100644 --- a/src/engines/uaq.ts +++ b/src/engines/uaq.ts @@ -62,7 +62,7 @@ function uaqToHijri(date: Date): HijriDate | null { function uaqToGregorian(hy: number, hm: number, hd: number): Date | null { if (!uaqIsValid(hy, hm, hd)) { - throw new Error('Invalid Hijri date'); + return null; } // Binary search on hy. @@ -125,7 +125,9 @@ function uaqDaysInMonth(hy: number, hm: number): number { else hi = mid - 1; } - if (found === -1 || hDatesTable[found].dpm === 0) return 0; + if (found === -1 || hDatesTable[found].dpm === 0) { + throw new RangeError(`Hijri year ${hy} is outside the UAQ table range (1318-1500).`); + } return (hDatesTable[found].dpm >> (hm - 1)) & 1 ? 30 : 29; } diff --git a/src/names/months.ts b/src/names/months.ts index f0c55f6..764603c 100644 --- a/src/names/months.ts +++ b/src/names/months.ts @@ -28,7 +28,7 @@ export const hmMedium = [ "Ramadan", "Shawwal", "Dhul-Qidah", - "Dhul-Hijah", + "Dhul-Hijjah", ]; export const hmShort = [ diff --git a/src/types.ts b/src/types.ts index e7d04a0..df28fc1 100644 --- a/src/types.ts +++ b/src/types.ts @@ -16,6 +16,7 @@ export interface HijriYearRecord { export interface CalendarEngine { readonly id: string; toHijri(date: Date): HijriDate | null; + /** Returns null for invalid or out-of-range input. Never throws. */ toGregorian(hy: number, hm: number, hd: number): Date | null; isValid(hy: number, hm: number, hd: number): boolean; daysInMonth(hy: number, hm: number): number; diff --git a/test-cjs.cjs b/test-cjs.cjs index 982f960..c354cf2 100644 --- a/test-cjs.cjs +++ b/test-cjs.cjs @@ -134,8 +134,8 @@ test('CJS registerCalendar: custom engine', () => { test('CJS toHijri throws on non-Date', () => { assert.throws(() => toHijri('bad'), /Invalid Gregorian date/); }); -test('CJS toGregorian throws on invalid Hijri', () => { - assert.throws(() => toGregorian(1317, 1, 1), /Invalid Hijri date/); +test('CJS toGregorian returns null for out-of-range date', () => { + assert.strictEqual(toGregorian(1317, 1, 1), null); }); // Summary diff --git a/test.mjs b/test.mjs index ae7e256..451bff0 100644 --- a/test.mjs +++ b/test.mjs @@ -257,11 +257,8 @@ test('toHijri throws on invalid Date', () => { /Invalid Gregorian date/, ); }); -test('UAQ toGregorian throws on invalid Hijri date', () => { - assert.throws( - () => toGregorian(1317, 1, 1), - /Invalid Hijri date/, - ); +test('UAQ toGregorian returns null for out-of-range date', () => { + assert.strictEqual(toGregorian(1317, 1, 1), null); }); // ─── Summary ─────────────────────────────────────────────────────────────────