From f0757b1333f70234105d7106f7bc05bdb66e0eb6 Mon Sep 17 00:00:00 2001 From: Aric Camarata Date: Sun, 8 Mar 2026 11:38:10 -0400 Subject: [PATCH] refactor: code quality improvements across the board --- .github/workflows/ci.yml | 18 +- .gitignore | 24 +- .prettierrc | 6 + .wiki/API-Reference.md | 64 +- .wiki/Architecture.md | 13 +- .wiki/Home.md | 2 +- README.md | 52 +- eslint.config.mjs | 10 + package.json | 49 +- pnpm-lock.yaml | 1603 ++++++++++++++++++++++++++++++++++---- src/constants.ts | 5 + src/data/hDates.ts | 354 ++++----- src/engines/fcna.ts | 206 +++-- src/engines/uaq.ts | 96 +-- src/index.ts | 53 +- src/names/months.ts | 72 +- src/names/weekdays.ts | 28 +- src/registry.ts | 23 +- src/types.ts | 8 +- test-cjs.cjs | 228 +++--- test.mjs | 431 +++++----- tsconfig.json | 5 +- 22 files changed, 2382 insertions(+), 968 deletions(-) create mode 100644 .prettierrc create mode 100644 eslint.config.mjs create mode 100644 src/constants.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d0f046..d5a3527 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,22 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm run build - - run: node test.mjs - - run: node test-cjs.cjs + - run: node --test test.mjs + - run: node --test test-cjs.cjs + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 24 + cache: pnpm + - run: pnpm install --frozen-lockfile + - run: pnpm run lint + - run: pnpm run format:check typecheck: name: Typecheck diff --git a/.gitignore b/.gitignore index 7860879..823fa68 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,30 @@ node_modules/ dist/ +build/ +out/ *.tgz +*.tsbuildinfo *.log +npm-debug.log* +coverage/ .DS_Store -.claude/ .env .env.* +!.env.example +.claude/ +.vscode/ +.idea/ +*.swp +.pnp +.pnp.js +.cursor/ +.copilot/ +.aider* +.continue/ +.codex/ +.gemini/ +.vscode/* +.aider/ +.aider.chat.history.md +.windsurf/ +.codeium/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..383f607 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "singleQuote": true, + "trailingComma": "all", + "printWidth": 100, + "tabWidth": 2 +} diff --git a/.wiki/API-Reference.md b/.wiki/API-Reference.md index aca9b1e..5a30952 100644 --- a/.wiki/API-Reference.md +++ b/.wiki/API-Reference.md @@ -7,12 +7,12 @@ Converts a Gregorian `Date` to a Hijri date object. ```typescript -function toHijri(date: Date, options?: ConversionOptions): HijriDate | null +function toHijri(date: Date, options?: ConversionOptions): HijriDate | null; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| `date` | `Date` | Gregorian date to convert | +| Parameter | Type | Description | +| ------------------ | -------- | ---------------------------------- | +| `date` | `Date` | Gregorian date to convert | | `options.calendar` | `string` | Calendar name. Defaults to `'uaq'` | Returns `HijriDate` or `null` if the date falls outside the calendar's supported range. @@ -26,19 +26,14 @@ UAQ uses local date components (`getFullYear`, `getMonth`, `getDate`) for timezo Converts a Hijri date to a Gregorian `Date`. ```typescript -function toGregorian( - hy: number, - hm: number, - hd: number, - options?: ConversionOptions -): Date | null +function toGregorian(hy: number, hm: number, hd: number, options?: ConversionOptions): Date | null; ``` -| Parameter | Type | Description | -| --- | --- | --- | -| `hy` | `number` | Hijri year | -| `hm` | `number` | Hijri month (1-12) | -| `hd` | `number` | Hijri day (1-30) | +| Parameter | Type | Description | +| ------------------ | -------- | ---------------------------------- | +| `hy` | `number` | Hijri year | +| `hm` | `number` | Hijri month (1-12) | +| `hd` | `number` | Hijri day (1-30) | | `options.calendar` | `string` | Calendar name. Defaults to `'uaq'` | Returns a UTC midnight `Date` or `null` if the input is out of range. @@ -50,12 +45,7 @@ Throws `Error('Invalid Hijri date')` for UAQ when the date is not in the referen Returns `true` if the given Hijri date exists in the selected calendar. ```typescript -function isValidHijriDate( - hy: number, - hm: number, - hd: number, - options?: ConversionOptions -): boolean +function isValidHijriDate(hy: number, hm: number, hd: number, options?: ConversionOptions): boolean; ``` ### `daysInHijriMonth(hy, hm, options?)` @@ -63,7 +53,7 @@ function isValidHijriDate( Returns the number of days in a given Hijri month. ```typescript -function daysInHijriMonth(hy: number, hm: number, options?: ConversionOptions): number +function daysInHijriMonth(hy: number, hm: number, options?: ConversionOptions): number; ``` Returns 29 or 30. Returns 0 for UAQ when the year is out of range. @@ -75,7 +65,7 @@ Returns 29 or 30. Returns 0 for UAQ when the year is out of range. Registers a calendar engine. Overwrites any existing engine with the same name. ```typescript -function registerCalendar(name: string, engine: CalendarEngine): void +function registerCalendar(name: string, engine: CalendarEngine): void; ``` ### `getCalendar(name)` @@ -83,7 +73,7 @@ function registerCalendar(name: string, engine: CalendarEngine): void Returns the registered engine for a given name. Throws if not found. ```typescript -function getCalendar(name: string): CalendarEngine +function getCalendar(name: string): CalendarEngine; ``` Throws `Error('Unknown Hijri calendar: "name". Available: ...')`. @@ -93,7 +83,7 @@ Throws `Error('Unknown Hijri calendar: "name". Available: ...')`. Returns the names of all registered calendars. ```typescript -function listCalendars(): string[] +function listCalendars(): string[]; ``` ## Types @@ -114,11 +104,11 @@ One entry in the Umm al-Qura reference table. ```typescript interface HijriYearRecord { - hy: number; // Hijri year + hy: number; // Hijri year dpm: number; // 12-bit days-per-month bitmask - gy: number; // Gregorian year of 1 Muharram - gm: number; // Gregorian month of 1 Muharram (1-based) - gd: number; // Gregorian day of 1 Muharram + gy: number; // Gregorian year of 1 Muharram + gm: number; // Gregorian month of 1 Muharram (1-based) + gd: number; // Gregorian day of 1 Muharram } ``` @@ -146,15 +136,15 @@ interface ConversionOptions { ## Data exports -| Export | Type | Description | -| --- | --- | --- | +| Export | Type | Description | +| ------------- | ------------------- | ------------------------------------------------------- | | `hDatesTable` | `HijriYearRecord[]` | Full UAQ table, 184 entries (1318-1500 H) plus sentinel | -| `hmLong` | `string[]` | Long month names. Index 0 = Muharram | -| `hmMedium` | `string[]` | Medium month names | -| `hmShort` | `string[]` | Short month codes (3 chars) | -| `hwLong` | `string[]` | Long weekday names. Index 0 = Sunday | -| `hwShort` | `string[]` | Short weekday names | -| `hwNumeric` | `number[]` | Weekday numbers, 1 = Sunday, 7 = Saturday | +| `hmLong` | `string[]` | Long month names. Index 0 = Muharram | +| `hmMedium` | `string[]` | Medium month names | +| `hmShort` | `string[]` | Short month codes (3 chars) | +| `hwLong` | `string[]` | Long weekday names. Index 0 = Sunday | +| `hwShort` | `string[]` | Short weekday names | +| `hwNumeric` | `number[]` | Weekday numbers, 1 = Sunday, 7 = Saturday | --- diff --git a/.wiki/Architecture.md b/.wiki/Architecture.md index dc2dac8..e098092 100644 --- a/.wiki/Architecture.md +++ b/.wiki/Architecture.md @@ -39,6 +39,7 @@ The Umm al-Qura calendar is the official Islamic calendar of Saudi Arabia. Month ### Data format Each `HijriYearRecord` stores: + - The Gregorian date of 1 Muharram for that Hijri year - A 12-bit `dpm` bitmask: bit `i` (0-indexed) = month length for month `i+1`. Bit 1 = 30 days, bit 0 = 29 days @@ -70,9 +71,11 @@ The Fiqh Council of North America uses a global astronomical criterion rather th ### Criterion If the new moon conjunction occurs before 12:00 noon UTC on calendar day D: + - The new Hijri month begins at midnight starting day D+1. If at or after 12:00 noon UTC: + - The new Hijri month begins at midnight starting day D+2. This makes every Hijri month start deterministic from the astronomical conjunction time. @@ -111,11 +114,11 @@ import { registerCalendar, type CalendarEngine } from 'hijri-core'; // 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; + const MEAN_MONTH_MS = 29.530588861 * 86_400_000; const months = Math.floor((ms - HIJRI_EPOCH_MS) / MEAN_MONTH_MS); - const hy = Math.floor(months / 12) + 1; - const hm = (months % 12) + 1; - const hd = Math.floor(((ms - HIJRI_EPOCH_MS) % MEAN_MONTH_MS) / 86_400_000) + 1; + const hy = Math.floor(months / 12) + 1; + const hm = (months % 12) + 1; + const hd = Math.floor(((ms - HIJRI_EPOCH_MS) % MEAN_MONTH_MS) / 86_400_000) + 1; return { hy, hm: hm <= 0 ? hm + 12 : hm, hd }; } @@ -124,7 +127,7 @@ const arithmeticEngine: CalendarEngine = { toHijri: (date) => hijriFromMs(date.getTime()), toGregorian: (hy, hm, hd) => null, // left as an exercise isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1 && hd <= 30, - daysInMonth: (hy, hm) => (hm % 2 === 1 || hm === 12) ? 30 : 29, + daysInMonth: (hy, hm) => (hm % 2 === 1 || hm === 12 ? 30 : 29), }; registerCalendar('arithmetic', arithmeticEngine); diff --git a/.wiki/Home.md b/.wiki/Home.md index 47fa928..705b8b5 100644 --- a/.wiki/Home.md +++ b/.wiki/Home.md @@ -18,7 +18,7 @@ const greg = toGregorian(1446, 9, 1); // FCNA const hijriFcna = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); -const gregFcna = toGregorian(1446, 9, 1, { calendar: 'fcna' }); +const gregFcna = toGregorian(1446, 9, 1, { calendar: 'fcna' }); ``` ## Custom calendar registration diff --git a/README.md b/README.md index 2fdbf4d..f0ce6f5 100644 --- a/README.md +++ b/README.md @@ -29,11 +29,11 @@ const greg = toGregorian(1446, 9, 1); // FCNA/ISNA calendar const hijriFcna = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); -const gregFcna = toGregorian(1446, 9, 1, { calendar: 'fcna' }); +const gregFcna = toGregorian(1446, 9, 1, { calendar: 'fcna' }); // Validation and month length -isValidHijriDate(1444, 9, 1); // true -daysInHijriMonth(1444, 9); // 29 +isValidHijriDate(1444, 9, 1); // true +daysInHijriMonth(1444, 9); // 29 ``` ### Custom Calendar @@ -43,8 +43,12 @@ 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(); }, + 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, }; @@ -58,34 +62,34 @@ const result = toHijri(new Date(), { calendar: 'my-calendar' }); ### 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` | | +| 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[]` | +| Function | Parameters | Returns | +| -------------------------------- | ------------------------ | ---------------- | +| `registerCalendar(name, engine)` | `string, CalendarEngine` | `void` | +| `getCalendar(name)` | `string` | `CalendarEngine` | +| `listCalendars()` | none | `string[]` | ### Data exports -| Export | Type | Description | -| --- | --- | --- | +| 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) | +| `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 diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..f4be42a --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,10 @@ +import js from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import prettier from 'eslint-config-prettier'; + +export default tseslint.config( + { ignores: ['dist/', 'node_modules/', '*.cjs', '*.mjs'] }, + js.configs.recommended, + ...tseslint.configs.recommended, + prettier, +); diff --git a/package.json b/package.json index 61b72c6..32e5d56 100644 --- a/package.json +++ b/package.json @@ -9,11 +9,20 @@ "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" } + "import": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "require": { + "types": "./dist/index.d.ts", + "default": "./dist/index.cjs" + } } }, - "sideEffects": ["./dist/index.cjs", "./dist/index.mjs"], + "sideEffects": [ + "./dist/index.cjs", + "./dist/index.mjs" + ], "files": [ "dist/index.cjs", "dist/index.mjs", @@ -23,14 +32,19 @@ "CHANGELOG.md", "LICENSE" ], - "engines": { "node": ">=20" }, + "engines": { + "node": ">=20" + }, "packageManager": "pnpm@10.30.1", "scripts": { "build": "tsup", "typecheck": "tsc --noEmit", "pretest": "tsup", - "test": "node test.mjs && node test-cjs.cjs", - "prepublishOnly": "tsup" + "test": "node --test test.mjs && node --test test-cjs.cjs", + "prepublishOnly": "tsup", + "lint": "eslint .", + "format": "prettier --write .", + "format:check": "prettier --check ." }, "keywords": [ "hijri", @@ -45,12 +59,25 @@ "typescript" ], "devDependencies": { - "@types/node": "^22.0.0", + "@eslint/js": "^10.0.1", + "@types/node": "^22.15.0", + "eslint": "^10.0.3", + "eslint-config-prettier": "^10.1.8", + "prettier": "^3.8.1", "tsup": "^8.0.0", - "typescript": "^5.5.0" + "typescript": "^5.5.0", + "typescript-eslint": "^8.56.1" + }, + "publishConfig": { + "access": "public", + "registry": "https://registry.npmjs.org/" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/acamarata/hijri-core.git" }, - "publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" }, - "repository": { "type": "git", "url": "git+https://github.com/acamarata/hijri-core.git" }, "homepage": "https://github.com/acamarata/hijri-core#readme", - "bugs": { "url": "https://github.com/acamarata/hijri-core/issues" } + "bugs": { + "url": "https://github.com/acamarata/hijri-core/issues" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ae0aeb..f6f059c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,449 +5,1214 @@ settings: excludeLinksFromLockfile: false importers: - .: devDependencies: + '@eslint/js': + specifier: ^10.0.1 + version: 10.0.1(eslint@10.0.3) '@types/node': - specifier: ^22.0.0 - version: 22.19.11 + specifier: ^22.15.0 + version: 22.15.0 + eslint: + specifier: ^10.0.3 + version: 10.0.3 + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@10.0.3) + prettier: + specifier: ^3.8.1 + version: 3.8.1 tsup: specifier: ^8.0.0 version: 8.5.1(typescript@5.9.3) typescript: specifier: ^5.5.0 version: 5.9.3 + typescript-eslint: + specifier: ^8.56.1 + version: 8.56.1(eslint@10.0.3)(typescript@5.9.3) packages: - '@esbuild/aix-ppc64@0.27.3': - resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [aix] '@esbuild/android-arm64@0.27.3': - resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==, + } + engines: { node: '>=18' } cpu: [arm64] os: [android] '@esbuild/android-arm@0.27.3': - resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==, + } + engines: { node: '>=18' } cpu: [arm] os: [android] '@esbuild/android-x64@0.27.3': - resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==, + } + engines: { node: '>=18' } cpu: [x64] os: [android] '@esbuild/darwin-arm64@0.27.3': - resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==, + } + engines: { node: '>=18' } cpu: [arm64] os: [darwin] '@esbuild/darwin-x64@0.27.3': - resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==, + } + engines: { node: '>=18' } cpu: [x64] os: [darwin] '@esbuild/freebsd-arm64@0.27.3': - resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==, + } + engines: { node: '>=18' } cpu: [arm64] os: [freebsd] '@esbuild/freebsd-x64@0.27.3': - resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==, + } + engines: { node: '>=18' } cpu: [x64] os: [freebsd] '@esbuild/linux-arm64@0.27.3': - resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==, + } + engines: { node: '>=18' } cpu: [arm64] os: [linux] '@esbuild/linux-arm@0.27.3': - resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==, + } + engines: { node: '>=18' } cpu: [arm] os: [linux] '@esbuild/linux-ia32@0.27.3': - resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==, + } + engines: { node: '>=18' } cpu: [ia32] os: [linux] '@esbuild/linux-loong64@0.27.3': - resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==, + } + engines: { node: '>=18' } cpu: [loong64] os: [linux] '@esbuild/linux-mips64el@0.27.3': - resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==, + } + engines: { node: '>=18' } cpu: [mips64el] os: [linux] '@esbuild/linux-ppc64@0.27.3': - resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==, + } + engines: { node: '>=18' } cpu: [ppc64] os: [linux] '@esbuild/linux-riscv64@0.27.3': - resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==, + } + engines: { node: '>=18' } cpu: [riscv64] os: [linux] '@esbuild/linux-s390x@0.27.3': - resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==, + } + engines: { node: '>=18' } cpu: [s390x] os: [linux] '@esbuild/linux-x64@0.27.3': - resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==, + } + engines: { node: '>=18' } cpu: [x64] os: [linux] '@esbuild/netbsd-arm64@0.27.3': - resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==, + } + engines: { node: '>=18' } cpu: [arm64] os: [netbsd] '@esbuild/netbsd-x64@0.27.3': - resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==, + } + engines: { node: '>=18' } cpu: [x64] os: [netbsd] '@esbuild/openbsd-arm64@0.27.3': - resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==, + } + engines: { node: '>=18' } cpu: [arm64] os: [openbsd] '@esbuild/openbsd-x64@0.27.3': - resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==, + } + engines: { node: '>=18' } cpu: [x64] os: [openbsd] '@esbuild/openharmony-arm64@0.27.3': - resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==, + } + engines: { node: '>=18' } cpu: [arm64] os: [openharmony] '@esbuild/sunos-x64@0.27.3': - resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==, + } + engines: { node: '>=18' } cpu: [x64] os: [sunos] '@esbuild/win32-arm64@0.27.3': - resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==, + } + engines: { node: '>=18' } cpu: [arm64] os: [win32] '@esbuild/win32-ia32@0.27.3': - resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==, + } + engines: { node: '>=18' } cpu: [ia32] os: [win32] '@esbuild/win32-x64@0.27.3': - resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==, + } + engines: { node: '>=18' } cpu: [x64] os: [win32] + '@eslint-community/eslint-utils@4.9.1': + resolution: + { + integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + '@eslint/config-array@0.23.3': + resolution: + { + integrity: sha512-j+eEWmB6YYLwcNOdlwQ6L2OsptI/LO6lNBuLIqe5R7RetD658HLoF+Mn7LzYmAWWNNzdC6cqP+L6r8ujeYXWLw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@eslint/config-helpers@0.5.3': + resolution: + { + integrity: sha512-lzGN0onllOZCGroKJmRwY6QcEHxbjBw1gwB8SgRSqK8YbbtEXMvKynsXc3553ckIEBxsbMBU7oOZXKIPGZNeZw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@eslint/core@1.1.1': + resolution: + { + integrity: sha512-QUPblTtE51/7/Zhfv8BDwO0qkkzQL7P/aWWbqcf4xWLEYn1oKjdO0gglQBB4GAsu7u6wjijbCmzsUTy6mnk6oQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@eslint/js@10.0.1': + resolution: + { + integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + '@eslint/object-schema@3.0.3': + resolution: + { + integrity: sha512-iM869Pugn9Nsxbh/YHRqYiqd23AmIbxJOcpUMOuWCVNdoQJ5ZtwL6h3t0bcZzJUlC3Dq9jCFCESBZnX0GTv7iQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@eslint/plugin-kit@0.6.1': + resolution: + { + integrity: sha512-iH1B076HoAshH1mLpHMgwdGeTs0CYwL0SPMkGuSebZrwBp16v415e9NZXg2jtrqPVQjf6IANe2Vtlr5KswtcZQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + '@humanfs/core@0.19.1': + resolution: + { + integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==, + } + engines: { node: '>=18.18.0' } + + '@humanfs/node@0.16.7': + resolution: + { + integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==, + } + engines: { node: '>=18.18.0' } + + '@humanwhocodes/module-importer@1.0.1': + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: '>=12.22' } + + '@humanwhocodes/retry@0.4.3': + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, + } + engines: { node: '>=18.18' } + '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + resolution: + { + integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==, + } '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + resolution: + { + integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==, + } + engines: { node: '>=6.0.0' } '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + resolution: + { + integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==, + } '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + resolution: + { + integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==, + } '@rollup/rollup-android-arm-eabi@4.59.0': - resolution: {integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==} + resolution: + { + integrity: sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==, + } cpu: [arm] os: [android] '@rollup/rollup-android-arm64@4.59.0': - resolution: {integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==} + resolution: + { + integrity: sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==, + } cpu: [arm64] os: [android] '@rollup/rollup-darwin-arm64@4.59.0': - resolution: {integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==} + resolution: + { + integrity: sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==, + } cpu: [arm64] os: [darwin] '@rollup/rollup-darwin-x64@4.59.0': - resolution: {integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==} + resolution: + { + integrity: sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==, + } cpu: [x64] os: [darwin] '@rollup/rollup-freebsd-arm64@4.59.0': - resolution: {integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==} + resolution: + { + integrity: sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==, + } cpu: [arm64] os: [freebsd] '@rollup/rollup-freebsd-x64@4.59.0': - resolution: {integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==} + resolution: + { + integrity: sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==, + } cpu: [x64] os: [freebsd] '@rollup/rollup-linux-arm-gnueabihf@4.59.0': - resolution: {integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==} + resolution: + { + integrity: sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==, + } cpu: [arm] os: [linux] libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.59.0': - resolution: {integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==} + resolution: + { + integrity: sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==, + } cpu: [arm] os: [linux] libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.59.0': - resolution: {integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==} + resolution: + { + integrity: sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==, + } cpu: [arm64] os: [linux] libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.59.0': - resolution: {integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==} + resolution: + { + integrity: sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==, + } cpu: [arm64] os: [linux] libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.59.0': - resolution: {integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==} + resolution: + { + integrity: sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==, + } cpu: [loong64] os: [linux] libc: [glibc] '@rollup/rollup-linux-loong64-musl@4.59.0': - resolution: {integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==} + resolution: + { + integrity: sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==, + } cpu: [loong64] os: [linux] libc: [musl] '@rollup/rollup-linux-ppc64-gnu@4.59.0': - resolution: {integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==} + resolution: + { + integrity: sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==, + } cpu: [ppc64] os: [linux] libc: [glibc] '@rollup/rollup-linux-ppc64-musl@4.59.0': - resolution: {integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==} + resolution: + { + integrity: sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==, + } cpu: [ppc64] os: [linux] libc: [musl] '@rollup/rollup-linux-riscv64-gnu@4.59.0': - resolution: {integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==} + resolution: + { + integrity: sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==, + } cpu: [riscv64] os: [linux] libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.59.0': - resolution: {integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==} + resolution: + { + integrity: sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==, + } cpu: [riscv64] os: [linux] libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.59.0': - resolution: {integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==} + resolution: + { + integrity: sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==, + } cpu: [s390x] os: [linux] libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.59.0': - resolution: {integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==} + resolution: + { + integrity: sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==, + } cpu: [x64] os: [linux] libc: [glibc] '@rollup/rollup-linux-x64-musl@4.59.0': - resolution: {integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==} + resolution: + { + integrity: sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==, + } cpu: [x64] os: [linux] libc: [musl] '@rollup/rollup-openbsd-x64@4.59.0': - resolution: {integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==} + resolution: + { + integrity: sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==, + } cpu: [x64] os: [openbsd] '@rollup/rollup-openharmony-arm64@4.59.0': - resolution: {integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==} + resolution: + { + integrity: sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==, + } cpu: [arm64] os: [openharmony] '@rollup/rollup-win32-arm64-msvc@4.59.0': - resolution: {integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==} + resolution: + { + integrity: sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==, + } cpu: [arm64] os: [win32] '@rollup/rollup-win32-ia32-msvc@4.59.0': - resolution: {integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==} + resolution: + { + integrity: sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==, + } cpu: [ia32] os: [win32] '@rollup/rollup-win32-x64-gnu@4.59.0': - resolution: {integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==} + resolution: + { + integrity: sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==, + } cpu: [x64] os: [win32] '@rollup/rollup-win32-x64-msvc@4.59.0': - resolution: {integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==} + resolution: + { + integrity: sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==, + } cpu: [x64] os: [win32] - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/esrecurse@4.3.1': + resolution: + { + integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, + } - '@types/node@22.19.11': - resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==} + '@types/estree@1.0.8': + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + '@types/json-schema@7.0.15': + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + '@types/node@22.15.0': + resolution: + { + integrity: sha512-99S8dWD2DkeE6PBaEDw+In3aar7hdoBvjyJMR6vaKBTzpvR0P00ClzJMOoVrj9D2+Sy/YCwACYHnBTpMhg1UCA==, + } + + '@typescript-eslint/eslint-plugin@8.56.1': + resolution: + { + integrity: sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + '@typescript-eslint/parser': ^8.56.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.56.1': + resolution: + { + integrity: sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.56.1': + resolution: + { + integrity: sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.56.1': + resolution: + { + integrity: sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/tsconfig-utils@8.56.1': + resolution: + { + integrity: sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.56.1': + resolution: + { + integrity: sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.56.1': + resolution: + { + integrity: sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + '@typescript-eslint/typescript-estree@8.56.1': + resolution: + { + integrity: sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.56.1': + resolution: + { + integrity: sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.56.1': + resolution: + { + integrity: sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.16.0: - resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} - engines: {node: '>=0.4.0'} + resolution: + { + integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==, + } + engines: { node: '>=0.4.0' } hasBin: true + ajv@6.14.0: + resolution: + { + integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==, + } + any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + resolution: + { + integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==, + } + + balanced-match@4.0.4: + resolution: + { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, + } + engines: { node: 18 || 20 || >=22 } + + brace-expansion@5.0.4: + resolution: + { + integrity: sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==, + } + engines: { node: 18 || 20 || >=22 } bundle-require@5.1.0: - resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + resolution: + { + integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } peerDependencies: esbuild: '>=0.18' cac@6.7.14: - resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} - engines: {node: '>=8'} + resolution: + { + integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==, + } + engines: { node: '>=8' } chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + resolution: + { + integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==, + } + engines: { node: '>= 14.16.0' } commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, + } + engines: { node: '>= 6' } confbox@0.1.8: - resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + resolution: + { + integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==, + } consola@3.4.2: - resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} - engines: {node: ^14.18.0 || >=16.10.0} + resolution: + { + integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==, + } + engines: { node: ^14.18.0 || >=16.10.0 } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: '>= 8' } debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: '>=6.0' } peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + esbuild@0.27.3: - resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==, + } + engines: { node: '>=18' } hasBin: true + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: '>=10' } + + eslint-config-prettier@10.1.8: + resolution: + { + integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, + } + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-scope@9.1.2: + resolution: + { + integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@5.0.1: + resolution: + { + integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@10.0.3: + resolution: + { + integrity: sha512-COV33RzXZkqhG9P2rZCFl9ZmJ7WL+gQSCRzE7RhkbclbQPtLAWReL7ysA0Sh4c8Im2U9ynybdR56PV0XcKvqaQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: + { + integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + esquery@1.7.0: + resolution: + { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, + } + engines: { node: '>=0.10' } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: '>=4.0' } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: '>=4.0' } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: '>=0.10.0' } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: '>=12.0.0' } peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true + file-entry-cache@8.0.0: + resolution: + { + integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==, + } + engines: { node: '>=16.0.0' } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: '>=10' } + fix-dts-default-cjs-exports@1.0.1: - resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + resolution: + { + integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==, + } + + flat-cache@4.0.1: + resolution: + { + integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==, + } + engines: { node: '>=16' } + + flatted@3.3.4: + resolution: + { + integrity: sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==, + } fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } os: [darwin] + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: '>=10.13.0' } + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: '>= 4' } + + ignore@7.0.5: + resolution: + { + integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==, + } + engines: { node: '>= 4' } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: '>=0.8.19' } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: '>=0.10.0' } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: '>=0.10.0' } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + joycon@3.1.1: - resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} - engines: {node: '>=10'} + resolution: + { + integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==, + } + engines: { node: '>=10' } + + json-buffer@3.0.1: + resolution: + { + integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + keyv@4.5.4: + resolution: + { + integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==, + } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: '>= 0.8.0' } lilconfig@3.1.3: - resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} - engines: {node: '>=14'} + resolution: + { + integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==, + } + engines: { node: '>=14' } lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + resolution: + { + integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, + } load-tsconfig@0.2.5: - resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + resolution: + { + integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: '>=10' } magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + resolution: + { + integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==, + } + + minimatch@10.2.4: + resolution: + { + integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==, + } + engines: { node: 18 || 20 || >=22 } mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + resolution: + { + integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==, + } ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } mz@2.7.0: - resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + resolution: + { + integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==, + } + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + resolution: + { + integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, + } + engines: { node: '>=0.10.0' } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: '>= 0.8.0' } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: '>=10' } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: '>=10' } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: '>=8' } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: '>=8' } pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + resolution: + { + integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==, + } picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: '>=12' } pirates@4.0.7: - resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} - engines: {node: '>= 6'} + resolution: + { + integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==, + } + engines: { node: '>= 6' } pkg-types@1.3.1: - resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + resolution: + { + integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==, + } postcss-load-config@6.0.1: - resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} - engines: {node: '>= 18'} + resolution: + { + integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==, + } + engines: { node: '>= 18' } peerDependencies: jiti: '>=1.21.0' postcss: '>=8.0.9' @@ -463,52 +1228,141 @@ packages: yaml: optional: true - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: '>= 0.8.0' } - resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - - rollup@4.59.0: - resolution: {integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} + prettier@3.8.1: + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, + } + engines: { node: '>=14' } hasBin: true + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: '>=6' } + + readdirp@4.1.2: + resolution: + { + integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==, + } + engines: { node: '>= 14.18.0' } + + resolve-from@5.0.0: + resolution: + { + integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, + } + engines: { node: '>=8' } + + rollup@4.59.0: + resolution: + { + integrity: sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==, + } + engines: { node: '>=18.0.0', npm: '>=8.0.0' } + hasBin: true + + semver@7.7.4: + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, + } + engines: { node: '>=10' } + hasBin: true + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: '>=8' } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: '>=8' } + source-map@0.7.6: - resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} - engines: {node: '>= 12'} + resolution: + { + integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, + } + engines: { node: '>= 12' } sucrase@3.35.1: - resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} - engines: {node: '>=16 || 14 >=14.17'} + resolution: + { + integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==, + } + engines: { node: '>=16 || 14 >=14.17' } hasBin: true thenify-all@1.6.0: - resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} - engines: {node: '>=0.8'} + resolution: + { + integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==, + } + engines: { node: '>=0.8' } thenify@3.3.1: - resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + resolution: + { + integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==, + } tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + resolution: + { + integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==, + } tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} - engines: {node: '>=12.0.0'} + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: '>=12.0.0' } tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + resolution: + { + integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==, + } hasBin: true + ts-api-utils@2.4.0: + resolution: + { + integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==, + } + engines: { node: '>=18.12' } + peerDependencies: + typescript: '>=4.8.4' + ts-interface-checker@0.1.13: - resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + resolution: + { + integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==, + } tsup@8.5.1: - resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} - engines: {node: '>=18'} + resolution: + { + integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==, + } + engines: { node: '>=18' } hasBin: true peerDependencies: '@microsoft/api-extractor': ^7.36.0 @@ -525,19 +1379,72 @@ packages: typescript: optional: true + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: '>= 0.8.0' } + + typescript-eslint@8.56.1: + resolution: + { + integrity: sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.0.0' + typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: '>=14.17' } hasBin: true ufo@1.6.3: - resolution: {integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==} + resolution: + { + integrity: sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==, + } undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + resolution: + { + integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==, + } + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: '>= 8' } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: '>=0.10.0' } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: '>=10' } snapshots: - '@esbuild/aix-ppc64@0.27.3': optional: true @@ -616,6 +1523,51 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true + '@eslint-community/eslint-utils@4.9.1(eslint@10.0.3)': + dependencies: + eslint: 10.0.3 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.23.3': + dependencies: + '@eslint/object-schema': 3.0.3 + debug: 4.4.3 + minimatch: 10.2.4 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.5.3': + dependencies: + '@eslint/core': 1.1.1 + + '@eslint/core@1.1.1': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/js@10.0.1(eslint@10.0.3)': + optionalDependencies: + eslint: 10.0.3 + + '@eslint/object-schema@3.0.3': {} + + '@eslint/plugin-kit@0.6.1': + dependencies: + '@eslint/core': 1.1.1 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -705,16 +1657,128 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.59.0': optional: true + '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} - '@types/node@22.19.11': + '@types/json-schema@7.0.15': {} + + '@types/node@22.15.0': dependencies: undici-types: 6.21.0 + '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/type-utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + eslint: 10.0.3 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.56.1(eslint@10.0.3)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + eslint: 10.0.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + + '@typescript-eslint/tsconfig-utils@8.56.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.56.1(eslint@10.0.3)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + debug: 4.4.3 + eslint: 10.0.3 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.56.1': {} + + '@typescript-eslint/typescript-estree@8.56.1(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.56.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.56.1(typescript@5.9.3) + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/visitor-keys': 8.56.1 + debug: 4.4.3 + minimatch: 10.2.4 + semver: 7.7.4 + tinyglobby: 0.2.15 + ts-api-utils: 2.4.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.56.1(eslint@10.0.3)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) + '@typescript-eslint/scope-manager': 8.56.1 + '@typescript-eslint/types': 8.56.1 + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + eslint: 10.0.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.56.1': + dependencies: + '@typescript-eslint/types': 8.56.1 + eslint-visitor-keys: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + acorn@8.16.0: {} + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + any-promise@1.3.0: {} + balanced-match@4.0.4: {} + + brace-expansion@5.0.4: + dependencies: + balanced-match: 4.0.4 + bundle-require@5.1.0(esbuild@0.27.3): dependencies: esbuild: 0.27.3 @@ -732,10 +1796,18 @@ snapshots: consola@3.4.2: {} + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + debug@4.4.3: dependencies: ms: 2.1.3 + deep-is@0.1.4: {} + esbuild@0.27.3: optionalDependencies: '@esbuild/aix-ppc64': 0.27.3 @@ -765,31 +1837,164 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@10.0.3): + dependencies: + eslint: 10.0.3 + + eslint-scope@9.1.2: + dependencies: + '@types/esrecurse': 4.3.1 + '@types/estree': 1.0.8 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.0.3: + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@10.0.3) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.23.3 + '@eslint/config-helpers': 0.5.3 + '@eslint/core': 1.1.1 + '@eslint/plugin-kit': 0.6.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.4 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: dependencies: magic-string: 0.30.21 mlly: 1.8.0 rollup: 4.59.0 + flat-cache@4.0.1: + dependencies: + flatted: 3.3.4 + keyv: 4.5.4 + + flatted@3.3.4: {} + fsevents@2.3.3: optional: true + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + isexe@2.0.0: {} + joycon@3.1.1: {} + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} load-tsconfig@0.2.5: {} + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + minimatch@10.2.4: + dependencies: + brace-expansion: 5.0.4 + mlly@1.8.0: dependencies: acorn: 8.16.0 @@ -805,8 +2010,31 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + natural-compare@1.4.0: {} + object-assign@4.1.1: {} + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -825,6 +2053,12 @@ snapshots: dependencies: lilconfig: 3.1.3 + prelude-ls@1.2.1: {} + + prettier@3.8.1: {} + + punycode@2.3.1: {} + readdirp@4.1.2: {} resolve-from@5.0.0: {} @@ -860,6 +2094,14 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.59.0 fsevents: 2.3.3 + semver@7.7.4: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + source-map@0.7.6: {} sucrase@3.35.1: @@ -889,6 +2131,10 @@ snapshots: tree-kill@1.2.2: {} + ts-api-utils@2.4.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + ts-interface-checker@0.1.13: {} tsup@8.5.1(typescript@5.9.3): @@ -918,8 +2164,35 @@ snapshots: - tsx - yaml + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.56.1(eslint@10.0.3)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.3)(typescript@5.9.3))(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/parser': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.56.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.56.1(eslint@10.0.3)(typescript@5.9.3) + eslint: 10.0.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + typescript@5.9.3: {} ufo@1.6.3: {} undici-types@6.21.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..03e3d4e --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,5 @@ +/** Milliseconds in one day. */ +export const MS_PER_DAY = 86_400_000; + +/** Number of months in a Hijri year. */ +export const MONTHS_PER_YEAR = 12; diff --git a/src/data/hDates.ts b/src/data/hDates.ts index efcfb6d..0b34106 100644 --- a/src/data/hDates.ts +++ b/src/data/hDates.ts @@ -5,188 +5,188 @@ import type { HijriYearRecord } from '../types'; // bitmask. Bit i (0-indexed from bit 0) corresponds to month i+1: 1 = 30 days, // 0 = 29 days. The final sentinel entry (hy 1501, dpm 0) marks the upper bound. export const hDatesTable: HijriYearRecord[] = [ - { hy: 1318, dpm: 0x02EA, gy: 1900, gm: 4, gd: 30 }, - { hy: 1319, dpm: 0x06E9, gy: 1901, gm: 4, gd: 19 }, - { hy: 1320, dpm: 0x0ED2, gy: 1902, gm: 4, gd: 9 }, - { hy: 1321, dpm: 0x0EA4, gy: 1903, gm: 3, gd: 30 }, - { hy: 1322, dpm: 0x0D4A, gy: 1904, gm: 3, gd: 18 }, - { hy: 1323, dpm: 0x0A96, gy: 1905, gm: 3, gd: 7 }, - { hy: 1324, dpm: 0x0536, gy: 1906, gm: 2, gd: 24 }, - { hy: 1325, dpm: 0x0AB5, gy: 1907, gm: 2, gd: 13 }, - { hy: 1326, dpm: 0x0DAA, gy: 1908, gm: 2, gd: 3 }, - { hy: 1327, dpm: 0x0BA4, gy: 1909, gm: 1, gd: 23 }, - { hy: 1328, dpm: 0x0B49, gy: 1910, gm: 1, gd: 12 }, - { hy: 1329, dpm: 0x0A93, gy: 1911, gm: 1, gd: 1 }, - { hy: 1330, dpm: 0x052B, gy: 1911, gm: 12, gd: 21 }, - { hy: 1331, dpm: 0x0A57, gy: 1912, gm: 12, gd: 9 }, - { hy: 1332, dpm: 0x04B6, gy: 1913, gm: 11, gd: 29 }, - { hy: 1333, dpm: 0x0AB5, gy: 1914, gm: 11, gd: 18 }, - { hy: 1334, dpm: 0x05AA, gy: 1915, gm: 11, gd: 8 }, - { hy: 1335, dpm: 0x0D55, gy: 1916, gm: 10, gd: 27 }, - { hy: 1336, dpm: 0x0D2A, gy: 1917, gm: 10, gd: 17 }, - { hy: 1337, dpm: 0x0A56, gy: 1918, gm: 10, gd: 6 }, - { hy: 1338, dpm: 0x04AE, gy: 1919, gm: 9, gd: 25 }, - { hy: 1339, dpm: 0x095D, gy: 1920, gm: 9, gd: 13 }, - { hy: 1340, dpm: 0x02EC, gy: 1921, gm: 9, gd: 3 }, - { hy: 1341, dpm: 0x06D5, gy: 1922, gm: 8, gd: 23 }, - { hy: 1342, dpm: 0x06AA, gy: 1923, gm: 8, gd: 13 }, - { hy: 1343, dpm: 0x0555, gy: 1924, gm: 8, gd: 1 }, - { hy: 1344, dpm: 0x04AB, gy: 1925, gm: 7, gd: 21 }, - { hy: 1345, dpm: 0x095B, gy: 1926, gm: 7, gd: 10 }, - { hy: 1346, dpm: 0x02BA, gy: 1927, gm: 6, gd: 30 }, - { hy: 1347, dpm: 0x0575, gy: 1928, gm: 6, gd: 18 }, - { hy: 1348, dpm: 0x0BB2, gy: 1929, gm: 6, gd: 8 }, - { hy: 1349, dpm: 0x0764, gy: 1930, gm: 5, gd: 29 }, - { hy: 1350, dpm: 0x0749, gy: 1931, gm: 5, gd: 18 }, - { hy: 1351, dpm: 0x0655, gy: 1932, gm: 5, gd: 6 }, - { hy: 1352, dpm: 0x02AB, gy: 1933, gm: 4, gd: 25 }, - { hy: 1353, dpm: 0x055B, gy: 1934, gm: 4, gd: 14 }, - { hy: 1354, dpm: 0x0ADA, gy: 1935, gm: 4, gd: 4 }, - { hy: 1355, dpm: 0x06D4, gy: 1936, gm: 3, gd: 24 }, - { hy: 1356, dpm: 0x0EC9, gy: 1937, gm: 3, gd: 13 }, - { hy: 1357, dpm: 0x0D92, gy: 1938, gm: 3, gd: 3 }, - { hy: 1358, dpm: 0x0D25, gy: 1939, gm: 2, gd: 20 }, - { hy: 1359, dpm: 0x0A4D, gy: 1940, gm: 2, gd: 9 }, - { hy: 1360, dpm: 0x02AD, gy: 1941, gm: 1, gd: 28 }, - { hy: 1361, dpm: 0x056D, gy: 1942, gm: 1, gd: 17 }, - { hy: 1362, dpm: 0x0B6A, gy: 1943, gm: 1, gd: 7 }, - { hy: 1363, dpm: 0x0B52, gy: 1943, gm: 12, gd: 28 }, - { hy: 1364, dpm: 0x0AA5, gy: 1944, gm: 12, gd: 16 }, - { hy: 1365, dpm: 0x0A4B, gy: 1945, gm: 12, gd: 5 }, + { hy: 1318, dpm: 0x02ea, gy: 1900, gm: 4, gd: 30 }, + { hy: 1319, dpm: 0x06e9, gy: 1901, gm: 4, gd: 19 }, + { hy: 1320, dpm: 0x0ed2, gy: 1902, gm: 4, gd: 9 }, + { hy: 1321, dpm: 0x0ea4, gy: 1903, gm: 3, gd: 30 }, + { hy: 1322, dpm: 0x0d4a, gy: 1904, gm: 3, gd: 18 }, + { hy: 1323, dpm: 0x0a96, gy: 1905, gm: 3, gd: 7 }, + { hy: 1324, dpm: 0x0536, gy: 1906, gm: 2, gd: 24 }, + { hy: 1325, dpm: 0x0ab5, gy: 1907, gm: 2, gd: 13 }, + { hy: 1326, dpm: 0x0daa, gy: 1908, gm: 2, gd: 3 }, + { hy: 1327, dpm: 0x0ba4, gy: 1909, gm: 1, gd: 23 }, + { hy: 1328, dpm: 0x0b49, gy: 1910, gm: 1, gd: 12 }, + { hy: 1329, dpm: 0x0a93, gy: 1911, gm: 1, gd: 1 }, + { hy: 1330, dpm: 0x052b, gy: 1911, gm: 12, gd: 21 }, + { hy: 1331, dpm: 0x0a57, gy: 1912, gm: 12, gd: 9 }, + { hy: 1332, dpm: 0x04b6, gy: 1913, gm: 11, gd: 29 }, + { hy: 1333, dpm: 0x0ab5, gy: 1914, gm: 11, gd: 18 }, + { hy: 1334, dpm: 0x05aa, gy: 1915, gm: 11, gd: 8 }, + { hy: 1335, dpm: 0x0d55, gy: 1916, gm: 10, gd: 27 }, + { hy: 1336, dpm: 0x0d2a, gy: 1917, gm: 10, gd: 17 }, + { hy: 1337, dpm: 0x0a56, gy: 1918, gm: 10, gd: 6 }, + { hy: 1338, dpm: 0x04ae, gy: 1919, gm: 9, gd: 25 }, + { hy: 1339, dpm: 0x095d, gy: 1920, gm: 9, gd: 13 }, + { hy: 1340, dpm: 0x02ec, gy: 1921, gm: 9, gd: 3 }, + { hy: 1341, dpm: 0x06d5, gy: 1922, gm: 8, gd: 23 }, + { hy: 1342, dpm: 0x06aa, gy: 1923, gm: 8, gd: 13 }, + { hy: 1343, dpm: 0x0555, gy: 1924, gm: 8, gd: 1 }, + { hy: 1344, dpm: 0x04ab, gy: 1925, gm: 7, gd: 21 }, + { hy: 1345, dpm: 0x095b, gy: 1926, gm: 7, gd: 10 }, + { hy: 1346, dpm: 0x02ba, gy: 1927, gm: 6, gd: 30 }, + { hy: 1347, dpm: 0x0575, gy: 1928, gm: 6, gd: 18 }, + { hy: 1348, dpm: 0x0bb2, gy: 1929, gm: 6, gd: 8 }, + { hy: 1349, dpm: 0x0764, gy: 1930, gm: 5, gd: 29 }, + { hy: 1350, dpm: 0x0749, gy: 1931, gm: 5, gd: 18 }, + { hy: 1351, dpm: 0x0655, gy: 1932, gm: 5, gd: 6 }, + { hy: 1352, dpm: 0x02ab, gy: 1933, gm: 4, gd: 25 }, + { hy: 1353, dpm: 0x055b, gy: 1934, gm: 4, gd: 14 }, + { hy: 1354, dpm: 0x0ada, gy: 1935, gm: 4, gd: 4 }, + { hy: 1355, dpm: 0x06d4, gy: 1936, gm: 3, gd: 24 }, + { hy: 1356, dpm: 0x0ec9, gy: 1937, gm: 3, gd: 13 }, + { hy: 1357, dpm: 0x0d92, gy: 1938, gm: 3, gd: 3 }, + { hy: 1358, dpm: 0x0d25, gy: 1939, gm: 2, gd: 20 }, + { hy: 1359, dpm: 0x0a4d, gy: 1940, gm: 2, gd: 9 }, + { hy: 1360, dpm: 0x02ad, gy: 1941, gm: 1, gd: 28 }, + { hy: 1361, dpm: 0x056d, gy: 1942, gm: 1, gd: 17 }, + { hy: 1362, dpm: 0x0b6a, gy: 1943, gm: 1, gd: 7 }, + { hy: 1363, dpm: 0x0b52, gy: 1943, gm: 12, gd: 28 }, + { hy: 1364, dpm: 0x0aa5, gy: 1944, gm: 12, gd: 16 }, + { hy: 1365, dpm: 0x0a4b, gy: 1945, gm: 12, gd: 5 }, { hy: 1366, dpm: 0x0497, gy: 1946, gm: 11, gd: 24 }, { hy: 1367, dpm: 0x0937, gy: 1947, gm: 11, gd: 13 }, - { hy: 1368, dpm: 0x02B6, gy: 1948, gm: 11, gd: 2 }, + { hy: 1368, dpm: 0x02b6, gy: 1948, gm: 11, gd: 2 }, { hy: 1369, dpm: 0x0575, gy: 1949, gm: 10, gd: 22 }, - { hy: 1370, dpm: 0x0D6A, gy: 1950, gm: 10, gd: 12 }, - { hy: 1371, dpm: 0x0D52, gy: 1951, gm: 10, gd: 2 }, - { hy: 1372, dpm: 0x0A96, gy: 1952, gm: 9, gd: 20 }, - { hy: 1373, dpm: 0x092D, gy: 1953, gm: 9, gd: 9 }, - { hy: 1374, dpm: 0x025D, gy: 1954, gm: 8, gd: 29 }, - { hy: 1375, dpm: 0x04DD, gy: 1955, gm: 8, gd: 18 }, - { hy: 1376, dpm: 0x0ADA, gy: 1956, gm: 8, gd: 7 }, - { hy: 1377, dpm: 0x05D4, gy: 1957, gm: 7, gd: 28 }, - { hy: 1378, dpm: 0x0DA9, gy: 1958, gm: 7, gd: 17 }, - { hy: 1379, dpm: 0x0D52, gy: 1959, gm: 7, gd: 7 }, - { hy: 1380, dpm: 0x0AAA, gy: 1960, gm: 6, gd: 25 }, - { hy: 1381, dpm: 0x04D6, gy: 1961, gm: 6, gd: 14 }, - { hy: 1382, dpm: 0x09B6, gy: 1962, gm: 6, gd: 3 }, - { hy: 1383, dpm: 0x0374, gy: 1963, gm: 5, gd: 24 }, - { hy: 1384, dpm: 0x0769, gy: 1964, gm: 5, gd: 12 }, - { hy: 1385, dpm: 0x0752, gy: 1965, gm: 5, gd: 2 }, - { hy: 1386, dpm: 0x06A5, gy: 1966, gm: 4, gd: 21 }, - { hy: 1387, dpm: 0x054B, gy: 1967, gm: 4, gd: 10 }, - { hy: 1388, dpm: 0x0AAB, gy: 1968, gm: 3, gd: 29 }, - { hy: 1389, dpm: 0x055A, gy: 1969, gm: 3, gd: 19 }, - { hy: 1390, dpm: 0x0AD5, gy: 1970, gm: 3, gd: 8 }, - { hy: 1391, dpm: 0x0DD2, gy: 1971, gm: 2, gd: 26 }, - { hy: 1392, dpm: 0x0DA4, gy: 1972, gm: 2, gd: 16 }, - { hy: 1393, dpm: 0x0D49, gy: 1973, gm: 2, gd: 4 }, - { hy: 1394, dpm: 0x0A95, gy: 1974, gm: 1, gd: 24 }, - { hy: 1395, dpm: 0x052D, gy: 1975, gm: 1, gd: 13 }, - { hy: 1396, dpm: 0x0A5D, gy: 1976, gm: 1, gd: 2 }, - { hy: 1397, dpm: 0x055A, gy: 1976, gm: 12, gd: 22 }, - { hy: 1398, dpm: 0x0AD5, gy: 1977, gm: 12, gd: 11 }, - { hy: 1399, dpm: 0x06AA, gy: 1978, gm: 12, gd: 1 }, + { hy: 1370, dpm: 0x0d6a, gy: 1950, gm: 10, gd: 12 }, + { hy: 1371, dpm: 0x0d52, gy: 1951, gm: 10, gd: 2 }, + { hy: 1372, dpm: 0x0a96, gy: 1952, gm: 9, gd: 20 }, + { hy: 1373, dpm: 0x092d, gy: 1953, gm: 9, gd: 9 }, + { hy: 1374, dpm: 0x025d, gy: 1954, gm: 8, gd: 29 }, + { hy: 1375, dpm: 0x04dd, gy: 1955, gm: 8, gd: 18 }, + { hy: 1376, dpm: 0x0ada, gy: 1956, gm: 8, gd: 7 }, + { hy: 1377, dpm: 0x05d4, gy: 1957, gm: 7, gd: 28 }, + { hy: 1378, dpm: 0x0da9, gy: 1958, gm: 7, gd: 17 }, + { hy: 1379, dpm: 0x0d52, gy: 1959, gm: 7, gd: 7 }, + { hy: 1380, dpm: 0x0aaa, gy: 1960, gm: 6, gd: 25 }, + { hy: 1381, dpm: 0x04d6, gy: 1961, gm: 6, gd: 14 }, + { hy: 1382, dpm: 0x09b6, gy: 1962, gm: 6, gd: 3 }, + { hy: 1383, dpm: 0x0374, gy: 1963, gm: 5, gd: 24 }, + { hy: 1384, dpm: 0x0769, gy: 1964, gm: 5, gd: 12 }, + { hy: 1385, dpm: 0x0752, gy: 1965, gm: 5, gd: 2 }, + { hy: 1386, dpm: 0x06a5, gy: 1966, gm: 4, gd: 21 }, + { hy: 1387, dpm: 0x054b, gy: 1967, gm: 4, gd: 10 }, + { hy: 1388, dpm: 0x0aab, gy: 1968, gm: 3, gd: 29 }, + { hy: 1389, dpm: 0x055a, gy: 1969, gm: 3, gd: 19 }, + { hy: 1390, dpm: 0x0ad5, gy: 1970, gm: 3, gd: 8 }, + { hy: 1391, dpm: 0x0dd2, gy: 1971, gm: 2, gd: 26 }, + { hy: 1392, dpm: 0x0da4, gy: 1972, gm: 2, gd: 16 }, + { hy: 1393, dpm: 0x0d49, gy: 1973, gm: 2, gd: 4 }, + { hy: 1394, dpm: 0x0a95, gy: 1974, gm: 1, gd: 24 }, + { hy: 1395, dpm: 0x052d, gy: 1975, gm: 1, gd: 13 }, + { hy: 1396, dpm: 0x0a5d, gy: 1976, gm: 1, gd: 2 }, + { hy: 1397, dpm: 0x055a, gy: 1976, gm: 12, gd: 22 }, + { hy: 1398, dpm: 0x0ad5, gy: 1977, gm: 12, gd: 11 }, + { hy: 1399, dpm: 0x06aa, gy: 1978, gm: 12, gd: 1 }, { hy: 1400, dpm: 0x0695, gy: 1979, gm: 11, gd: 20 }, - { hy: 1401, dpm: 0x052B, gy: 1980, gm: 11, gd: 8 }, - { hy: 1402, dpm: 0x0A57, gy: 1981, gm: 10, gd: 28 }, - { hy: 1403, dpm: 0x04AE, gy: 1982, gm: 10, gd: 18 }, - { hy: 1404, dpm: 0x0976, gy: 1983, gm: 10, gd: 7 }, - { hy: 1405, dpm: 0x056C, gy: 1984, gm: 9, gd: 26 }, - { hy: 1406, dpm: 0x0B55, gy: 1985, gm: 9, gd: 15 }, - { hy: 1407, dpm: 0x0AAA, gy: 1986, gm: 9, gd: 5 }, - { hy: 1408, dpm: 0x0A55, gy: 1987, gm: 8, gd: 25 }, - { hy: 1409, dpm: 0x04AD, gy: 1988, gm: 8, gd: 13 }, - { hy: 1410, dpm: 0x095D, gy: 1989, gm: 8, gd: 2 }, - { hy: 1411, dpm: 0x02DA, gy: 1990, gm: 7, gd: 23 }, - { hy: 1412, dpm: 0x05D9, gy: 1991, gm: 7, gd: 12 }, - { hy: 1413, dpm: 0x0DB2, gy: 1992, gm: 7, gd: 1 }, - { hy: 1414, dpm: 0x0BA4, gy: 1993, gm: 6, gd: 21 }, - { hy: 1415, dpm: 0x0B4A, gy: 1994, gm: 6, gd: 10 }, - { hy: 1416, dpm: 0x0A55, gy: 1995, gm: 5, gd: 30 }, - { hy: 1417, dpm: 0x02B5, gy: 1996, gm: 5, gd: 18 }, - { hy: 1418, dpm: 0x0575, gy: 1997, gm: 5, gd: 7 }, - { hy: 1419, dpm: 0x0B6A, gy: 1998, gm: 4, gd: 27 }, - { hy: 1420, dpm: 0x0BD2, gy: 1999, gm: 4, gd: 17 }, - { hy: 1421, dpm: 0x0BC4, gy: 2000, gm: 4, gd: 6 }, - { hy: 1422, dpm: 0x0B89, gy: 2001, gm: 3, gd: 26 }, - { hy: 1423, dpm: 0x0A95, gy: 2002, gm: 3, gd: 15 }, - { hy: 1424, dpm: 0x052D, gy: 2003, gm: 3, gd: 4 }, - { hy: 1425, dpm: 0x05AD, gy: 2004, gm: 2, gd: 21 }, - { hy: 1426, dpm: 0x0B6A, gy: 2005, gm: 2, gd: 10 }, - { hy: 1427, dpm: 0x06D4, gy: 2006, gm: 1, gd: 31 }, - { hy: 1428, dpm: 0x0DC9, gy: 2007, gm: 1, gd: 20 }, - { hy: 1429, dpm: 0x0D92, gy: 2008, gm: 1, gd: 10 }, - { hy: 1430, dpm: 0x0AA6, gy: 2008, gm: 12, gd: 29 }, + { hy: 1401, dpm: 0x052b, gy: 1980, gm: 11, gd: 8 }, + { hy: 1402, dpm: 0x0a57, gy: 1981, gm: 10, gd: 28 }, + { hy: 1403, dpm: 0x04ae, gy: 1982, gm: 10, gd: 18 }, + { hy: 1404, dpm: 0x0976, gy: 1983, gm: 10, gd: 7 }, + { hy: 1405, dpm: 0x056c, gy: 1984, gm: 9, gd: 26 }, + { hy: 1406, dpm: 0x0b55, gy: 1985, gm: 9, gd: 15 }, + { hy: 1407, dpm: 0x0aaa, gy: 1986, gm: 9, gd: 5 }, + { hy: 1408, dpm: 0x0a55, gy: 1987, gm: 8, gd: 25 }, + { hy: 1409, dpm: 0x04ad, gy: 1988, gm: 8, gd: 13 }, + { hy: 1410, dpm: 0x095d, gy: 1989, gm: 8, gd: 2 }, + { hy: 1411, dpm: 0x02da, gy: 1990, gm: 7, gd: 23 }, + { hy: 1412, dpm: 0x05d9, gy: 1991, gm: 7, gd: 12 }, + { hy: 1413, dpm: 0x0db2, gy: 1992, gm: 7, gd: 1 }, + { hy: 1414, dpm: 0x0ba4, gy: 1993, gm: 6, gd: 21 }, + { hy: 1415, dpm: 0x0b4a, gy: 1994, gm: 6, gd: 10 }, + { hy: 1416, dpm: 0x0a55, gy: 1995, gm: 5, gd: 30 }, + { hy: 1417, dpm: 0x02b5, gy: 1996, gm: 5, gd: 18 }, + { hy: 1418, dpm: 0x0575, gy: 1997, gm: 5, gd: 7 }, + { hy: 1419, dpm: 0x0b6a, gy: 1998, gm: 4, gd: 27 }, + { hy: 1420, dpm: 0x0bd2, gy: 1999, gm: 4, gd: 17 }, + { hy: 1421, dpm: 0x0bc4, gy: 2000, gm: 4, gd: 6 }, + { hy: 1422, dpm: 0x0b89, gy: 2001, gm: 3, gd: 26 }, + { hy: 1423, dpm: 0x0a95, gy: 2002, gm: 3, gd: 15 }, + { hy: 1424, dpm: 0x052d, gy: 2003, gm: 3, gd: 4 }, + { hy: 1425, dpm: 0x05ad, gy: 2004, gm: 2, gd: 21 }, + { hy: 1426, dpm: 0x0b6a, gy: 2005, gm: 2, gd: 10 }, + { hy: 1427, dpm: 0x06d4, gy: 2006, gm: 1, gd: 31 }, + { hy: 1428, dpm: 0x0dc9, gy: 2007, gm: 1, gd: 20 }, + { hy: 1429, dpm: 0x0d92, gy: 2008, gm: 1, gd: 10 }, + { hy: 1430, dpm: 0x0aa6, gy: 2008, gm: 12, gd: 29 }, { hy: 1431, dpm: 0x0956, gy: 2009, gm: 12, gd: 18 }, - { hy: 1432, dpm: 0x02AE, gy: 2010, gm: 12, gd: 7 }, - { hy: 1433, dpm: 0x056D, gy: 2011, gm: 11, gd: 26 }, - { hy: 1434, dpm: 0x036A, gy: 2012, gm: 11, gd: 15 }, - { hy: 1435, dpm: 0x0B55, gy: 2013, gm: 11, gd: 4 }, - { hy: 1436, dpm: 0x0AAA, gy: 2014, gm: 10, gd: 25 }, - { hy: 1437, dpm: 0x094D, gy: 2015, gm: 10, gd: 14 }, - { hy: 1438, dpm: 0x049D, gy: 2016, gm: 10, gd: 2 }, - { hy: 1439, dpm: 0x095D, gy: 2017, gm: 9, gd: 21 }, - { hy: 1440, dpm: 0x02BA, gy: 2018, gm: 9, gd: 11 }, - { hy: 1441, dpm: 0x05B5, gy: 2019, gm: 8, gd: 31 }, - { hy: 1442, dpm: 0x05AA, gy: 2020, gm: 8, gd: 20 }, - { hy: 1443, dpm: 0x0D55, gy: 2021, gm: 8, gd: 9 }, - { hy: 1444, dpm: 0x0A9A, gy: 2022, gm: 7, gd: 30 }, - { hy: 1445, dpm: 0x092E, gy: 2023, gm: 7, gd: 19 }, - { hy: 1446, dpm: 0x026E, gy: 2024, gm: 7, gd: 7 }, - { hy: 1447, dpm: 0x055D, gy: 2025, gm: 6, gd: 26 }, - { hy: 1448, dpm: 0x0ADA, gy: 2026, gm: 6, gd: 16 }, - { hy: 1449, dpm: 0x06D4, gy: 2027, gm: 6, gd: 6 }, - { hy: 1450, dpm: 0x06A5, gy: 2028, gm: 5, gd: 25 }, - { hy: 1451, dpm: 0x054B, gy: 2029, gm: 5, gd: 14 }, - { hy: 1452, dpm: 0x0A97, gy: 2030, gm: 5, gd: 3 }, - { hy: 1453, dpm: 0x054E, gy: 2031, gm: 4, gd: 23 }, - { hy: 1454, dpm: 0x0AAE, gy: 2032, gm: 4, gd: 11 }, - { hy: 1455, dpm: 0x05AC, gy: 2033, gm: 4, gd: 1 }, - { hy: 1456, dpm: 0x0BA9, gy: 2034, gm: 3, gd: 21 }, - { hy: 1457, dpm: 0x0D92, gy: 2035, gm: 3, gd: 11 }, - { hy: 1458, dpm: 0x0B25, gy: 2036, gm: 2, gd: 28 }, - { hy: 1459, dpm: 0x064B, gy: 2037, gm: 2, gd: 16 }, - { hy: 1460, dpm: 0x0CAB, gy: 2038, gm: 2, gd: 5 }, - { hy: 1461, dpm: 0x055A, gy: 2039, gm: 1, gd: 26 }, - { hy: 1462, dpm: 0x0B55, gy: 2040, gm: 1, gd: 15 }, - { hy: 1463, dpm: 0x06D2, gy: 2041, gm: 1, gd: 4 }, - { hy: 1464, dpm: 0x0EA5, gy: 2041, gm: 12, gd: 24 }, - { hy: 1465, dpm: 0x0E4A, gy: 2042, gm: 12, gd: 14 }, - { hy: 1466, dpm: 0x0A95, gy: 2043, gm: 12, gd: 3 }, - { hy: 1467, dpm: 0x052D, gy: 2044, gm: 11, gd: 21 }, - { hy: 1468, dpm: 0x0AAD, gy: 2045, gm: 11, gd: 10 }, - { hy: 1469, dpm: 0x036C, gy: 2046, gm: 10, gd: 31 }, + { hy: 1432, dpm: 0x02ae, gy: 2010, gm: 12, gd: 7 }, + { hy: 1433, dpm: 0x056d, gy: 2011, gm: 11, gd: 26 }, + { hy: 1434, dpm: 0x036a, gy: 2012, gm: 11, gd: 15 }, + { hy: 1435, dpm: 0x0b55, gy: 2013, gm: 11, gd: 4 }, + { hy: 1436, dpm: 0x0aaa, gy: 2014, gm: 10, gd: 25 }, + { hy: 1437, dpm: 0x094d, gy: 2015, gm: 10, gd: 14 }, + { hy: 1438, dpm: 0x049d, gy: 2016, gm: 10, gd: 2 }, + { hy: 1439, dpm: 0x095d, gy: 2017, gm: 9, gd: 21 }, + { hy: 1440, dpm: 0x02ba, gy: 2018, gm: 9, gd: 11 }, + { hy: 1441, dpm: 0x05b5, gy: 2019, gm: 8, gd: 31 }, + { hy: 1442, dpm: 0x05aa, gy: 2020, gm: 8, gd: 20 }, + { hy: 1443, dpm: 0x0d55, gy: 2021, gm: 8, gd: 9 }, + { hy: 1444, dpm: 0x0a9a, gy: 2022, gm: 7, gd: 30 }, + { hy: 1445, dpm: 0x092e, gy: 2023, gm: 7, gd: 19 }, + { hy: 1446, dpm: 0x026e, gy: 2024, gm: 7, gd: 7 }, + { hy: 1447, dpm: 0x055d, gy: 2025, gm: 6, gd: 26 }, + { hy: 1448, dpm: 0x0ada, gy: 2026, gm: 6, gd: 16 }, + { hy: 1449, dpm: 0x06d4, gy: 2027, gm: 6, gd: 6 }, + { hy: 1450, dpm: 0x06a5, gy: 2028, gm: 5, gd: 25 }, + { hy: 1451, dpm: 0x054b, gy: 2029, gm: 5, gd: 14 }, + { hy: 1452, dpm: 0x0a97, gy: 2030, gm: 5, gd: 3 }, + { hy: 1453, dpm: 0x054e, gy: 2031, gm: 4, gd: 23 }, + { hy: 1454, dpm: 0x0aae, gy: 2032, gm: 4, gd: 11 }, + { hy: 1455, dpm: 0x05ac, gy: 2033, gm: 4, gd: 1 }, + { hy: 1456, dpm: 0x0ba9, gy: 2034, gm: 3, gd: 21 }, + { hy: 1457, dpm: 0x0d92, gy: 2035, gm: 3, gd: 11 }, + { hy: 1458, dpm: 0x0b25, gy: 2036, gm: 2, gd: 28 }, + { hy: 1459, dpm: 0x064b, gy: 2037, gm: 2, gd: 16 }, + { hy: 1460, dpm: 0x0cab, gy: 2038, gm: 2, gd: 5 }, + { hy: 1461, dpm: 0x055a, gy: 2039, gm: 1, gd: 26 }, + { hy: 1462, dpm: 0x0b55, gy: 2040, gm: 1, gd: 15 }, + { hy: 1463, dpm: 0x06d2, gy: 2041, gm: 1, gd: 4 }, + { hy: 1464, dpm: 0x0ea5, gy: 2041, gm: 12, gd: 24 }, + { hy: 1465, dpm: 0x0e4a, gy: 2042, gm: 12, gd: 14 }, + { hy: 1466, dpm: 0x0a95, gy: 2043, gm: 12, gd: 3 }, + { hy: 1467, dpm: 0x052d, gy: 2044, gm: 11, gd: 21 }, + { hy: 1468, dpm: 0x0aad, gy: 2045, gm: 11, gd: 10 }, + { hy: 1469, dpm: 0x036c, gy: 2046, gm: 10, gd: 31 }, { hy: 1470, dpm: 0x0759, gy: 2047, gm: 10, gd: 20 }, - { hy: 1471, dpm: 0x06D2, gy: 2048, gm: 10, gd: 9 }, - { hy: 1472, dpm: 0x0695, gy: 2049, gm: 9, gd: 28 }, - { hy: 1473, dpm: 0x052D, gy: 2050, gm: 9, gd: 17 }, - { hy: 1474, dpm: 0x0A5B, gy: 2051, gm: 9, gd: 6 }, - { hy: 1475, dpm: 0x04BA, gy: 2052, gm: 8, gd: 26 }, - { hy: 1476, dpm: 0x09BA, gy: 2053, gm: 8, gd: 15 }, - { hy: 1477, dpm: 0x03B4, gy: 2054, gm: 8, gd: 5 }, - { hy: 1478, dpm: 0x0B69, gy: 2055, gm: 7, gd: 25 }, - { hy: 1479, dpm: 0x0B52, gy: 2056, gm: 7, gd: 14 }, - { hy: 1480, dpm: 0x0AA6, gy: 2057, gm: 7, gd: 3 }, - { hy: 1481, dpm: 0x04B6, gy: 2058, gm: 6, gd: 22 }, - { hy: 1482, dpm: 0x096D, gy: 2059, gm: 6, gd: 11 }, - { hy: 1483, dpm: 0x02EC, gy: 2060, gm: 5, gd: 31 }, - { hy: 1484, dpm: 0x06D9, gy: 2061, gm: 5, gd: 20 }, - { hy: 1485, dpm: 0x0EB2, gy: 2062, gm: 5, gd: 10 }, - { hy: 1486, dpm: 0x0D54, gy: 2063, gm: 4, gd: 30 }, - { hy: 1487, dpm: 0x0D2A, gy: 2064, gm: 4, gd: 18 }, - { hy: 1488, dpm: 0x0A56, gy: 2065, gm: 4, gd: 7 }, - { hy: 1489, dpm: 0x04AE, gy: 2066, gm: 3, gd: 27 }, - { hy: 1490, dpm: 0x096D, gy: 2067, gm: 3, gd: 16 }, - { hy: 1491, dpm: 0x0D6A, gy: 2068, gm: 3, gd: 5 }, - { hy: 1492, dpm: 0x0B54, gy: 2069, gm: 2, gd: 23 }, - { hy: 1493, dpm: 0x0B29, gy: 2070, gm: 2, gd: 12 }, - { hy: 1494, dpm: 0x0A93, gy: 2071, gm: 2, gd: 1 }, - { hy: 1495, dpm: 0x052B, gy: 2072, gm: 1, gd: 21 }, - { hy: 1496, dpm: 0x0A57, gy: 2073, gm: 1, gd: 9 }, + { hy: 1471, dpm: 0x06d2, gy: 2048, gm: 10, gd: 9 }, + { hy: 1472, dpm: 0x0695, gy: 2049, gm: 9, gd: 28 }, + { hy: 1473, dpm: 0x052d, gy: 2050, gm: 9, gd: 17 }, + { hy: 1474, dpm: 0x0a5b, gy: 2051, gm: 9, gd: 6 }, + { hy: 1475, dpm: 0x04ba, gy: 2052, gm: 8, gd: 26 }, + { hy: 1476, dpm: 0x09ba, gy: 2053, gm: 8, gd: 15 }, + { hy: 1477, dpm: 0x03b4, gy: 2054, gm: 8, gd: 5 }, + { hy: 1478, dpm: 0x0b69, gy: 2055, gm: 7, gd: 25 }, + { hy: 1479, dpm: 0x0b52, gy: 2056, gm: 7, gd: 14 }, + { hy: 1480, dpm: 0x0aa6, gy: 2057, gm: 7, gd: 3 }, + { hy: 1481, dpm: 0x04b6, gy: 2058, gm: 6, gd: 22 }, + { hy: 1482, dpm: 0x096d, gy: 2059, gm: 6, gd: 11 }, + { hy: 1483, dpm: 0x02ec, gy: 2060, gm: 5, gd: 31 }, + { hy: 1484, dpm: 0x06d9, gy: 2061, gm: 5, gd: 20 }, + { hy: 1485, dpm: 0x0eb2, gy: 2062, gm: 5, gd: 10 }, + { hy: 1486, dpm: 0x0d54, gy: 2063, gm: 4, gd: 30 }, + { hy: 1487, dpm: 0x0d2a, gy: 2064, gm: 4, gd: 18 }, + { hy: 1488, dpm: 0x0a56, gy: 2065, gm: 4, gd: 7 }, + { hy: 1489, dpm: 0x04ae, gy: 2066, gm: 3, gd: 27 }, + { hy: 1490, dpm: 0x096d, gy: 2067, gm: 3, gd: 16 }, + { hy: 1491, dpm: 0x0d6a, gy: 2068, gm: 3, gd: 5 }, + { hy: 1492, dpm: 0x0b54, gy: 2069, gm: 2, gd: 23 }, + { hy: 1493, dpm: 0x0b29, gy: 2070, gm: 2, gd: 12 }, + { hy: 1494, dpm: 0x0a93, gy: 2071, gm: 2, gd: 1 }, + { hy: 1495, dpm: 0x052b, gy: 2072, gm: 1, gd: 21 }, + { hy: 1496, dpm: 0x0a57, gy: 2073, gm: 1, gd: 9 }, { hy: 1497, dpm: 0x0536, gy: 2073, gm: 12, gd: 30 }, - { hy: 1498, dpm: 0x0AB5, gy: 2074, gm: 12, gd: 19 }, - { hy: 1499, dpm: 0x06AA, gy: 2075, gm: 12, gd: 9 }, - { hy: 1500, dpm: 0x0E93, gy: 2076, gm: 11, gd: 27 }, - { hy: 1501, dpm: 0, gy: 2077, gm: 11, gd: 17 }, + { hy: 1498, dpm: 0x0ab5, gy: 2074, gm: 12, gd: 19 }, + { hy: 1499, dpm: 0x06aa, gy: 2075, gm: 12, gd: 9 }, + { hy: 1500, dpm: 0x0e93, gy: 2076, gm: 11, gd: 27 }, + { hy: 1501, dpm: 0, gy: 2077, gm: 11, gd: 17 }, ]; diff --git a/src/engines/fcna.ts b/src/engines/fcna.ts index cfed3a4..8450790 100644 --- a/src/engines/fcna.ts +++ b/src/engines/fcna.ts @@ -8,15 +8,15 @@ // Chapter 49, accurate to within a few minutes for 1000-3000 CE. import { hDatesTable } from '../data/hDates'; +import { MS_PER_DAY, MONTHS_PER_YEAR } from '../constants'; import type { CalendarEngine, HijriDate } from '../types'; // ─── Constants ─────────────────────────────────────────────────────────────── -const SYNODIC = 29.530588861; // Mean synodic month (days) -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; +const SYNODIC = 29.530588861; // Mean synodic month (days) +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 TO_RAD = Math.PI / 180; // Approximate k index of 1 Muharram 1 AH in Meeus numbering. // Islamic epoch JDE ~1948438.5 -> k ~= (1948438.5 - JDE0) / SYNODIC ~= -17037. @@ -25,104 +25,88 @@ const K_EPOCH = -17037; // ─── Meeus Chapter 49: corrected new moon JDE ──────────────────────────────── function newMoonJDE(k: number): number { - const T = k / 1236.85; + const T = k / 1236.85; const T2 = T * T; const T3 = T2 * T; const T4 = T3 * T; - let jde = JDE0 - + SYNODIC * k - + 0.00015437 * T2 - - 0.000000150 * T3 - + 0.00000000073 * T4; + let jde = JDE0 + SYNODIC * k + 0.00015437 * T2 - 0.00000015 * T3 + 0.00000000073 * T4; - const M = (2.5534 - + 29.10535670 * k - - 0.0000014 * T2 - - 0.00000011 * T3) % 360; + const M = (2.5534 + 29.1053567 * k - 0.0000014 * T2 - 0.00000011 * T3) % 360; - const Mprime = (201.5643 - + 385.81693528 * k - + 0.0107582 * T2 - + 0.00001238 * T3 - - 0.000000058 * T4) % 360; + const Mprime = + (201.5643 + 385.81693528 * k + 0.0107582 * T2 + 0.00001238 * T3 - 0.000000058 * T4) % 360; - const F = (160.7108 - + 390.67050284 * k - - 0.0016118 * T2 - - 0.00000227 * T3 - + 0.000000011 * T4) % 360; + const F = + (160.7108 + 390.67050284 * k - 0.0016118 * T2 - 0.00000227 * T3 + 0.000000011 * T4) % 360; - const Omega = (124.7746 - - 1.56375588 * k - + 0.0020672 * T2 - + 0.00000215 * T3) % 360; + const Omega = (124.7746 - 1.56375588 * k + 0.0020672 * T2 + 0.00000215 * T3) % 360; - const E = 1 - 0.002516 * T - 0.0000074 * T2; + const E = 1 - 0.002516 * T - 0.0000074 * T2; const E2 = E * E; - const Mrad = M * TO_RAD; + const Mrad = M * TO_RAD; const Mprad = Mprime * TO_RAD; - const Frad = F * TO_RAD; - const Orad = Omega * TO_RAD; + const Frad = F * TO_RAD; + const Orad = Omega * TO_RAD; jde += - - 0.40720 * Math.sin(Mprad) - + 0.17241 * E * Math.sin(Mrad) - + 0.01608 * Math.sin(2 * Mprad) - + 0.01039 * Math.sin(2 * Frad) - + 0.00739 * E * Math.sin(Mprad - Mrad) - - 0.00514 * E * Math.sin(Mprad + Mrad) - + 0.00208 * E2 * Math.sin(2 * Mrad) - - 0.00111 * Math.sin(Mprad - 2 * Frad) - - 0.00057 * Math.sin(Mprad + 2 * Frad) - + 0.00056 * E * Math.sin(2 * Mprad + Mrad) - - 0.00042 * Math.sin(3 * Mprad) - + 0.00042 * E * Math.sin(Mrad + 2 * Frad) - + 0.00038 * E * Math.sin(Mrad - 2 * Frad) - - 0.00024 * E * Math.sin(2 * Mprad - Mrad) - - 0.00017 * Math.sin(Orad) - - 0.00007 * Math.sin(Mprad + 2 * Mrad) - + 0.00004 * Math.sin(2 * Mprad - 2 * Frad) - + 0.00004 * Math.sin(3 * Mrad) - + 0.00003 * Math.sin(Mprad + Mrad - 2 * Frad) - + 0.00003 * Math.sin(2 * Mprad + 2 * Frad) - - 0.00003 * Math.sin(Mprad + Mrad + 2 * Frad) - + 0.00003 * Math.sin(Mprad - Mrad + 2 * Frad) - - 0.00002 * Math.sin(Mprad - Mrad - 2 * Frad) - - 0.00002 * Math.sin(3 * Mprad + Mrad) - + 0.00002 * Math.sin(4 * Mprad); + -0.4072 * Math.sin(Mprad) + + 0.17241 * E * Math.sin(Mrad) + + 0.01608 * Math.sin(2 * Mprad) + + 0.01039 * Math.sin(2 * Frad) + + 0.00739 * E * Math.sin(Mprad - Mrad) - + 0.00514 * E * Math.sin(Mprad + Mrad) + + 0.00208 * E2 * Math.sin(2 * Mrad) - + 0.00111 * Math.sin(Mprad - 2 * Frad) - + 0.00057 * Math.sin(Mprad + 2 * Frad) + + 0.00056 * E * Math.sin(2 * Mprad + Mrad) - + 0.00042 * Math.sin(3 * Mprad) + + 0.00042 * E * Math.sin(Mrad + 2 * Frad) + + 0.00038 * E * Math.sin(Mrad - 2 * Frad) - + 0.00024 * E * Math.sin(2 * Mprad - Mrad) - + 0.00017 * Math.sin(Orad) - + 0.00007 * Math.sin(Mprad + 2 * Mrad) + + 0.00004 * Math.sin(2 * Mprad - 2 * Frad) + + 0.00004 * Math.sin(3 * Mrad) + + 0.00003 * Math.sin(Mprad + Mrad - 2 * Frad) + + 0.00003 * Math.sin(2 * Mprad + 2 * Frad) - + 0.00003 * Math.sin(Mprad + Mrad + 2 * Frad) + + 0.00003 * Math.sin(Mprad - Mrad + 2 * Frad) - + 0.00002 * Math.sin(Mprad - Mrad - 2 * Frad) - + 0.00002 * Math.sin(3 * Mprad + Mrad) + + 0.00002 * Math.sin(4 * Mprad); - const A1 = (299.77 + 0.107408 * k - 0.009173 * T2) * TO_RAD; - const A2 = (251.88 + 0.016321 * k) * TO_RAD; - const A3 = (251.83 + 26.651886 * k) * TO_RAD; - const A4 = (349.42 + 36.412478 * k) * TO_RAD; - const A5 = ( 84.66 + 18.206239 * k) * TO_RAD; - const A6 = (141.74 + 53.303771 * k) * TO_RAD; - const A7 = (207.14 + 2.453732 * k) * TO_RAD; - const A8 = (154.84 + 7.306860 * k) * TO_RAD; - const A9 = ( 34.52 + 27.261239 * k) * TO_RAD; - const A10 = (207.19 + 0.121824 * k) * TO_RAD; - const A11 = (291.34 + 1.844379 * k) * TO_RAD; + const A1 = (299.77 + 0.107408 * k - 0.009173 * T2) * TO_RAD; + const A2 = (251.88 + 0.016321 * k) * TO_RAD; + const A3 = (251.83 + 26.651886 * k) * TO_RAD; + const A4 = (349.42 + 36.412478 * k) * TO_RAD; + const A5 = (84.66 + 18.206239 * k) * TO_RAD; + const A6 = (141.74 + 53.303771 * k) * TO_RAD; + const A7 = (207.14 + 2.453732 * k) * TO_RAD; + const A8 = (154.84 + 7.30686 * k) * TO_RAD; + const A9 = (34.52 + 27.261239 * k) * TO_RAD; + const A10 = (207.19 + 0.121824 * k) * TO_RAD; + const A11 = (291.34 + 1.844379 * k) * TO_RAD; const A12 = (161.72 + 24.198154 * k) * TO_RAD; const A13 = (239.56 + 25.513099 * k) * TO_RAD; - const A14 = (331.55 + 3.592518 * k) * TO_RAD; + const A14 = (331.55 + 3.592518 * k) * TO_RAD; jde += - + 0.000325 * Math.sin(A1) - + 0.000165 * Math.sin(A2) - + 0.000164 * Math.sin(A3) - + 0.000126 * Math.sin(A4) - + 0.000110 * Math.sin(A5) - + 0.000062 * Math.sin(A6) - + 0.000060 * Math.sin(A7) - + 0.000056 * Math.sin(A8) - + 0.000047 * Math.sin(A9) - + 0.000042 * Math.sin(A10) - + 0.000040 * Math.sin(A11) - + 0.000037 * Math.sin(A12) - + 0.000035 * Math.sin(A13) - + 0.000023 * Math.sin(A14); + +0.000325 * Math.sin(A1) + + 0.000165 * Math.sin(A2) + + 0.000164 * Math.sin(A3) + + 0.000126 * Math.sin(A4) + + 0.00011 * Math.sin(A5) + + 0.000062 * Math.sin(A6) + + 0.00006 * Math.sin(A7) + + 0.000056 * Math.sin(A8) + + 0.000047 * Math.sin(A9) + + 0.000042 * Math.sin(A10) + + 0.00004 * Math.sin(A11) + + 0.000037 * Math.sin(A12) + + 0.000035 * Math.sin(A13) + + 0.000023 * Math.sin(A14); return jde; } @@ -143,15 +127,15 @@ function utcMsToKApprox(ms: number): number { // Searches k0-2 through k0+2 to handle any estimation error. function nearestNewMoonMs(anchorMs: number): number { const k0 = Math.round(utcMsToKApprox(anchorMs)); - let bestMs = 0; + let bestMs = 0; let bestDist = Infinity; for (let k = k0 - 2; k <= k0 + 2; k++) { - const ms = jdeToUtcMs(newMoonJDE(k)); + const ms = jdeToUtcMs(newMoonJDE(k)); const dist = Math.abs(ms - anchorMs); if (dist < bestDist) { bestDist = dist; - bestMs = ms; + bestMs = ms; } } @@ -163,7 +147,7 @@ function nearestNewMoonMs(anchorMs: number): number { // Returns the midnight UTC ms that starts the new FCNA Hijri month. function fcnaCriterionMs(conjMs: number): number { const midnight = Math.floor(conjMs / MS_PER_DAY) * MS_PER_DAY; - const noon = midnight + 12 * 3_600_000; + const noon = midnight + 12 * 3_600_000; return conjMs < noon ? midnight + MS_PER_DAY : midnight + 2 * MS_PER_DAY; } @@ -173,13 +157,17 @@ function fcnaCriterionMs(conjMs: number): number { // In-range years (1318-1500 H): binary-search table, sum dpm day counts. // Out-of-range years: estimate from Islamic epoch + mean synodic month count. function uaqAnchorMs(hy: number, hm: number): number { - let lo = 0, hi = hDatesTable.length - 1, found = -1; + let lo = 0, + hi = hDatesTable.length - 1, + found = -1; while (lo <= hi) { const mid = (lo + hi) >>> 1; const midHy = hDatesTable[mid].hy; - if (midHy === hy) { found = mid; break; } - else if (midHy < hy) lo = mid + 1; - else hi = mid - 1; + if (midHy === hy) { + found = mid; + break; + } else if (midHy < hy) lo = mid + 1; + else hi = mid - 1; } if (found !== -1 && hDatesTable[found].dpm !== 0) { @@ -191,7 +179,7 @@ function uaqAnchorMs(hy: number, hm: number): number { return Date.UTC(r.gy, r.gm - 1, r.gd) + days * MS_PER_DAY; } - const monthsFromEpoch = (hy - 1) * 12 + (hm - 1); + const monthsFromEpoch = (hy - 1) * MONTHS_PER_YEAR + (hm - 1); const kApprox = K_EPOCH + monthsFromEpoch; return jdeToUtcMs(newMoonJDE(kApprox)); } @@ -207,9 +195,12 @@ function fcnaMonthStartMs(hy: number, hm: number): number { // ─── FCNA month length ─────────────────────────────────────────────────────── function fcnaDaysInMonth(hy: number, hm: number): number { + if (hm < 1 || hm > MONTHS_PER_YEAR) { + throw new RangeError(`month must be 1-12, got ${hm}`); + } const thisStart = fcnaMonthStartMs(hy, hm); - const nextHy = hm < 12 ? hy : hy + 1; - const nextHm = hm < 12 ? hm + 1 : 1; + const nextHy = hm < MONTHS_PER_YEAR ? hy : hy + 1; + const nextHm = hm < MONTHS_PER_YEAR ? hm + 1 : 1; const nextStart = fcnaMonthStartMs(nextHy, nextHm); return Math.round((nextStart - thisStart) / MS_PER_DAY); } @@ -229,22 +220,25 @@ function fcnaToHijri(gregorianDate: Date): HijriDate | null { ); const kApprox = utcMsToKApprox(inputMs - 15 * MS_PER_DAY); - const k0 = Math.floor(kApprox); + const k0 = Math.floor(kApprox); for (let ki = k0 - 1; ki <= k0 + 1; ki++) { - const conjMs = jdeToUtcMs(newMoonJDE(ki)); - const monthStart = fcnaCriterionMs(conjMs); + const conjMs = jdeToUtcMs(newMoonJDE(ki)); + const monthStart = fcnaCriterionMs(conjMs); if (monthStart > inputMs) continue; - const nextConjMs = jdeToUtcMs(newMoonJDE(ki + 1)); + const nextConjMs = jdeToUtcMs(newMoonJDE(ki + 1)); const nextMonthStart = fcnaCriterionMs(nextConjMs); if (inputMs < nextMonthStart) { const monthsFromEpoch = ki - K_EPOCH; - let hy = Math.floor(monthsFromEpoch / 12) + 1; - let hm = (monthsFromEpoch % 12) + 1; - if (hm <= 0) { hm += 12; hy--; } + let hy = Math.floor(monthsFromEpoch / MONTHS_PER_YEAR) + 1; + let hm = (monthsFromEpoch % MONTHS_PER_YEAR) + 1; + if (hm <= 0) { + hm += MONTHS_PER_YEAR; + hy--; + } if (hy < 1) return null; const hd = Math.round((inputMs - monthStart) / MS_PER_DAY) + 1; @@ -258,7 +252,7 @@ function fcnaToHijri(gregorianDate: Date): HijriDate | null { // ─── FCNA Hijri -> Gregorian ────────────────────────────────────────────────── function fcnaToGregorian(hy: number, hm: number, hd: number): Date | null { - if (hy < 1 || hm < 1 || hm > 12 || hd < 1) return null; + if (hy < 1 || hm < 1 || hm > MONTHS_PER_YEAR || hd < 1) return null; const days = fcnaDaysInMonth(hy, hm); if (hd > days) return null; const startMs = fcnaMonthStartMs(hy, hm); @@ -268,7 +262,7 @@ function fcnaToGregorian(hy: number, hm: number, hd: number): Date | null { // ─── FCNA validation ───────────────────────────────────────────────────────── function fcnaIsValid(hy: number, hm: number, hd: number): boolean { - if (hy < 1 || hm < 1 || hm > 12 || hd < 1) return false; + if (hy < 1 || hm < 1 || hm > MONTHS_PER_YEAR || hd < 1) return false; return hd <= fcnaDaysInMonth(hy, hm); } @@ -276,8 +270,8 @@ function fcnaIsValid(hy: number, hm: number, hd: number): boolean { export const fcnaEngine: CalendarEngine = { id: 'fcna', - toHijri: fcnaToHijri, + toHijri: fcnaToHijri, toGregorian: fcnaToGregorian, - isValid: fcnaIsValid, + isValid: fcnaIsValid, daysInMonth: fcnaDaysInMonth, }; diff --git a/src/engines/uaq.ts b/src/engines/uaq.ts index f65f66c..fb8f906 100644 --- a/src/engines/uaq.ts +++ b/src/engines/uaq.ts @@ -5,7 +5,32 @@ // a 12-bit days-per-month bitmask. Dates outside that window return null. import { hDatesTable } from '../data/hDates'; -import type { CalendarEngine, HijriDate } from '../types'; +import { MS_PER_DAY, MONTHS_PER_YEAR } from '../constants'; +import type { CalendarEngine, HijriDate, HijriYearRecord } from '../types'; + +/** + * Binary search for a Hijri year entry in the UAQ table. + * + * Returns the entry whose `hy` matches exactly, or null if the year is not + * present in the table. The table covers Hijri years 1318 through 1501 + * (the final entry is a sentinel with dpm === 0). + * + * @param hy - Hijri year to locate + * @returns the matching table entry, or null if not found + */ +function findYearEntry(hy: number): HijriYearRecord | null { + let lo = 0; + let hi = hDatesTable.length - 1; + + while (lo <= hi) { + const mid = (lo + hi) >>> 1; + const midHy = hDatesTable[mid].hy; + if (midHy === hy) return hDatesTable[mid]; + else if (midHy < hy) lo = mid + 1; + else hi = mid - 1; + } + return null; +} // toHijri uses local date components (getFullYear, getMonth, getDate) so that // the calendar-date lookup is timezone-safe regardless of the host environment. @@ -14,11 +39,7 @@ function uaqToHijri(date: Date): HijriDate | null { throw new Error('Invalid Gregorian date'); } - const inputUtc = Date.UTC( - date.getFullYear(), - date.getMonth(), - date.getDate(), - ); + const inputUtc = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()); // Binary search: find the last table entry whose Gregorian start date <= input. let lo = 0; @@ -43,10 +64,10 @@ function uaqToHijri(date: Date): HijriDate | null { const record = hDatesTable[found]; const startUtc = Date.UTC(record.gy, record.gm - 1, record.gd); - let remaining = Math.round((inputUtc - startUtc) / 86_400_000); + let remaining = Math.round((inputUtc - startUtc) / MS_PER_DAY); let hijriMonth = 0; - for (let i = 0; i < 12; i++) { + for (let i = 0; i < MONTHS_PER_YEAR; i++) { const dim = (record.dpm >> i) & 1 ? 30 : 29; if (remaining < dim) { hijriMonth = i + 1; @@ -65,22 +86,9 @@ function uaqToGregorian(hy: number, hm: number, hd: number): Date | null { return null; } - // Binary search on hy. - let lo = 0; - let hi = hDatesTable.length - 1; - let found = -1; + const record = findYearEntry(hy); + if (!record) return null; - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - const midHy = hDatesTable[mid].hy; - if (midHy === hy) { found = mid; break; } - else if (midHy < hy) lo = mid + 1; - else hi = mid - 1; - } - - if (found === -1) return null; - - const record = hDatesTable[found]; let totalDays = 0; for (let i = 0; i < hm - 1; i++) { @@ -88,53 +96,35 @@ function uaqToGregorian(hy: number, hm: number, hd: number): Date | null { } totalDays += hd - 1; - return new Date(Date.UTC(record.gy, record.gm - 1, record.gd) + totalDays * 86_400_000); + return new Date(Date.UTC(record.gy, record.gm - 1, record.gd) + totalDays * MS_PER_DAY); } function uaqIsValid(hy: number, hm: number, hd: number): boolean { - if (hm < 1 || hm > 12 || hd < 1) return false; + if (hm < 1 || hm > MONTHS_PER_YEAR || hd < 1) return false; - let lo = 0; - let hi = hDatesTable.length - 1; - let found = -1; + const record = findYearEntry(hy); + if (!record || record.dpm === 0) return false; - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - const midHy = hDatesTable[mid].hy; - if (midHy === hy) { found = mid; break; } - else if (midHy < hy) lo = mid + 1; - else hi = mid - 1; - } - - if (found === -1 || hDatesTable[found].dpm === 0) return false; - - const dim = (hDatesTable[found].dpm >> (hm - 1)) & 1 ? 30 : 29; + const dim = (record.dpm >> (hm - 1)) & 1 ? 30 : 29; return hd <= dim; } function uaqDaysInMonth(hy: number, hm: number): number { - let lo = 0; - let hi = hDatesTable.length - 1; - let found = -1; - - while (lo <= hi) { - const mid = (lo + hi) >>> 1; - const midHy = hDatesTable[mid].hy; - if (midHy === hy) { found = mid; break; } - else if (midHy < hy) lo = mid + 1; - else hi = mid - 1; + if (hm < 1 || hm > MONTHS_PER_YEAR) { + throw new RangeError(`month must be 1-12, got ${hm}`); } - if (found === -1 || hDatesTable[found].dpm === 0) { + const record = findYearEntry(hy); + if (!record || record.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; + return (record.dpm >> (hm - 1)) & 1 ? 30 : 29; } export const uaqEngine: CalendarEngine = { id: 'uaq', - toHijri: uaqToHijri, + toHijri: uaqToHijri, toGregorian: uaqToGregorian, - isValid: uaqIsValid, + isValid: uaqIsValid, daysInMonth: uaqDaysInMonth, }; diff --git a/src/index.ts b/src/index.ts index 640335e..45b53fd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ -// Register built-in engines at module load. +// Built-in engines are registered at module load so that 'uaq' and 'fcna' are +// available immediately on import. This module-level side effect is intentional +// and documented in the sideEffects field of package.json. import { uaqEngine } from './engines/uaq'; import { fcnaEngine } from './engines/fcna'; import { registerCalendar } from './registry'; @@ -9,6 +11,9 @@ registerCalendar('fcna', fcnaEngine); // Registry export { registerCalendar, getCalendar, listCalendars } from './registry'; +// Constants +export { MS_PER_DAY, MONTHS_PER_YEAR } from './constants'; + // Types export type { HijriDate, HijriYearRecord, CalendarEngine, ConversionOptions } from './types'; @@ -23,6 +28,17 @@ export { hwLong, hwShort, hwNumeric } from './names/weekdays'; import { getCalendar } from './registry'; import type { HijriDate, ConversionOptions } from './types'; +/** + * Convert a Gregorian date to a Hijri date. + * + * Uses the UAQ (Umm al-Qura) calendar by default. Pass `{ calendar: 'fcna' }` + * or any registered calendar name via options to use a different engine. + * + * @param date - a valid JavaScript Date object + * @param options - conversion options (calendar engine selection) + * @returns the corresponding Hijri date, or null if the date is out of range + * @throws {Error} if `date` is not a valid Date instance + */ export function toHijri(date: Date, options?: ConversionOptions): HijriDate | null { if (!(date instanceof Date) || isNaN(date.getTime())) { throw new Error('Invalid Gregorian date'); @@ -30,6 +46,17 @@ export function toHijri(date: Date, options?: ConversionOptions): HijriDate | nu return getCalendar(options?.calendar ?? 'uaq').toHijri(date); } +/** + * Convert a Hijri date to a Gregorian date. + * + * Uses the UAQ calendar by default. + * + * @param hy - Hijri year + * @param hm - Hijri month (1-12) + * @param hd - Hijri day (1-30) + * @param options - conversion options (calendar engine selection) + * @returns a Date in UTC, or null if the input is invalid or out of range + */ export function toGregorian( hy: number, hm: number, @@ -39,6 +66,15 @@ export function toGregorian( return getCalendar(options?.calendar ?? 'uaq').toGregorian(hy, hm, hd); } +/** + * Check whether a Hijri date is valid for the given calendar engine. + * + * @param hy - Hijri year + * @param hm - Hijri month (1-12) + * @param hd - Hijri day (1-30) + * @param options - conversion options (calendar engine selection) + * @returns true if the date is valid + */ export function isValidHijriDate( hy: number, hm: number, @@ -48,10 +84,15 @@ export function isValidHijriDate( return getCalendar(options?.calendar ?? 'uaq').isValid(hy, hm, hd); } -export function daysInHijriMonth( - hy: number, - hm: number, - options?: ConversionOptions, -): number { +/** + * Return the number of days in a given Hijri month. + * + * @param hy - Hijri year + * @param hm - Hijri month (1-12) + * @param options - conversion options (calendar engine selection) + * @returns 29 or 30 + * @throws {RangeError} if the month or year is out of range + */ +export function daysInHijriMonth(hy: number, hm: number, options?: ConversionOptions): number { return getCalendar(options?.calendar ?? 'uaq').daysInMonth(hy, hm); } diff --git a/src/names/months.ts b/src/names/months.ts index 764603c..e454b09 100644 --- a/src/names/months.ts +++ b/src/names/months.ts @@ -2,46 +2,46 @@ // Index 0 = Muharram (month 1), index 11 = Dhul Hijjah (month 12). export const hmLong = [ - "Muharram", // 1 - "Safar", // 2 - "Rabi'l Awwal", // 3 - "Rabi'l Thani", // 4 - "Jumadal Awwal", // 5 - "Jumadal Thani", // 6 - "Rajab", // 7 - "Sha'ban", // 8 - "Ramadan", // 9 - "Shawwal", // 10 - "Dhul Qi'dah", // 11 - "Dhul Hijjah", // 12 + 'Muharram', // 1 + 'Safar', // 2 + "Rabi'l Awwal", // 3 + "Rabi'l Thani", // 4 + 'Jumadal Awwal', // 5 + 'Jumadal Thani', // 6 + 'Rajab', // 7 + "Sha'ban", // 8 + 'Ramadan', // 9 + 'Shawwal', // 10 + "Dhul Qi'dah", // 11 + 'Dhul Hijjah', // 12 ]; export const hmMedium = [ - "Muharram", - "Safar", - "Rabi1", - "Rabi2", - "Jumada1", - "Jumada2", - "Rajab", - "Shaban", - "Ramadan", - "Shawwal", - "Dhul-Qidah", - "Dhul-Hijjah", + 'Muharram', + 'Safar', + 'Rabi1', + 'Rabi2', + 'Jumada1', + 'Jumada2', + 'Rajab', + 'Shaban', + 'Ramadan', + 'Shawwal', + 'Dhul-Qidah', + 'Dhul-Hijjah', ]; export const hmShort = [ - "Muh", - "Saf", - "Ra1", - "Ra2", - "Ju1", - "Ju2", - "Raj", - "Shb", - "Ram", - "Shw", - "DhQ", - "DhH", + 'Muh', + 'Saf', + 'Ra1', + 'Ra2', + 'Ju1', + 'Ju2', + 'Raj', + 'Shb', + 'Ram', + 'Shw', + 'DhQ', + 'DhH', ]; diff --git a/src/names/weekdays.ts b/src/names/weekdays.ts index 5add636..3a51388 100644 --- a/src/names/weekdays.ts +++ b/src/names/weekdays.ts @@ -2,23 +2,23 @@ // Index 0 = Sunday, index 6 = Saturday (matching JS Date.getDay()). export const hwLong = [ - "Yawm al-Ahad", // Sunday - "Yawm al-Ithnayn", // Monday - "Yawm ath-Thulatha'", // Tuesday - "Yawm al-Arba`a'", // Wednesday - "Yawm al-Khamis", // Thursday - "Yawm al-Jum`a", // Friday - "Yawm as-Sabt", // Saturday + 'Yawm al-Ahad', // Sunday + 'Yawm al-Ithnayn', // Monday + "Yawm ath-Thulatha'", // Tuesday + "Yawm al-Arba`a'", // Wednesday + 'Yawm al-Khamis', // Thursday + 'Yawm al-Jum`a', // Friday + 'Yawm as-Sabt', // Saturday ]; export const hwShort = [ - "Ahad", // Sunday - "Ithn", // Monday - "Thul", // Tuesday - "Arba", // Wednesday - "Kham", // Thursday - "Jum`a", // Friday - "Sabt", // Saturday + 'Ahad', // Sunday + 'Ithn', // Monday + 'Thul', // Tuesday + 'Arba', // Wednesday + 'Kham', // Thursday + 'Jum`a', // Friday + 'Sabt', // Saturday ]; // Numeric representation: 1 = Sunday, 7 = Saturday. diff --git a/src/registry.ts b/src/registry.ts index e333a2e..dee6d12 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -2,21 +2,42 @@ import type { CalendarEngine } from './types'; const _engines = new Map(); +/** + * Register a calendar engine under the given name. + * + * Once registered, the engine can be selected via `{ calendar: name }` in any + * conversion function or retrieved directly with {@link getCalendar}. + * + * @param name - unique identifier for the calendar (e.g. 'uaq', 'fcna') + * @param engine - an object implementing the {@link CalendarEngine} interface + */ export function registerCalendar(name: string, engine: CalendarEngine): void { _engines.set(name, engine); } +/** + * Retrieve a registered calendar engine by name. + * + * @param name - the calendar identifier passed to {@link registerCalendar} + * @returns the matching engine + * @throws {Error} if no engine is registered under that name + */ export function getCalendar(name: string): CalendarEngine { const engine = _engines.get(name); if (!engine) { const available = listCalendars().join(', '); throw new Error( - `Unknown Hijri calendar: "${name}". Available: ${available}. Register custom calendars with registerCalendar().` + `Unknown Hijri calendar: "${name}". Available: ${available}. Register custom calendars with registerCalendar().`, ); } return engine; } +/** + * List the names of all registered calendar engines. + * + * @returns an array of calendar names (e.g. ['uaq', 'fcna']) + */ export function listCalendars(): string[] { return Array.from(_engines.keys()); } diff --git a/src/types.ts b/src/types.ts index df28fc1..9a19c3a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -5,11 +5,11 @@ export interface HijriDate { } export interface HijriYearRecord { - hy: number; // Hijri year + 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 (1-based) - gd: number; // Gregorian day of 1 Muharram + gy: number; // Gregorian year of 1 Muharram + gm: number; // Gregorian month of 1 Muharram (1-based) + gd: number; // Gregorian day of 1 Muharram } // Any calendar engine must implement this interface. diff --git a/test-cjs.cjs b/test-cjs.cjs index c354cf2..decfe22 100644 --- a/test-cjs.cjs +++ b/test-cjs.cjs @@ -1,8 +1,9 @@ 'use strict'; // CJS test suite for hijri-core. -// Subset of test.mjs — verifies the CommonJS build works correctly. +// Subset of test.mjs to verify the CommonJS build works correctly. +const { describe, it } = require('node:test'); const assert = require('node:assert/strict'); const { toHijri, @@ -19,126 +20,131 @@ const { hwNumeric, } = require('./dist/index.cjs'); -let passed = 0; -let failed = 0; +// ─── Exports ──────────────────────────────────────────────────────────────── -function test(name, fn) { - try { - fn(); - console.log(`[${name}]... PASS`); - passed++; - } catch (err) { - console.error(`[${name}]... FAIL: ${err.message}`); - failed++; - } -} - -// Exports -test('CJS exports: toHijri is a function', () => { - assert.equal(typeof toHijri, 'function'); -}); -test('CJS exports: toGregorian is a function', () => { - assert.equal(typeof toGregorian, 'function'); -}); -test('CJS exports: hDatesTable is an array', () => { - assert.ok(Array.isArray(hDatesTable)); - assert.ok(hDatesTable.length > 180); -}); -test('CJS exports: hmLong[8] = Ramadan', () => { - assert.equal(hmLong[8], 'Ramadan'); -}); -test('CJS exports: hmShort[8] = Ram', () => { - assert.equal(hmShort[8], 'Ram'); -}); -test('CJS exports: hwLong[4] = Yawm al-Khamis', () => { - assert.equal(hwLong[4], 'Yawm al-Khamis'); -}); -test('CJS exports: hwNumeric[0] = 1', () => { - assert.equal(hwNumeric[0], 1); +describe('CJS exports', () => { + it('toHijri is a function', () => { + assert.equal(typeof toHijri, 'function'); + }); + it('toGregorian is a function', () => { + assert.equal(typeof toGregorian, 'function'); + }); + it('hDatesTable is an array with > 180 entries', () => { + assert.ok(Array.isArray(hDatesTable)); + assert.ok(hDatesTable.length > 180); + }); + it('hmLong[8] = Ramadan', () => { + assert.equal(hmLong[8], 'Ramadan'); + }); + it('hmShort[8] = Ram', () => { + assert.equal(hmShort[8], 'Ram'); + }); + it('hwLong[4] = Yawm al-Khamis', () => { + assert.equal(hwLong[4], 'Yawm al-Khamis'); + }); + it('hwNumeric[0] = 1', () => { + assert.equal(hwNumeric[0], 1); + }); }); -// UAQ conversions -test('CJS UAQ toGregorian: 1444/9/1 = 2023-03-23', () => { - const d = toGregorian(1444, 9, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2023-03-23'); -}); -test('CJS UAQ toGregorian: 1446/9/1 = 2025-03-01', () => { - const d = toGregorian(1446, 9, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); -}); -test('CJS UAQ toHijri: 2023-03-23 = 1444/9/1', () => { - const h = toHijri(new Date(2023, 2, 23, 12)); - assert.ok(h !== null); - assert.equal(h.hy, 1444); - assert.equal(h.hm, 9); - assert.equal(h.hd, 1); -}); -test('CJS UAQ isValid: 1444/9/1 = true', () => { - assert.equal(isValidHijriDate(1444, 9, 1), true); -}); -test('CJS UAQ isValid: 1317/1/1 = false', () => { - assert.equal(isValidHijriDate(1317, 1, 1), false); -}); -test('CJS UAQ daysInMonth: Ramadan 1444 = 29', () => { - assert.equal(daysInHijriMonth(1444, 9), 29); +// ─── UAQ conversions ──────────────────────────────────────────────────────── + +describe('CJS UAQ conversions', () => { + it('toGregorian: 1444/9/1 = 2023-03-23', () => { + const d = toGregorian(1444, 9, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2023-03-23'); + }); + it('toGregorian: 1446/9/1 = 2025-03-01', () => { + const d = toGregorian(1446, 9, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); + }); + it('toHijri: 2023-03-23 = 1444/9/1', () => { + const h = toHijri(new Date(2023, 2, 23, 12)); + assert.ok(h !== null); + assert.equal(h.hy, 1444); + assert.equal(h.hm, 9); + assert.equal(h.hd, 1); + }); + it('isValid: 1444/9/1 = true', () => { + assert.equal(isValidHijriDate(1444, 9, 1), true); + }); + it('isValid: 1317/1/1 = false', () => { + assert.equal(isValidHijriDate(1317, 1, 1), false); + }); + it('daysInMonth: Ramadan 1444 = 29', () => { + assert.equal(daysInHijriMonth(1444, 9), 29); + }); }); -// FCNA conversions -test('CJS FCNA toGregorian: 1446/9/1 = 2025-03-01', () => { - const d = toGregorian(1446, 9, 1, { calendar: 'fcna' }); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); -}); -test('CJS FCNA toHijri: 2025-03-01 = 1446/9/1', () => { - const h = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); - assert.ok(h !== null); - assert.equal(h.hy, 1446); - assert.equal(h.hm, 9); - assert.equal(h.hd, 1); +// ─── Invalid month validation ─────────────────────────────────────────────── + +describe('CJS invalid month', () => { + it('daysInMonth throws for month 0', () => { + assert.throws(() => daysInHijriMonth(1444, 0), /month must be 1-12/); + }); + it('daysInMonth throws for month 13', () => { + assert.throws(() => daysInHijriMonth(1444, 13), /month must be 1-12/); + }); + it('isValid: month 13 = false', () => { + assert.equal(isValidHijriDate(1444, 13, 1), false); + }); }); -// Registry -test('CJS listCalendars includes uaq and fcna', () => { - const cals = listCalendars(); - assert.ok(cals.includes('uaq')); - assert.ok(cals.includes('fcna')); -}); -test('CJS getCalendar throws for unknown', () => { - assert.throws( - () => getCalendar('nope'), - /Unknown Hijri calendar/, - ); +// ─── FCNA conversions ─────────────────────────────────────────────────────── + +describe('CJS FCNA conversions', () => { + it('toGregorian: 1446/9/1 = 2025-03-01', () => { + const d = toGregorian(1446, 9, 1, { calendar: 'fcna' }); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); + }); + it('toHijri: 2025-03-01 = 1446/9/1', () => { + const h = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); + assert.ok(h !== null); + assert.equal(h.hy, 1446); + assert.equal(h.hm, 9); + assert.equal(h.hd, 1); + }); }); -// Custom calendar -test('CJS registerCalendar: custom engine', () => { - const mockEngine = { - id: 'mock-cjs', - toHijri: () => ({ hy: 888, hm: 2, hd: 5 }), - toGregorian: () => new Date(Date.UTC(2001, 0, 1)), - isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1, - daysInMonth: () => 29, - }; - registerCalendar('mock-cjs', mockEngine); +// ─── Registry ─────────────────────────────────────────────────────────────── - const h = toHijri(new Date(2020, 0, 1), { calendar: 'mock-cjs' }); - assert.ok(h !== null); - assert.equal(h.hy, 888); - assert.equal(h.hm, 2); - assert.equal(h.hd, 5); +describe('CJS registry', () => { + it('listCalendars includes uaq and fcna', () => { + const cals = listCalendars(); + assert.ok(cals.includes('uaq')); + assert.ok(cals.includes('fcna')); + }); + it('getCalendar throws for unknown', () => { + assert.throws(() => getCalendar('nope'), /Unknown Hijri calendar/); + }); + it('registerCalendar: custom engine', () => { + const mockEngine = { + id: 'mock-cjs', + toHijri: () => ({ hy: 888, hm: 2, hd: 5 }), + toGregorian: () => new Date(Date.UTC(2001, 0, 1)), + isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1, + daysInMonth: () => 29, + }; + registerCalendar('mock-cjs', mockEngine); + + const h = toHijri(new Date(2020, 0, 1), { calendar: 'mock-cjs' }); + assert.ok(h !== null); + assert.equal(h.hy, 888); + assert.equal(h.hm, 2); + assert.equal(h.hd, 5); + }); }); -// Error cases -test('CJS toHijri throws on non-Date', () => { - assert.throws(() => toHijri('bad'), /Invalid Gregorian date/); -}); -test('CJS toGregorian returns null for out-of-range date', () => { - assert.strictEqual(toGregorian(1317, 1, 1), null); -}); +// ─── Error cases ──────────────────────────────────────────────────────────── -// Summary -const total = passed + failed; -console.log(`\n${total} tests total: ${passed} passed, ${failed} failed`); -if (failed > 0) process.exit(1); +describe('CJS error cases', () => { + it('toHijri throws on non-Date', () => { + assert.throws(() => toHijri('bad'), /Invalid Gregorian date/); + }); + it('toGregorian returns null for out-of-range date', () => { + assert.strictEqual(toGregorian(1317, 1, 1), null); + }); +}); diff --git a/test.mjs b/test.mjs index 451bff0..ac47a6b 100644 --- a/test.mjs +++ b/test.mjs @@ -1,6 +1,6 @@ // ESM test suite for hijri-core. -// Uses Node.js assert — no test framework needed. +import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { toHijri, @@ -19,250 +19,255 @@ import { hwNumeric, } from './dist/index.mjs'; -let passed = 0; -let failed = 0; +// ─── Exports exist ────────────────────────────────────────────────────────── -function test(name, fn) { - try { - fn(); - console.log(`[${name}]... PASS`); - passed++; - } catch (err) { - console.error(`[${name}]... FAIL: ${err.message}`); - failed++; - } -} - -// ─── 1. Exports exist ───────────────────────────────────────────────────────── - -test('exports: toHijri is a function', () => { - assert.equal(typeof toHijri, 'function'); -}); -test('exports: toGregorian is a function', () => { - assert.equal(typeof toGregorian, 'function'); -}); -test('exports: isValidHijriDate is a function', () => { - assert.equal(typeof isValidHijriDate, 'function'); -}); -test('exports: daysInHijriMonth is a function', () => { - assert.equal(typeof daysInHijriMonth, 'function'); -}); -test('exports: registerCalendar is a function', () => { - assert.equal(typeof registerCalendar, 'function'); -}); -test('exports: getCalendar is a function', () => { - assert.equal(typeof getCalendar, 'function'); -}); -test('exports: listCalendars is a function', () => { - assert.equal(typeof listCalendars, 'function'); -}); -test('exports: hDatesTable is an array', () => { - assert.ok(Array.isArray(hDatesTable)); - assert.ok(hDatesTable.length > 180); -}); -test('exports: hmLong has 12 entries', () => { - assert.equal(hmLong.length, 12); -}); -test('exports: hmMedium has 12 entries', () => { - assert.equal(hmMedium.length, 12); -}); -test('exports: hmShort has 12 entries', () => { - assert.equal(hmShort.length, 12); -}); -test('exports: hwLong has 7 entries', () => { - assert.equal(hwLong.length, 7); -}); -test('exports: hwShort has 7 entries', () => { - assert.equal(hwShort.length, 7); -}); -test('exports: hwNumeric has 7 entries', () => { - assert.equal(hwNumeric.length, 7); - assert.deepEqual(hwNumeric, [1, 2, 3, 4, 5, 6, 7]); +describe('exports', () => { + it('toHijri is a function', () => { + assert.equal(typeof toHijri, 'function'); + }); + it('toGregorian is a function', () => { + assert.equal(typeof toGregorian, 'function'); + }); + it('isValidHijriDate is a function', () => { + assert.equal(typeof isValidHijriDate, 'function'); + }); + it('daysInHijriMonth is a function', () => { + assert.equal(typeof daysInHijriMonth, 'function'); + }); + it('registerCalendar is a function', () => { + assert.equal(typeof registerCalendar, 'function'); + }); + it('getCalendar is a function', () => { + assert.equal(typeof getCalendar, 'function'); + }); + it('listCalendars is a function', () => { + assert.equal(typeof listCalendars, 'function'); + }); + it('hDatesTable is an array with > 180 entries', () => { + assert.ok(Array.isArray(hDatesTable)); + assert.ok(hDatesTable.length > 180); + }); + it('hmLong has 12 entries', () => { + assert.equal(hmLong.length, 12); + }); + it('hmMedium has 12 entries', () => { + assert.equal(hmMedium.length, 12); + }); + it('hmShort has 12 entries', () => { + assert.equal(hmShort.length, 12); + }); + it('hwLong has 7 entries', () => { + assert.equal(hwLong.length, 7); + }); + it('hwShort has 7 entries', () => { + assert.equal(hwShort.length, 7); + }); + it('hwNumeric has 7 entries [1..7]', () => { + assert.equal(hwNumeric.length, 7); + assert.deepEqual(hwNumeric, [1, 2, 3, 4, 5, 6, 7]); + }); }); -// ─── 2. UAQ toGregorian ─────────────────────────────────────────────────────── +// ─── UAQ toGregorian ──────────────────────────────────────────────────────── -test('UAQ toGregorian: 1444/9/1 = 2023-03-23', () => { - const d = toGregorian(1444, 9, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2023-03-23'); -}); -test('UAQ toGregorian: 1446/9/1 = 2025-03-01', () => { - const d = toGregorian(1446, 9, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); -}); -test('UAQ toGregorian: 1446/10/1 = 2025-03-30', () => { - const d = toGregorian(1446, 10, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-30'); -}); -test('UAQ toGregorian: 1318/1/1 = 1900-04-30', () => { - const d = toGregorian(1318, 1, 1); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '1900-04-30'); +describe('UAQ toGregorian', () => { + it('1444/9/1 = 2023-03-23', () => { + const d = toGregorian(1444, 9, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2023-03-23'); + }); + it('1446/9/1 = 2025-03-01', () => { + const d = toGregorian(1446, 9, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); + }); + it('1446/10/1 = 2025-03-30', () => { + const d = toGregorian(1446, 10, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-30'); + }); + it('1318/1/1 = 1900-04-30', () => { + const d = toGregorian(1318, 1, 1); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '1900-04-30'); + }); }); -// ─── 3. UAQ toHijri ─────────────────────────────────────────────────────────── +// ─── UAQ toHijri ──────────────────────────────────────────────────────────── -test('UAQ toHijri: 2023-03-23 = 1444/9/1', () => { - const h = toHijri(new Date(2023, 2, 23, 12)); - assert.ok(h !== null); - assert.equal(h.hy, 1444); - assert.equal(h.hm, 9); - assert.equal(h.hd, 1); -}); -test('UAQ toHijri: 2025-03-01 = 1446/9/1', () => { - const h = toHijri(new Date(2025, 2, 1, 12)); - assert.ok(h !== null); - assert.equal(h.hy, 1446); - assert.equal(h.hm, 9); - assert.equal(h.hd, 1); +describe('UAQ toHijri', () => { + it('2023-03-23 = 1444/9/1', () => { + const h = toHijri(new Date(2023, 2, 23, 12)); + assert.ok(h !== null); + assert.equal(h.hy, 1444); + assert.equal(h.hm, 9); + assert.equal(h.hd, 1); + }); + it('2025-03-01 = 1446/9/1', () => { + const h = toHijri(new Date(2025, 2, 1, 12)); + assert.ok(h !== null); + assert.equal(h.hy, 1446); + assert.equal(h.hm, 9); + assert.equal(h.hd, 1); + }); }); -// ─── 4. UAQ isValidHijriDate ────────────────────────────────────────────────── +// ─── UAQ isValidHijriDate ─────────────────────────────────────────────────── -test('UAQ isValid: 1444/9/1 = true', () => { - assert.equal(isValidHijriDate(1444, 9, 1), true); -}); -test('UAQ isValid: 1317/1/1 = false (before table)', () => { - assert.equal(isValidHijriDate(1317, 1, 1), false); -}); -test('UAQ isValid: 1501/1/1 = false (sentinel)', () => { - assert.equal(isValidHijriDate(1501, 1, 1), false); -}); -test('UAQ isValid: month 0 = false', () => { - assert.equal(isValidHijriDate(1444, 0, 1), false); +describe('UAQ isValid', () => { + it('1444/9/1 = true', () => { + assert.equal(isValidHijriDate(1444, 9, 1), true); + }); + it('1317/1/1 = false (before table)', () => { + assert.equal(isValidHijriDate(1317, 1, 1), false); + }); + it('1501/1/1 = false (sentinel)', () => { + assert.equal(isValidHijriDate(1501, 1, 1), false); + }); + it('month 0 = false', () => { + assert.equal(isValidHijriDate(1444, 0, 1), false); + }); + it('month 13 = false', () => { + assert.equal(isValidHijriDate(1444, 13, 1), false); + }); }); -// ─── 5. daysInHijriMonth ────────────────────────────────────────────────────── +// ─── daysInHijriMonth ─────────────────────────────────────────────────────── -test('UAQ daysInMonth: Ramadan 1444 = 29 days', () => { - // 1444 dpm = 0x0A9A; bit 8 (month 9) = (0x0A9A >> 8) & 1 = 0x0A & 1 = 0 -> 29 - assert.equal(daysInHijriMonth(1444, 9), 29); +describe('UAQ daysInMonth', () => { + it('Ramadan 1444 = 29 days', () => { + assert.equal(daysInHijriMonth(1444, 9), 29); + }); + it('throws for month 0', () => { + assert.throws(() => daysInHijriMonth(1444, 0), /month must be 1-12/); + }); + it('throws for month 13', () => { + assert.throws(() => daysInHijriMonth(1444, 13), /month must be 1-12/); + }); }); -// ─── 6. FCNA toGregorian ────────────────────────────────────────────────────── +// ─── FCNA toGregorian ─────────────────────────────────────────────────────── -test('FCNA toGregorian: 1446/9/1 = 2025-03-01', () => { - const d = toGregorian(1446, 9, 1, { calendar: 'fcna' }); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); -}); -test('FCNA toGregorian: 1446/10/1 = 2025-03-30', () => { - const d = toGregorian(1446, 10, 1, { calendar: 'fcna' }); - assert.ok(d instanceof Date); - assert.equal(d.toISOString().slice(0, 10), '2025-03-30'); +describe('FCNA toGregorian', () => { + it('1446/9/1 = 2025-03-01', () => { + const d = toGregorian(1446, 9, 1, { calendar: 'fcna' }); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-01'); + }); + it('1446/10/1 = 2025-03-30', () => { + const d = toGregorian(1446, 10, 1, { calendar: 'fcna' }); + assert.ok(d instanceof Date); + assert.equal(d.toISOString().slice(0, 10), '2025-03-30'); + }); }); -// ─── 7. FCNA toHijri ────────────────────────────────────────────────────────── +// ─── FCNA toHijri ─────────────────────────────────────────────────────────── -test('FCNA toHijri: 2025-03-01 = 1446/9/1', () => { - // Use UTC date for FCNA (criterion is UTC-based). - const h = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); - assert.ok(h !== null); - assert.equal(h.hy, 1446); - assert.equal(h.hm, 9); - assert.equal(h.hd, 1); +describe('FCNA toHijri', () => { + it('2025-03-01 = 1446/9/1', () => { + const h = toHijri(new Date('2025-03-01'), { calendar: 'fcna' }); + assert.ok(h !== null); + assert.equal(h.hy, 1446); + assert.equal(h.hm, 9); + assert.equal(h.hd, 1); + }); }); -// ─── 8. FCNA round-trips ────────────────────────────────────────────────────── +// ─── FCNA round-trips ─────────────────────────────────────────────────────── -test('FCNA round-trip: 1446/9/1 toGregorian->toHijri', () => { - const greg = toGregorian(1446, 9, 1, { calendar: 'fcna' }); - assert.ok(greg !== null); - const hijri = toHijri(greg, { calendar: 'fcna' }); - assert.ok(hijri !== null); - assert.equal(hijri.hy, 1446); - assert.equal(hijri.hm, 9); - assert.equal(hijri.hd, 1); -}); -test('FCNA round-trip: 1446/10/15 toGregorian->toHijri', () => { - const greg = toGregorian(1446, 10, 15, { calendar: 'fcna' }); - assert.ok(greg !== null); - const hijri = toHijri(greg, { calendar: 'fcna' }); - assert.ok(hijri !== null); - assert.equal(hijri.hy, 1446); - assert.equal(hijri.hm, 10); - assert.equal(hijri.hd, 15); +describe('FCNA round-trips', () => { + it('1446/9/1 toGregorian->toHijri', () => { + const greg = toGregorian(1446, 9, 1, { calendar: 'fcna' }); + assert.ok(greg !== null); + const hijri = toHijri(greg, { calendar: 'fcna' }); + assert.ok(hijri !== null); + assert.equal(hijri.hy, 1446); + assert.equal(hijri.hm, 9); + assert.equal(hijri.hd, 1); + }); + it('1446/10/15 toGregorian->toHijri', () => { + const greg = toGregorian(1446, 10, 15, { calendar: 'fcna' }); + assert.ok(greg !== null); + const hijri = toHijri(greg, { calendar: 'fcna' }); + assert.ok(hijri !== null); + assert.equal(hijri.hy, 1446); + assert.equal(hijri.hm, 10); + assert.equal(hijri.hd, 15); + }); }); -// ─── 9. FCNA isValid ────────────────────────────────────────────────────────── +// ─── FCNA isValid ─────────────────────────────────────────────────────────── -test('FCNA isValid: 1/1/1 = true', () => { - assert.equal(isValidHijriDate(1, 1, 1, { calendar: 'fcna' }), true); -}); -test('FCNA isValid: 1600/1/1 = true', () => { - assert.equal(isValidHijriDate(1600, 1, 1, { calendar: 'fcna' }), true); -}); -test('FCNA isValid: 0/1/1 = false', () => { - assert.equal(isValidHijriDate(0, 1, 1, { calendar: 'fcna' }), false); +describe('FCNA isValid', () => { + it('1/1/1 = true', () => { + assert.equal(isValidHijriDate(1, 1, 1, { calendar: 'fcna' }), true); + }); + it('1600/1/1 = true', () => { + assert.equal(isValidHijriDate(1600, 1, 1, { calendar: 'fcna' }), true); + }); + it('0/1/1 = false', () => { + assert.equal(isValidHijriDate(0, 1, 1, { calendar: 'fcna' }), false); + }); }); -// ─── 10. listCalendars ──────────────────────────────────────────────────────── +// ─── FCNA daysInMonth invalid month ───────────────────────────────────────── -test('listCalendars includes uaq and fcna', () => { - const cals = listCalendars(); - assert.ok(cals.includes('uaq')); - assert.ok(cals.includes('fcna')); +describe('FCNA daysInMonth', () => { + it('throws for month 0', () => { + assert.throws(() => daysInHijriMonth(1446, 0, { calendar: 'fcna' }), /month must be 1-12/); + }); + it('throws for month 13', () => { + assert.throws(() => daysInHijriMonth(1446, 13, { calendar: 'fcna' }), /month must be 1-12/); + }); }); -// ─── 11. getCalendar throws for unknown ─────────────────────────────────────── +// ─── Registry ─────────────────────────────────────────────────────────────── -test('getCalendar throws for unknown calendar', () => { - assert.throws( - () => getCalendar('nonexistent'), - /Unknown Hijri calendar/, - ); +describe('registry', () => { + it('listCalendars includes uaq and fcna', () => { + const cals = listCalendars(); + assert.ok(cals.includes('uaq')); + assert.ok(cals.includes('fcna')); + }); + it('getCalendar throws for unknown calendar', () => { + assert.throws(() => getCalendar('nonexistent'), /Unknown Hijri calendar/); + }); + it('registerCalendar: custom engine works', () => { + const mockEngine = { + id: 'mock', + toHijri: (_date) => ({ hy: 999, hm: 1, hd: 1 }), + toGregorian: (_hy, _hm, _hd) => new Date(Date.UTC(2000, 0, 1)), + isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1, + daysInMonth: (_hy, _hm) => 30, + }; + registerCalendar('mock', mockEngine); + + const cals = listCalendars(); + assert.ok(cals.includes('mock')); + + const h = toHijri(new Date(2020, 0, 1), { calendar: 'mock' }); + assert.ok(h !== null); + assert.equal(h.hy, 999); + + const g = toGregorian(1, 1, 1, { calendar: 'mock' }); + assert.ok(g instanceof Date); + assert.equal(g.toISOString().slice(0, 10), '2000-01-01'); + + assert.equal(isValidHijriDate(1, 1, 1, { calendar: 'mock' }), true); + assert.equal(daysInHijriMonth(1, 1, { calendar: 'mock' }), 30); + }); }); -// ─── 12. Custom calendar registration ──────────────────────────────────────── +// ─── Error cases ──────────────────────────────────────────────────────────── -test('registerCalendar: custom engine works', () => { - const mockEngine = { - id: 'mock', - toHijri: (_date) => ({ hy: 999, hm: 1, hd: 1 }), - toGregorian: (_hy, _hm, _hd) => new Date(Date.UTC(2000, 0, 1)), - isValid: (hy, hm, hd) => hy > 0 && hm >= 1 && hm <= 12 && hd >= 1, - daysInMonth: (_hy, _hm) => 30, - }; - registerCalendar('mock', mockEngine); - - const cals = listCalendars(); - assert.ok(cals.includes('mock')); - - const h = toHijri(new Date(2020, 0, 1), { calendar: 'mock' }); - assert.ok(h !== null); - assert.equal(h.hy, 999); - - const g = toGregorian(1, 1, 1, { calendar: 'mock' }); - assert.ok(g instanceof Date); - assert.equal(g.toISOString().slice(0, 10), '2000-01-01'); - - assert.equal(isValidHijriDate(1, 1, 1, { calendar: 'mock' }), true); - assert.equal(daysInHijriMonth(1, 1, { calendar: 'mock' }), 30); +describe('error cases', () => { + it('toHijri throws on non-Date input', () => { + assert.throws(() => toHijri('2023-03-23'), /Invalid Gregorian date/); + }); + it('toHijri throws on invalid Date', () => { + assert.throws(() => toHijri(new Date('invalid')), /Invalid Gregorian date/); + }); + it('UAQ toGregorian returns null for out-of-range date', () => { + assert.strictEqual(toGregorian(1317, 1, 1), null); + }); }); - -// ─── 13. Error cases ────────────────────────────────────────────────────────── - -test('toHijri throws on non-Date input', () => { - assert.throws( - () => toHijri('2023-03-23'), - /Invalid Gregorian date/, - ); -}); -test('toHijri throws on invalid Date', () => { - assert.throws( - () => toHijri(new Date('invalid')), - /Invalid Gregorian date/, - ); -}); -test('UAQ toGregorian returns null for out-of-range date', () => { - assert.strictEqual(toGregorian(1317, 1, 1), null); -}); - -// ─── Summary ───────────────────────────────────────────────────────────────── - -const total = passed + failed; -console.log(`\n${total} tests total: ${passed} passed, ${failed} failed`); -if (failed > 0) process.exit(1); diff --git a/tsconfig.json b/tsconfig.json index e89eff8..85feff4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,9 +9,12 @@ "declaration": true, "declarationMap": true, "sourceMap": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "outDir": "dist", "rootDir": "src", "types": ["node"] }, - "include": ["src"] + "include": ["src"], + "exclude": ["node_modules", "dist"] }