mirror of
https://github.com/acamarata/temporal-hijri.git
synced 2026-06-30 19:04:29 +00:00
refactor: code quality improvements and Temporal Protocol compliance
- Replace O(n) while-loops in dateAdd() with O(1) modular arithmetic - Implement overflow option handling in dateFromFields, yearMonthFromFields, monthDayFromFields - Add fields() method per Temporal Calendar Protocol - Extract shared borrow logic from dateUntil() into borrowHijriDiff helper - Replace magic number 1444 with REFERENCE_YEAR constant - Convert test suites to node:test runner with describe/it blocks - Add tests for dateUntil, dateAdd with days/weeks, overflow reject/constrain, fields(), yearMonthFromFields, monthDayFromFields - Add ESLint + Prettier with typescript-eslint config - Add lint job to CI workflow - Add noImplicitReturns and noFallthroughCasesInSwitch to tsconfig - Disable unused sourcemap generation in tsup - Update .editorconfig to include .mts and .cts extensions - Add missing AI agent dirs to .gitignore
This commit is contained in:
parent
997ce2a097
commit
41956c0dc4
12 changed files with 1180 additions and 226 deletions
|
|
@ -6,7 +6,7 @@ charset = utf-8
|
|||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.{js,mjs,cjs,ts,json,yaml,yml,md}]
|
||||
[*.{js,mjs,cjs,ts,mts,cts,json,yaml,yml,md}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
|
|
|||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
10
.gitignore
vendored
10
.gitignore
vendored
|
|
@ -9,5 +9,15 @@ dist/
|
|||
|
||||
# AI agent directories
|
||||
.aider*
|
||||
.copilot/
|
||||
.cursor/
|
||||
.continue/
|
||||
.windsurf/
|
||||
.codeium/
|
||||
.tabnine/
|
||||
.vscode/*
|
||||
.idea/
|
||||
.codex/
|
||||
.aider/
|
||||
.aider.chat.history.md
|
||||
.gemini/
|
||||
|
|
|
|||
6
.prettierrc
Normal file
6
.prettierrc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"semi": true
|
||||
}
|
||||
20
eslint.config.mjs
Normal file
20
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import eslint from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
|
||||
export default tseslint.config(
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
{
|
||||
ignores: ['dist/', 'node_modules/', 'test.mjs', 'test-cjs.cjs'],
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
44
package.json
44
package.json
|
|
@ -9,8 +9,14 @@
|
|||
"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": false,
|
||||
|
|
@ -23,13 +29,18 @@
|
|||
"CHANGELOG.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"engines": { "node": ">=20" },
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"packageManager": "pnpm@10.30.1",
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "eslint src/",
|
||||
"format": "prettier --write src/ test.mjs test-cjs.cjs eslint.config.mjs tsup.config.ts",
|
||||
"format:check": "prettier --check src/ test.mjs test-cjs.cjs eslint.config.mjs tsup.config.ts",
|
||||
"pretest": "tsup",
|
||||
"test": "node test.mjs && node test-cjs.cjs",
|
||||
"test": "node --test test.mjs && node --test test-cjs.cjs",
|
||||
"prepublishOnly": "tsup"
|
||||
},
|
||||
"keywords": [
|
||||
|
|
@ -45,8 +56,8 @@
|
|||
"typescript"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"hijri-core": "^1.0.0",
|
||||
"@js-temporal/polyfill": "^0.4.0"
|
||||
"@js-temporal/polyfill": "^0.4.0",
|
||||
"hijri-core": "^1.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@js-temporal/polyfill": {
|
||||
|
|
@ -54,14 +65,27 @@
|
|||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@js-temporal/polyfill": "^0.4.4",
|
||||
"@types/node": "^22.0.0",
|
||||
"eslint": "^10.0.3",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"hijri-core": "^1.0.0",
|
||||
"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/temporal-hijri.git"
|
||||
},
|
||||
"publishConfig": { "access": "public", "registry": "https://registry.npmjs.org/" },
|
||||
"repository": { "type": "git", "url": "git+https://github.com/acamarata/temporal-hijri.git" },
|
||||
"homepage": "https://github.com/acamarata/temporal-hijri#readme",
|
||||
"bugs": { "url": "https://github.com/acamarata/temporal-hijri/issues" }
|
||||
"bugs": {
|
||||
"url": "https://github.com/acamarata/temporal-hijri/issues"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
733
pnpm-lock.yaml
733
pnpm-lock.yaml
|
|
@ -8,21 +8,36 @@ importers:
|
|||
|
||||
.:
|
||||
devDependencies:
|
||||
'@eslint/js':
|
||||
specifier: ^10.0.1
|
||||
version: 10.0.1(eslint@10.0.3)
|
||||
'@js-temporal/polyfill':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
'@types/node':
|
||||
specifier: ^22.0.0
|
||||
version: 22.19.11
|
||||
eslint:
|
||||
specifier: ^10.0.3
|
||||
version: 10.0.3
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.8
|
||||
version: 10.1.8(eslint@10.0.3)
|
||||
hijri-core:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
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:
|
||||
|
||||
|
|
@ -182,6 +197,61 @@ packages:
|
|||
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==}
|
||||
|
||||
|
|
@ -337,20 +407,101 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@types/esrecurse@4.3.1':
|
||||
resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==}
|
||||
|
||||
'@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.19.11':
|
||||
resolution: {integrity: sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==}
|
||||
|
||||
'@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'}
|
||||
hasBin: true
|
||||
|
||||
ajv@6.14.0:
|
||||
resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==}
|
||||
|
||||
any-promise@1.3.0:
|
||||
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}
|
||||
|
|
@ -376,6 +527,10 @@ packages:
|
|||
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'}
|
||||
|
|
@ -385,11 +540,75 @@ packages:
|
|||
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'}
|
||||
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'}
|
||||
|
|
@ -399,18 +618,60 @@ packages:
|
|||
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==}
|
||||
|
||||
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}
|
||||
os: [darwin]
|
||||
|
||||
glob-parent@6.0.2:
|
||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
|
||||
hijri-core@1.0.0:
|
||||
resolution: {integrity: sha512-wImBZLBKbEWEEUE1nrc1CFY/uvx4XjGNWYChImJZlswXIVhrBCzSVaj6DP1AU2gUMJ6KDh2ygXo/u/Qx232CXA==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
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'}
|
||||
|
|
@ -418,6 +679,22 @@ packages:
|
|||
jsbi@4.3.2:
|
||||
resolution: {integrity: sha512-9fqMSQbhJykSeii05nxKl4m6Eqn2P6rOlYiS+C5Dr/HPIU/7yZxu5qzbs40tgaFORiw2Amd0mirjxatXYMkIew==}
|
||||
|
||||
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'}
|
||||
|
|
@ -429,9 +706,17 @@ packages:
|
|||
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==}
|
||||
|
||||
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==}
|
||||
|
||||
|
|
@ -441,10 +726,33 @@ packages:
|
|||
mz@2.7.0:
|
||||
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'}
|
||||
|
||||
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==}
|
||||
|
||||
|
|
@ -480,6 +788,19 @@ packages:
|
|||
yaml:
|
||||
optional: true
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
|
||||
engines: {node: '>= 0.8.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'}
|
||||
|
|
@ -493,6 +814,19 @@ packages:
|
|||
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'}
|
||||
|
|
@ -520,6 +854,12 @@ packages:
|
|||
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==}
|
||||
|
||||
|
|
@ -545,6 +885,17 @@ 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'}
|
||||
|
|
@ -556,6 +907,22 @@ packages:
|
|||
undici-types@6.21.0:
|
||||
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':
|
||||
|
|
@ -636,6 +1003,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
|
||||
|
|
@ -730,16 +1142,128 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.59.0':
|
||||
optional: true
|
||||
|
||||
'@types/esrecurse@4.3.1': {}
|
||||
|
||||
'@types/estree@1.0.8': {}
|
||||
|
||||
'@types/json-schema@7.0.15': {}
|
||||
|
||||
'@types/node@22.19.11':
|
||||
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
|
||||
|
|
@ -757,10 +1281,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
|
||||
|
|
@ -790,35 +1322,168 @@ 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
|
||||
|
||||
hijri-core@1.0.0: {}
|
||||
|
||||
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: {}
|
||||
|
||||
jsbi@4.3.2: {}
|
||||
|
||||
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
|
||||
|
|
@ -834,8 +1499,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: {}
|
||||
|
|
@ -854,6 +1542,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: {}
|
||||
|
|
@ -889,6 +1583,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:
|
||||
|
|
@ -918,6 +1620,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: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
|
@ -949,8 +1655,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: {}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,43 @@ import type { CalendarEngine } from 'hijri-core';
|
|||
|
||||
type DateUnit = 'year' | 'years' | 'month' | 'months' | 'week' | 'weeks' | 'day' | 'days';
|
||||
|
||||
/** Reference year for monthDay construction when no year is specified. */
|
||||
const REFERENCE_YEAR = 1444;
|
||||
|
||||
/**
|
||||
* Borrow days/months so that a Hijri date difference has non-negative components.
|
||||
*
|
||||
* Shared by the 'years' and 'months' branches of dateUntil() to avoid
|
||||
* duplicating the borrow logic.
|
||||
*/
|
||||
function borrowHijriDiff(
|
||||
engine: CalendarEngine,
|
||||
years: number,
|
||||
months: number,
|
||||
days: number,
|
||||
h2: { hy: number; hm: number },
|
||||
): { years: number; months: number; days: number } {
|
||||
// Borrow from months when days are negative.
|
||||
if (days < 0) {
|
||||
months--;
|
||||
let borrowHm = h2.hm - 1;
|
||||
let borrowHy = h2.hy;
|
||||
if (borrowHm < 1) {
|
||||
borrowHm = 12;
|
||||
borrowHy--;
|
||||
}
|
||||
days += engine.daysInMonth(borrowHy, borrowHm);
|
||||
}
|
||||
|
||||
// Borrow from years when months are negative.
|
||||
if (months < 0) {
|
||||
years--;
|
||||
months += 12;
|
||||
}
|
||||
|
||||
return { years, months, days };
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class implementing the TC39 Temporal Calendar Protocol for Hijri calendars.
|
||||
*
|
||||
|
|
@ -43,9 +80,7 @@ export class HijriCalendar {
|
|||
const jsDate = new Date(date.year, date.month - 1, date.day);
|
||||
const hijri = this.engine.toHijri(jsDate);
|
||||
if (!hijri) {
|
||||
throw new RangeError(
|
||||
`Date ${date.toString()} is out of range for the ${this.id} calendar`
|
||||
);
|
||||
throw new RangeError(`Date ${date.toString()} is out of range for the ${this.id} calendar`);
|
||||
}
|
||||
return hijri;
|
||||
}
|
||||
|
|
@ -60,7 +95,7 @@ export class HijriCalendar {
|
|||
const greg = this.engine.toGregorian(hy, hm, hd);
|
||||
if (!greg) {
|
||||
throw new RangeError(
|
||||
`Hijri date ${hy}/${hm}/${hd} is out of range for the ${this.id} calendar`
|
||||
`Hijri date ${hy}/${hm}/${hd} is out of range for the ${this.id} calendar`,
|
||||
);
|
||||
}
|
||||
return Temporal.PlainDate.from({
|
||||
|
|
@ -70,6 +105,29 @@ export class HijriCalendar {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the overflow option from a Temporal options bag.
|
||||
* Returns 'constrain' (the default) or 'reject'.
|
||||
*/
|
||||
private resolveOverflow(options?: { overflow?: 'constrain' | 'reject' }): 'constrain' | 'reject' {
|
||||
return options?.overflow ?? 'constrain';
|
||||
}
|
||||
|
||||
/**
|
||||
* Clamp or reject a day value based on the overflow setting.
|
||||
*/
|
||||
private applyOverflow(
|
||||
day: number,
|
||||
maxDay: number,
|
||||
month: number,
|
||||
overflow: 'constrain' | 'reject',
|
||||
): number {
|
||||
if (overflow === 'reject' && day > maxDay) {
|
||||
throw new RangeError(`Day ${day} exceeds ${maxDay} days in month ${month}`);
|
||||
}
|
||||
return Math.min(day, maxDay);
|
||||
}
|
||||
|
||||
// ── Field accessors ───────────────────────────────────────────────────────
|
||||
|
||||
year(date: Temporal.PlainDate): number {
|
||||
|
|
@ -155,13 +213,26 @@ export class HijriCalendar {
|
|||
return 7;
|
||||
}
|
||||
|
||||
// ── Temporal Calendar Protocol: fields() ──────────────────────────────────
|
||||
|
||||
/**
|
||||
* Return the list of fields that the calendar adds to a Temporal object.
|
||||
* Non-era calendars return the input array unchanged.
|
||||
*/
|
||||
fields(fields: string[]): string[] {
|
||||
return fields;
|
||||
}
|
||||
|
||||
// ── Construction from fields ───────────────────────────────────────────────
|
||||
|
||||
dateFromFields(
|
||||
fields: { year: number; month: number; day: number },
|
||||
_options?: { overflow?: 'constrain' | 'reject' }
|
||||
options?: { overflow?: 'constrain' | 'reject' },
|
||||
): Temporal.PlainDate {
|
||||
return this.fromHijri(fields.year, fields.month, fields.day);
|
||||
const overflow = this.resolveOverflow(options);
|
||||
const maxDay = this.engine.daysInMonth(fields.year, fields.month);
|
||||
const day = this.applyOverflow(fields.day, maxDay, fields.month, overflow);
|
||||
return this.fromHijri(fields.year, fields.month, day);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -171,9 +242,16 @@ export class HijriCalendar {
|
|||
*/
|
||||
yearMonthFromFields(
|
||||
fields: { year: number; month: number },
|
||||
_options?: { overflow?: 'constrain' | 'reject' }
|
||||
options?: { overflow?: 'constrain' | 'reject' },
|
||||
): Temporal.PlainYearMonth {
|
||||
const isoDate = this.fromHijri(fields.year, fields.month, 1);
|
||||
const overflow = this.resolveOverflow(options);
|
||||
// Clamp month to 1-12 or reject.
|
||||
const maxMonth = 12;
|
||||
if (overflow === 'reject' && (fields.month < 1 || fields.month > maxMonth)) {
|
||||
throw new RangeError(`Month ${fields.month} is out of range 1-${maxMonth}`);
|
||||
}
|
||||
const month = Math.max(1, Math.min(fields.month, maxMonth));
|
||||
const isoDate = this.fromHijri(fields.year, month, 1);
|
||||
return Temporal.PlainYearMonth.from({
|
||||
year: isoDate.year,
|
||||
month: isoDate.month,
|
||||
|
|
@ -187,12 +265,13 @@ export class HijriCalendar {
|
|||
*/
|
||||
monthDayFromFields(
|
||||
fields: { month: number; day: number; year?: number },
|
||||
_options?: { overflow?: 'constrain' | 'reject' }
|
||||
options?: { overflow?: 'constrain' | 'reject' },
|
||||
): Temporal.PlainMonthDay {
|
||||
// A reference year is needed to resolve the Hijri month/day to an ISO date.
|
||||
// Default to 1444 AH (2022-2023 CE), a recent well-covered year.
|
||||
const year = fields.year ?? 1444;
|
||||
const isoDate = this.fromHijri(year, fields.month, fields.day);
|
||||
const overflow = this.resolveOverflow(options);
|
||||
const year = fields.year ?? REFERENCE_YEAR;
|
||||
const maxDay = this.engine.daysInMonth(year, fields.month);
|
||||
const day = this.applyOverflow(fields.day, maxDay, fields.month, overflow);
|
||||
const isoDate = this.fromHijri(year, fields.month, day);
|
||||
return Temporal.PlainMonthDay.from({
|
||||
month: isoDate.month,
|
||||
day: isoDate.day,
|
||||
|
|
@ -209,25 +288,27 @@ export class HijriCalendar {
|
|||
* fixed 30-day offset). Day and week additions are then applied in ISO space
|
||||
* so that they always represent exact day counts.
|
||||
*
|
||||
* Month normalization uses O(1) modular arithmetic instead of iterative loops.
|
||||
* When the day-of-month exceeds the target month's length after a Hijri-space
|
||||
* adjustment, it is clamped to the last valid day of that month.
|
||||
*/
|
||||
dateAdd(
|
||||
date: Temporal.PlainDate,
|
||||
duration: Temporal.Duration,
|
||||
_options?: { overflow?: 'constrain' | 'reject' }
|
||||
_options?: { overflow?: 'constrain' | 'reject' },
|
||||
): Temporal.PlainDate {
|
||||
const { hy, hm, hd } = this.toHijri(date);
|
||||
|
||||
let newHy = hy + (duration.years ?? 0);
|
||||
let newHm = hm + (duration.months ?? 0);
|
||||
const years = duration.years ?? 0;
|
||||
const months = duration.months ?? 0;
|
||||
|
||||
// Normalize month overflow into years.
|
||||
while (newHm > 12) {
|
||||
newHm -= 12;
|
||||
newHy++;
|
||||
}
|
||||
while (newHm < 1) {
|
||||
// O(1) month normalization via modular arithmetic.
|
||||
const totalMonths = (hy - 1) * 12 + (hm - 1) + years * 12 + months;
|
||||
let newHy = Math.floor(totalMonths / 12) + 1;
|
||||
let newHm = (totalMonths % 12) + 1;
|
||||
|
||||
// Handle negative modulo (JS % can return negative values).
|
||||
if (newHm < 1) {
|
||||
newHm += 12;
|
||||
newHy--;
|
||||
}
|
||||
|
|
@ -252,66 +333,23 @@ export class HijriCalendar {
|
|||
dateUntil(
|
||||
one: Temporal.PlainDate,
|
||||
two: Temporal.PlainDate,
|
||||
options?: { largestUnit?: DateUnit }
|
||||
options?: { largestUnit?: DateUnit },
|
||||
): Temporal.Duration {
|
||||
const largestUnit: DateUnit = options?.largestUnit ?? 'days';
|
||||
|
||||
if (largestUnit === 'years' || largestUnit === 'year') {
|
||||
const h1 = this.toHijri(one);
|
||||
const h2 = this.toHijri(two);
|
||||
|
||||
let years = h2.hy - h1.hy;
|
||||
let months = h2.hm - h1.hm;
|
||||
let days = h2.hd - h1.hd;
|
||||
|
||||
// Borrow from months when days are negative.
|
||||
if (days < 0) {
|
||||
months--;
|
||||
// Add the day count of the previous Hijri month to resolve the borrow.
|
||||
let borrowHm = h2.hm - 1;
|
||||
let borrowHy = h2.hy;
|
||||
if (borrowHm < 1) {
|
||||
borrowHm = 12;
|
||||
borrowHy--;
|
||||
}
|
||||
days += this.engine.daysInMonth(borrowHy, borrowHm);
|
||||
}
|
||||
|
||||
// Borrow from years when months are negative.
|
||||
if (months < 0) {
|
||||
years--;
|
||||
months += 12;
|
||||
}
|
||||
|
||||
return new Temporal.Duration(years, months, 0, days);
|
||||
const diff = borrowHijriDiff(this.engine, h2.hy - h1.hy, h2.hm - h1.hm, h2.hd - h1.hd, h2);
|
||||
return new Temporal.Duration(diff.years, diff.months, 0, diff.days);
|
||||
}
|
||||
|
||||
if (largestUnit === 'months' || largestUnit === 'month') {
|
||||
const h1 = this.toHijri(one);
|
||||
const h2 = this.toHijri(two);
|
||||
|
||||
let years = h2.hy - h1.hy;
|
||||
let months = h2.hm - h1.hm;
|
||||
let days = h2.hd - h1.hd;
|
||||
|
||||
if (days < 0) {
|
||||
months--;
|
||||
let borrowHm = h2.hm - 1;
|
||||
let borrowHy = h2.hy;
|
||||
if (borrowHm < 1) {
|
||||
borrowHm = 12;
|
||||
borrowHy--;
|
||||
}
|
||||
days += this.engine.daysInMonth(borrowHy, borrowHm);
|
||||
}
|
||||
|
||||
if (months < 0) {
|
||||
years--;
|
||||
months += 12;
|
||||
}
|
||||
|
||||
const diff = borrowHijriDiff(this.engine, h2.hy - h1.hy, h2.hm - h1.hm, h2.hd - h1.hd, h2);
|
||||
// Roll years into months.
|
||||
return new Temporal.Duration(0, years * 12 + months, 0, days);
|
||||
return new Temporal.Duration(0, diff.years * 12 + diff.months, 0, diff.days);
|
||||
}
|
||||
|
||||
// For weeks and days, delegate to ISO arithmetic which is exact.
|
||||
|
|
@ -324,7 +362,7 @@ export class HijriCalendar {
|
|||
|
||||
mergeFields(
|
||||
fields: Record<string, unknown>,
|
||||
additionalFields: Record<string, unknown>
|
||||
additionalFields: Record<string, unknown>,
|
||||
): Record<string, unknown> {
|
||||
return { ...fields, ...additionalFields };
|
||||
}
|
||||
|
|
|
|||
86
test-cjs.cjs
86
test-cjs.cjs
|
|
@ -6,74 +6,66 @@
|
|||
* Verifies that the CommonJS build loads and functions correctly via require().
|
||||
*/
|
||||
|
||||
const { describe, it } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { Temporal } = require('@js-temporal/polyfill');
|
||||
const { UaqCalendar, FcnaCalendar, uaqCalendar, fcnaCalendar } = require('./dist/index.cjs');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
const total = 8;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(`[${name}]... PASS`);
|
||||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
const isoRamadan = Temporal.PlainDate.from('2023-03-23');
|
||||
|
||||
// ── Class and singleton exports ───────────────────────────────────────────────
|
||||
|
||||
test('UaqCalendar class loads via require', () => {
|
||||
assert(typeof UaqCalendar === 'function', 'UaqCalendar should be a constructor');
|
||||
const cal = new UaqCalendar();
|
||||
assert.equal(cal.id, 'hijri-uaq');
|
||||
});
|
||||
describe('CJS class exports', () => {
|
||||
it('UaqCalendar class loads via require', () => {
|
||||
assert(typeof UaqCalendar === 'function', 'UaqCalendar should be a constructor');
|
||||
const cal = new UaqCalendar();
|
||||
assert.equal(cal.id, 'hijri-uaq');
|
||||
});
|
||||
|
||||
test('FcnaCalendar class loads via require', () => {
|
||||
assert(typeof FcnaCalendar === 'function', 'FcnaCalendar should be a constructor');
|
||||
const cal = new FcnaCalendar();
|
||||
assert.equal(cal.id, 'hijri-fcna');
|
||||
});
|
||||
it('FcnaCalendar class loads via require', () => {
|
||||
assert(typeof FcnaCalendar === 'function', 'FcnaCalendar should be a constructor');
|
||||
const cal = new FcnaCalendar();
|
||||
assert.equal(cal.id, 'hijri-fcna');
|
||||
});
|
||||
|
||||
test('uaqCalendar singleton id', () => {
|
||||
assert.equal(uaqCalendar.id, 'hijri-uaq');
|
||||
});
|
||||
it('uaqCalendar singleton id', () => {
|
||||
assert.equal(uaqCalendar.id, 'hijri-uaq');
|
||||
});
|
||||
|
||||
test('fcnaCalendar singleton id', () => {
|
||||
assert.equal(fcnaCalendar.id, 'hijri-fcna');
|
||||
it('fcnaCalendar singleton id', () => {
|
||||
assert.equal(fcnaCalendar.id, 'hijri-fcna');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Field accessors ───────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.year(2023-03-23) = 1444', () => {
|
||||
assert.equal(uaqCalendar.year(isoRamadan), 1444);
|
||||
});
|
||||
describe('CJS field accessors', () => {
|
||||
it('uaqCalendar.year(2023-03-23) = 1444', () => {
|
||||
assert.equal(uaqCalendar.year(isoRamadan), 1444);
|
||||
});
|
||||
|
||||
test('uaqCalendar.month(2023-03-23) = 9', () => {
|
||||
assert.equal(uaqCalendar.month(isoRamadan), 9);
|
||||
});
|
||||
it('uaqCalendar.month(2023-03-23) = 9', () => {
|
||||
assert.equal(uaqCalendar.month(isoRamadan), 9);
|
||||
});
|
||||
|
||||
test('uaqCalendar.day(2023-03-23) = 1', () => {
|
||||
assert.equal(uaqCalendar.day(isoRamadan), 1);
|
||||
it('uaqCalendar.day(2023-03-23) = 1', () => {
|
||||
assert.equal(uaqCalendar.day(isoRamadan), 1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── dateFromFields ─────────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.dateFromFields({year:1444, month:9, day:1}) = 2023-03-23', () => {
|
||||
const result = uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 1 });
|
||||
assert.equal(result.toString(), '2023-03-23');
|
||||
describe('CJS dateFromFields', () => {
|
||||
it('uaqCalendar.dateFromFields({year:1444, month:9, day:1}) = 2023-03-23', () => {
|
||||
const result = uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 1 });
|
||||
assert.equal(result.toString(), '2023-03-23');
|
||||
});
|
||||
});
|
||||
|
||||
// ── Summary ───────────────────────────────────────────────────────────────────
|
||||
// ── fields() ──────────────────────────────────────────────────────────────────
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
if (failed > 0) {
|
||||
console.error(`${failed} test(s) failed`);
|
||||
process.exit(1);
|
||||
}
|
||||
describe('CJS fields()', () => {
|
||||
it('returns the input array unchanged', () => {
|
||||
assert.deepEqual(uaqCalendar.fields(['year', 'month', 'day']), ['year', 'month', 'day']);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
300
test.mjs
300
test.mjs
|
|
@ -5,25 +5,11 @@
|
|||
* Reference point: 2023-03-23 = 1 Ramadan 1444 AH (both UAQ and FCNA agree).
|
||||
*/
|
||||
|
||||
import { describe, it } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { Temporal } from '@js-temporal/polyfill';
|
||||
import { UaqCalendar, FcnaCalendar, uaqCalendar, fcnaCalendar } from './dist/index.mjs';
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
const total = 18;
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(`[${name}]... PASS`);
|
||||
passed++;
|
||||
} catch (err) {
|
||||
console.error(`[${name}]... FAIL: ${err.message}`);
|
||||
failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// Reference date: 2023-03-23 = 1 Ramadan 1444 AH
|
||||
const isoRamadan = Temporal.PlainDate.from('2023-03-23');
|
||||
// 2023-04-21 = 1 Shawwal 1444 AH (first day after Ramadan)
|
||||
|
|
@ -33,119 +19,247 @@ const isoLeapYear = Temporal.PlainDate.from('2021-08-09');
|
|||
|
||||
// ── 1. Class exports ──────────────────────────────────────────────────────────
|
||||
|
||||
test('UaqCalendar class export', () => {
|
||||
assert(UaqCalendar, 'UaqCalendar should be exported');
|
||||
const cal = new UaqCalendar();
|
||||
assert(cal instanceof UaqCalendar, 'UaqCalendar should be instantiable');
|
||||
});
|
||||
describe('Class exports', () => {
|
||||
it('UaqCalendar class export', () => {
|
||||
assert(UaqCalendar, 'UaqCalendar should be exported');
|
||||
const cal = new UaqCalendar();
|
||||
assert(cal instanceof UaqCalendar, 'UaqCalendar should be instantiable');
|
||||
});
|
||||
|
||||
test('FcnaCalendar class export', () => {
|
||||
assert(FcnaCalendar, 'FcnaCalendar should be exported');
|
||||
const cal = new FcnaCalendar();
|
||||
assert(cal instanceof FcnaCalendar, 'FcnaCalendar should be instantiable');
|
||||
it('FcnaCalendar class export', () => {
|
||||
assert(FcnaCalendar, 'FcnaCalendar should be exported');
|
||||
const cal = new FcnaCalendar();
|
||||
assert(cal instanceof FcnaCalendar, 'FcnaCalendar should be instantiable');
|
||||
});
|
||||
});
|
||||
|
||||
// ── 2. Calendar IDs ───────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.id', () => {
|
||||
assert.equal(uaqCalendar.id, 'hijri-uaq');
|
||||
});
|
||||
describe('Calendar IDs', () => {
|
||||
it('uaqCalendar.id', () => {
|
||||
assert.equal(uaqCalendar.id, 'hijri-uaq');
|
||||
});
|
||||
|
||||
test('fcnaCalendar.id', () => {
|
||||
assert.equal(fcnaCalendar.id, 'hijri-fcna');
|
||||
it('fcnaCalendar.id', () => {
|
||||
assert.equal(fcnaCalendar.id, 'hijri-fcna');
|
||||
});
|
||||
});
|
||||
|
||||
// ── 3. Field accessors on 1 Ramadan 1444 (2023-03-23) ────────────────────────
|
||||
|
||||
test('uaqCalendar.year(2023-03-23) = 1444', () => {
|
||||
assert.equal(uaqCalendar.year(isoRamadan), 1444);
|
||||
});
|
||||
describe('Field accessors (UAQ, 1 Ramadan 1444)', () => {
|
||||
it('year = 1444', () => {
|
||||
assert.equal(uaqCalendar.year(isoRamadan), 1444);
|
||||
});
|
||||
|
||||
test('uaqCalendar.month(2023-03-23) = 9 (Ramadan)', () => {
|
||||
assert.equal(uaqCalendar.month(isoRamadan), 9);
|
||||
});
|
||||
it('month = 9 (Ramadan)', () => {
|
||||
assert.equal(uaqCalendar.month(isoRamadan), 9);
|
||||
});
|
||||
|
||||
test('uaqCalendar.day(2023-03-23) = 1', () => {
|
||||
assert.equal(uaqCalendar.day(isoRamadan), 1);
|
||||
});
|
||||
it('day = 1', () => {
|
||||
assert.equal(uaqCalendar.day(isoRamadan), 1);
|
||||
});
|
||||
|
||||
test('uaqCalendar.monthCode(2023-03-23) = "M09"', () => {
|
||||
assert.equal(uaqCalendar.monthCode(isoRamadan), 'M09');
|
||||
});
|
||||
it('monthCode = "M09"', () => {
|
||||
assert.equal(uaqCalendar.monthCode(isoRamadan), 'M09');
|
||||
});
|
||||
|
||||
test('uaqCalendar.daysInMonth(2023-03-23) = 29 (Ramadan 1444 is 29 days)', () => {
|
||||
assert.equal(uaqCalendar.daysInMonth(isoRamadan), 29);
|
||||
});
|
||||
it('daysInMonth = 29 (Ramadan 1444)', () => {
|
||||
assert.equal(uaqCalendar.daysInMonth(isoRamadan), 29);
|
||||
});
|
||||
|
||||
test('uaqCalendar.monthsInYear(2023-03-23) = 12', () => {
|
||||
assert.equal(uaqCalendar.monthsInYear(isoRamadan), 12);
|
||||
});
|
||||
it('monthsInYear = 12', () => {
|
||||
assert.equal(uaqCalendar.monthsInYear(isoRamadan), 12);
|
||||
});
|
||||
|
||||
test('uaqCalendar.daysInWeek(2023-03-23) = 7', () => {
|
||||
assert.equal(uaqCalendar.daysInWeek(isoRamadan), 7);
|
||||
});
|
||||
it('daysInWeek = 7', () => {
|
||||
assert.equal(uaqCalendar.daysInWeek(isoRamadan), 7);
|
||||
});
|
||||
|
||||
// 2023-03-23 is a Thursday. ISO weekday: 1=Mon, ..., 4=Thu, ..., 7=Sun.
|
||||
test('uaqCalendar.dayOfWeek(2023-03-23) = 4 (Thursday)', () => {
|
||||
assert.equal(uaqCalendar.dayOfWeek(isoRamadan), 4);
|
||||
});
|
||||
it('dayOfWeek = 4 (Thursday)', () => {
|
||||
assert.equal(uaqCalendar.dayOfWeek(isoRamadan), 4);
|
||||
});
|
||||
|
||||
// dayOfYear: sum of months 1-8 in 1444 + 1 (first day of month 9).
|
||||
// Months 1-8 of 1444 total 236 days, so day 237 of the year.
|
||||
test('uaqCalendar.dayOfYear(2023-03-23) = 237', () => {
|
||||
assert.equal(uaqCalendar.dayOfYear(isoRamadan), 237);
|
||||
it('dayOfYear = 237', () => {
|
||||
assert.equal(uaqCalendar.dayOfYear(isoRamadan), 237);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 4. dateFromFields ─────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.dateFromFields({year:1444, month:9, day:1}) = 2023-03-23', () => {
|
||||
const result = uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 1 });
|
||||
assert.equal(result.toString(), '2023-03-23');
|
||||
describe('dateFromFields', () => {
|
||||
it('dateFromFields({year:1444, month:9, day:1}) = 2023-03-23', () => {
|
||||
const result = uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 1 });
|
||||
assert.equal(result.toString(), '2023-03-23');
|
||||
});
|
||||
});
|
||||
|
||||
// ── 5. dateAdd ────────────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.dateAdd: adding 1 month from 1 Ramadan 1444 lands on 1 Shawwal 1444', () => {
|
||||
const oneMonth = new Temporal.Duration(0, 1, 0, 0);
|
||||
const result = uaqCalendar.dateAdd(isoRamadan, oneMonth);
|
||||
// 1 Shawwal 1444 = 2023-04-21
|
||||
assert.equal(result.toString(), isoShawwal.toString());
|
||||
// Verify the result is in Shawwal (month 10)
|
||||
assert.equal(uaqCalendar.month(result), 10);
|
||||
describe('dateAdd', () => {
|
||||
it('adding 1 month from 1 Ramadan 1444 lands on 1 Shawwal 1444', () => {
|
||||
const oneMonth = new Temporal.Duration(0, 1, 0, 0);
|
||||
const result = uaqCalendar.dateAdd(isoRamadan, oneMonth);
|
||||
assert.equal(result.toString(), isoShawwal.toString());
|
||||
assert.equal(uaqCalendar.month(result), 10);
|
||||
});
|
||||
|
||||
it('adding 7 days from 1 Ramadan 1444', () => {
|
||||
const sevenDays = new Temporal.Duration(0, 0, 0, 7);
|
||||
const result = uaqCalendar.dateAdd(isoRamadan, sevenDays);
|
||||
assert.equal(uaqCalendar.day(result), 8);
|
||||
assert.equal(uaqCalendar.month(result), 9);
|
||||
});
|
||||
|
||||
it('adding 1 week from 1 Ramadan 1444', () => {
|
||||
const oneWeek = new Temporal.Duration(0, 0, 1, 0);
|
||||
const result = uaqCalendar.dateAdd(isoRamadan, oneWeek);
|
||||
assert.equal(uaqCalendar.day(result), 8);
|
||||
assert.equal(uaqCalendar.month(result), 9);
|
||||
});
|
||||
|
||||
it('adding 12 months rolls the year forward', () => {
|
||||
const twelveMonths = new Temporal.Duration(0, 12, 0, 0);
|
||||
const result = uaqCalendar.dateAdd(isoRamadan, twelveMonths);
|
||||
assert.equal(uaqCalendar.year(result), 1445);
|
||||
assert.equal(uaqCalendar.month(result), 9);
|
||||
});
|
||||
|
||||
it('subtracting months via negative duration', () => {
|
||||
const negMonth = new Temporal.Duration(0, -1, 0, 0);
|
||||
const result = uaqCalendar.dateAdd(isoShawwal, negMonth);
|
||||
assert.equal(uaqCalendar.month(result), 9);
|
||||
assert.equal(uaqCalendar.year(result), 1444);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 6. inLeapYear ─────────────────────────────────────────────────────────────
|
||||
// ── 6. dateUntil ──────────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.inLeapYear: 1443 AH (355 days) is a leap year, 1444 AH (354) is not', () => {
|
||||
// 2021-08-09 = 1 Muharram 1443 (355-day year)
|
||||
assert.equal(uaqCalendar.inLeapYear(isoLeapYear), true);
|
||||
// 2023-03-23 = in 1444 (354-day year)
|
||||
assert.equal(uaqCalendar.inLeapYear(isoRamadan), false);
|
||||
describe('dateUntil', () => {
|
||||
it('days between 1 Ramadan and 1 Shawwal 1444', () => {
|
||||
const dur = uaqCalendar.dateUntil(isoRamadan, isoShawwal, { largestUnit: 'days' });
|
||||
assert.equal(dur.days, 29);
|
||||
});
|
||||
|
||||
it('months between 1 Ramadan and 1 Shawwal 1444', () => {
|
||||
const dur = uaqCalendar.dateUntil(isoRamadan, isoShawwal, { largestUnit: 'months' });
|
||||
assert.equal(dur.months, 1);
|
||||
assert.equal(dur.days, 0);
|
||||
});
|
||||
|
||||
it('years between dates spanning one Hijri year', () => {
|
||||
const iso1443 = uaqCalendar.dateFromFields({ year: 1443, month: 1, day: 1 });
|
||||
const iso1444 = uaqCalendar.dateFromFields({ year: 1444, month: 1, day: 1 });
|
||||
const dur = uaqCalendar.dateUntil(iso1443, iso1444, { largestUnit: 'years' });
|
||||
assert.equal(dur.years, 1);
|
||||
assert.equal(dur.months, 0);
|
||||
assert.equal(dur.days, 0);
|
||||
});
|
||||
|
||||
it('weeks between dates', () => {
|
||||
const dur = uaqCalendar.dateUntil(isoRamadan, isoShawwal, { largestUnit: 'weeks' });
|
||||
assert.equal(dur.weeks, 4);
|
||||
assert.equal(dur.days, 1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 7. FCNA calendar ──────────────────────────────────────────────────────────
|
||||
// ── 7. inLeapYear ─────────────────────────────────────────────────────────────
|
||||
|
||||
// Both UAQ and FCNA agree on 1 Ramadan 1444 = 2023-03-23
|
||||
test('fcnaCalendar.year(2023-03-23) returns a valid Hijri year', () => {
|
||||
const year = fcnaCalendar.year(isoRamadan);
|
||||
assert(typeof year === 'number' && year > 1400, `Expected a Hijri year > 1400, got ${year}`);
|
||||
describe('inLeapYear', () => {
|
||||
it('1443 AH (355 days) is a leap year, 1444 AH (354) is not', () => {
|
||||
assert.equal(uaqCalendar.inLeapYear(isoLeapYear), true);
|
||||
assert.equal(uaqCalendar.inLeapYear(isoRamadan), false);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 8. Out-of-range error ─────────────────────────────────────────────────────
|
||||
// ── 8. FCNA calendar ──────────────────────────────────────────────────────────
|
||||
|
||||
test('uaqCalendar.year throws RangeError for out-of-range date (1800-01-01)', () => {
|
||||
// UAQ table covers 1318-1500 AH (Gregorian 1900-2076). 1800 is out of range.
|
||||
const outOfRange = Temporal.PlainDate.from('1800-01-01');
|
||||
assert.throws(
|
||||
() => uaqCalendar.year(outOfRange),
|
||||
(err) => err instanceof RangeError
|
||||
);
|
||||
describe('FCNA calendar', () => {
|
||||
it('fcnaCalendar.year(2023-03-23) returns a valid Hijri year', () => {
|
||||
const year = fcnaCalendar.year(isoRamadan);
|
||||
assert(typeof year === 'number' && year > 1400, `Expected a Hijri year > 1400, got ${year}`);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Summary ───────────────────────────────────────────────────────────────────
|
||||
// ── 9. Out-of-range error ─────────────────────────────────────────────────────
|
||||
|
||||
console.log(`\n${passed}/${total} tests passed`);
|
||||
if (failed > 0) {
|
||||
console.error(`${failed} test(s) failed`);
|
||||
process.exit(1);
|
||||
}
|
||||
describe('Out-of-range error', () => {
|
||||
it('uaqCalendar.year throws RangeError for out-of-range date (1800-01-01)', () => {
|
||||
const outOfRange = Temporal.PlainDate.from('1800-01-01');
|
||||
assert.throws(
|
||||
() => uaqCalendar.year(outOfRange),
|
||||
(err) => err instanceof RangeError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 10. overflow option ───────────────────────────────────────────────────────
|
||||
|
||||
describe('overflow option', () => {
|
||||
it('dateFromFields with overflow: "constrain" clamps day', () => {
|
||||
const result = uaqCalendar.dateFromFields(
|
||||
{ year: 1444, month: 9, day: 31 },
|
||||
{ overflow: 'constrain' },
|
||||
);
|
||||
assert.equal(uaqCalendar.day(result), 29);
|
||||
assert.equal(uaqCalendar.month(result), 9);
|
||||
});
|
||||
|
||||
it('dateFromFields with overflow: "reject" throws RangeError', () => {
|
||||
assert.throws(
|
||||
() => uaqCalendar.dateFromFields({ year: 1444, month: 9, day: 31 }, { overflow: 'reject' }),
|
||||
(err) => err instanceof RangeError,
|
||||
);
|
||||
});
|
||||
|
||||
it('monthDayFromFields with overflow: "constrain" clamps day', () => {
|
||||
const result = uaqCalendar.monthDayFromFields({ month: 9, day: 31 }, { overflow: 'constrain' });
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
it('monthDayFromFields with overflow: "reject" throws RangeError', () => {
|
||||
assert.throws(
|
||||
() => uaqCalendar.monthDayFromFields({ month: 9, day: 31 }, { overflow: 'reject' }),
|
||||
(err) => err instanceof RangeError,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 11. fields() ──────────────────────────────────────────────────────────────
|
||||
|
||||
describe('fields()', () => {
|
||||
it('returns the input array unchanged', () => {
|
||||
const input = ['year', 'month', 'day'];
|
||||
const result = uaqCalendar.fields(input);
|
||||
assert.deepEqual(result, ['year', 'month', 'day']);
|
||||
});
|
||||
|
||||
it('returns an empty array for empty input', () => {
|
||||
assert.deepEqual(uaqCalendar.fields([]), []);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 12. yearMonthFromFields ─────────────────────────────────────────────────
|
||||
|
||||
describe('yearMonthFromFields', () => {
|
||||
it('creates a PlainYearMonth for Ramadan 1444', () => {
|
||||
const result = uaqCalendar.yearMonthFromFields({ year: 1444, month: 9 });
|
||||
assert.ok(result);
|
||||
assert.equal(result.month, 3);
|
||||
assert.equal(result.year, 2023);
|
||||
});
|
||||
});
|
||||
|
||||
// ── 13. monthDayFromFields ──────────────────────────────────────────────────
|
||||
|
||||
describe('monthDayFromFields', () => {
|
||||
it('creates a PlainMonthDay for 15 Ramadan (default reference year)', () => {
|
||||
const result = uaqCalendar.monthDayFromFields({ month: 9, day: 15 });
|
||||
assert.ok(result);
|
||||
});
|
||||
|
||||
it('creates a PlainMonthDay with explicit year', () => {
|
||||
const result = uaqCalendar.monthDayFromFields({ month: 9, day: 1, year: 1445 });
|
||||
assert.ok(result);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,13 +5,16 @@
|
|||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "dist",
|
||||
"rootDir": "src",
|
||||
"types": ["node"]
|
||||
"types": ["node"],
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ export default defineConfig({
|
|||
clean: true,
|
||||
outDir: 'dist',
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
sourcemap: false,
|
||||
target: 'es2020',
|
||||
platform: 'node',
|
||||
external: ['hijri-core', '@js-temporal/polyfill'],
|
||||
|
|
|
|||
Loading…
Reference in a new issue