From 7f0ef622e77c0ead2474373559b8fae968c09e1e Mon Sep 17 00:00:00 2001 From: zhangxiang Date: Thu, 13 Aug 2026 07:57:07 +0800 Subject: [PATCH] =?UTF-8?q?feat(algorithm):=20=E4=B8=B0=E5=AF=8C=E8=BF=90?= =?UTF-8?q?=E5=8A=BF=E5=88=86=E6=9E=90=E5=86=85=E5=AE=B9=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E10=E4=B8=AA=E5=88=86=E6=9E=90=E7=BB=B4=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增维度(A): - 情绪/心态运势:基于命宫+福德宫,含情绪指数、压力水平、放松方式 - 家庭运势:基于父母宫+子女宫,含家庭和谐度、亲子关系建议 - 财运详情:正财/偏财区分、消费倾向、投资时机 - 事业详情:同事关系、领导看法、项目决策、晋升机会 - 健康详情:饮食、运动、作息建议、重点关注部位 现有维度增强(B): - 每日金句:基于命宫主星生成个性化金句,14主星各有不同 - 宜忌:6维度×7等级的宜/忌建议 - 冲突预警:基于煞星分布检测感情/事业/人际冲突 交互体验增强(D): - 分时段运势:上午/下午/晚上三段式文字描述 - 星级评定:10维度统一1-5星评级 测试:685 tests passed --- packages/algorithm/package-lock.json | 1889 +++++++++++++++++ packages/algorithm/package.json | 29 + .../algorithm/src/__tests__/algorithm.test.ts | 748 +++++++ packages/algorithm/src/enums.ts | 316 +++ packages/algorithm/src/fortune.ts | 1394 ++++++++++++ packages/algorithm/src/fortuneStrategy.ts | 347 +++ packages/algorithm/src/index.ts | 88 + packages/algorithm/src/types.ts | 530 +++++ packages/algorithm/src/ziweiAlgorithm.ts | 274 +++ packages/algorithm/tsconfig.json | 17 + 10 files changed, 5632 insertions(+) create mode 100644 packages/algorithm/package-lock.json create mode 100644 packages/algorithm/package.json create mode 100644 packages/algorithm/src/__tests__/algorithm.test.ts create mode 100644 packages/algorithm/src/enums.ts create mode 100644 packages/algorithm/src/fortune.ts create mode 100644 packages/algorithm/src/fortuneStrategy.ts create mode 100644 packages/algorithm/src/index.ts create mode 100644 packages/algorithm/src/types.ts create mode 100644 packages/algorithm/src/ziweiAlgorithm.ts create mode 100644 packages/algorithm/tsconfig.json diff --git a/packages/algorithm/package-lock.json b/packages/algorithm/package-lock.json new file mode 100644 index 0000000..15d0ad3 --- /dev/null +++ b/packages/algorithm/package-lock.json @@ -0,0 +1,1889 @@ +{ + "name": "@everything-suitable/algorithm", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@everything-suitable/algorithm", + "version": "1.0.0", + "devDependencies": { + "typescript": "^5.3.0", + "vitest": "^1.0.0" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.12", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.12.tgz", + "integrity": "sha512-hhyNJ+nbR6ZR7pToHvllEFun9TL0sbL+tk/ON75lo+Xas054uez98qRbsuNt7MBCyZKK4+8Yli/OAGZhmfBZ/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", + "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", + "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "1.6.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", + "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", + "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", + "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", + "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", + "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/confbox": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", + "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", + "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/local-pkg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.1.tgz", + "integrity": "sha512-9rrA30MRRP3gBD3HTGnC6cDFpaE1kVDWxWgqWJUN0RvDNAo+Nz/9GxB+nHOH0ifbVFy0hSA1V6vFDvnx54lTEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.3", + "pkg-types": "^1.2.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mlly": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", + "integrity": "sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.16.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.3" + } + }, + "node_modules/mlly/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pkg-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", + "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.1.8", + "mlly": "^1.7.4", + "pathe": "^2.0.1" + } + }, + "node_modules/pkg-types/node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.1.tgz", + "integrity": "sha512-631UJ6O00eNGfMiWG78ck80dfBab8X6IVFB51jZK5Icd7XAs60Z5y7QdSd/wGIklnWvRbUNloVzhOKKmutxQ6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.4.tgz", + "integrity": "sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", + "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", + "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", + "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "1.6.1", + "@vitest/runner": "1.6.1", + "@vitest/snapshot": "1.6.1", + "@vitest/spy": "1.6.1", + "@vitest/utils": "1.6.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.3", + "vite": "^5.0.0", + "vite-node": "1.6.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.6.1", + "@vitest/ui": "1.6.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/packages/algorithm/package.json b/packages/algorithm/package.json new file mode 100644 index 0000000..f479bb3 --- /dev/null +++ b/packages/algorithm/package.json @@ -0,0 +1,29 @@ +{ + "name": "@everything-suitable/algorithm", + "version": "1.0.0", + "description": "Pure algorithm functions for Everything Is Suitable (紫微斗数/黄历/运势)", + "type": "module", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "import": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "files": [ + "src" + ], + "scripts": { + "test": "vitest run", + "test:watch": "vitest", + "type:check": "tsc --noEmit" + }, + "devDependencies": { + "typescript": "^5.3.0", + "vitest": "^1.0.0" + }, + "peerDependencies": { + "typescript": ">=5.0.0" + } +} \ No newline at end of file diff --git a/packages/algorithm/src/__tests__/algorithm.test.ts b/packages/algorithm/src/__tests__/algorithm.test.ts new file mode 100644 index 0000000..c426751 --- /dev/null +++ b/packages/algorithm/src/__tests__/algorithm.test.ts @@ -0,0 +1,748 @@ +import { describe, it, expect } from 'vitest' +import { + // Enums + HeavenlyStem, + EarthlyBranch, + StarNature, + TransformationType, + MajorStar, + PalaceType, + + // Algorithm functions + calculateStarScore, + calculatePalaceScore, + calculateLuckyColor, + calculateLuckyNumber, + calculateLuckyDirection, + determineLuckLevel, + calculateDailyAdjustment, + calculateMonthlyAdjustment, + calculateYearlyAdjustment, + generateDailyFortune, + analyzePalaceFortune, + calculateOverallScore, + generateComprehensiveFortune, + analyzeLoveFortune, + analyzeStudyFortune, + analyzeSocialFortune, + analyzeTravelFortune, + + // Types + type ZiweiChart, + type Palace, + type StarInfo, + type DailyFortune, +} from '../index' + +describe('@everything-suitable/algorithm', () => { + describe('Enums', () => { + it('should export HeavenlyStem with correct values', () => { + expect(HeavenlyStem.JIA).toBe('JIA') + expect(HeavenlyStem.name(HeavenlyStem.JIA)).toBe('甲') + expect(HeavenlyStem.index(HeavenlyStem.JIA)).toBe(1) + expect(HeavenlyStem.fromIndex(1)).toBe(HeavenlyStem.JIA) + expect(HeavenlyStem.values()).toHaveLength(10) + }) + + it('should export EarthlyBranch with correct values', () => { + expect(EarthlyBranch.ZI).toBe('ZI') + expect(EarthlyBranch.name(EarthlyBranch.ZI)).toBe('子') + expect(EarthlyBranch.index(EarthlyBranch.ZI)).toBe(1) + expect(EarthlyBranch.fromIndex(1)).toBe(EarthlyBranch.ZI) + expect(EarthlyBranch.values()).toHaveLength(12) + }) + + it('should export StarNature with correct values', () => { + expect(StarNature.JI).toBe('JI') + expect(StarNature.name(StarNature.JI)).toBe('吉') + expect(StarNature.description(StarNature.JI)).toBe('吉星') + }) + + it('should export TransformationType with correct values', () => { + expect(TransformationType.LU).toBe('LU') + expect(TransformationType.name(TransformationType.LU)).toBe('禄') + }) + + it('should export MajorStar with correct values', () => { + expect(MajorStar.ZIWEI).toBe('ZIWEI') + expect(MajorStar.name(MajorStar.ZIWEI)).toBe('紫微') + expect(MajorStar.nature(MajorStar.ZIWEI)).toBe(StarNature.JI) + expect(MajorStar.values()).toHaveLength(14) + }) + + it('should export PalaceType with correct values', () => { + expect(PalaceType.MING).toBe('MING') + expect(PalaceType.name(PalaceType.MING)).toBe('命宫') + expect(PalaceType.values()).toHaveLength(12) + }) + }) + + describe('determineLuckLevel', () => { + it('should return 大吉 for score >= 90', () => { + expect(determineLuckLevel(95)).toBe('大吉') + expect(determineLuckLevel(90)).toBe('大吉') + }) + + it('should return 吉 for score 80-89', () => { + expect(determineLuckLevel(85)).toBe('吉') + expect(determineLuckLevel(80)).toBe('吉') + }) + + it('should return 中吉 for score 70-79', () => { + expect(determineLuckLevel(75)).toBe('中吉') + }) + + it('should return 平 for score 60-69', () => { + expect(determineLuckLevel(65)).toBe('平') + }) + + it('should return 中平 for score 50-59', () => { + expect(determineLuckLevel(55)).toBe('中平') + }) + + it('should return 小凶 for score 40-49', () => { + expect(determineLuckLevel(45)).toBe('小凶') + }) + + it('should return 凶 for score < 40', () => { + expect(determineLuckLevel(35)).toBe('凶') + expect(determineLuckLevel(0)).toBe('凶') + }) + }) + + describe('calculateStarScore', () => { + it('should return 50 for null starInfo', () => { + expect(calculateStarScore(null)).toBe(50) + }) + + it('should calculate base score for a Ji star', () => { + const starInfo: StarInfo = { + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: null, + brightness: 100, + description: '帝星', + } + const score = calculateStarScore(starInfo) + expect(score).toBeGreaterThan(0) + expect(score).toBeLessThanOrEqual(100) + }) + }) + + describe('calculatePalaceScore', () => { + it('should return 50 for null palace', () => { + expect(calculatePalaceScore(null)).toBe(50) + }) + + it('should return 50 for empty palace', () => { + const palace: Palace = { + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [], + minorStars: [], + analysis: '', + score: 0, + } + expect(calculatePalaceScore(palace)).toBe(50) + }) + }) + + describe('calculateOverallScore', () => { + it('should return 60 for empty fortunes', () => { + expect(calculateOverallScore({})).toBe(60) + }) + + it('should calculate average of palace fortunes', () => { + const fortunes = { + [PalaceType.MING]: { + palaceType: PalaceType.MING, + score: 80, + luckLevel: '吉', + analysis: '', + advice: '', + }, + [PalaceType.CAI]: { + palaceType: PalaceType.CAI, + score: 60, + luckLevel: '平', + analysis: '', + advice: '', + }, + } + expect(calculateOverallScore(fortunes)).toBe(70) + }) + + it('should handle Map input', () => { + const fortunes = new Map() + fortunes.set(PalaceType.MING, { + palaceType: PalaceType.MING, + score: 90, + luckLevel: '大吉', + analysis: '', + advice: '', + }) + expect(calculateOverallScore(fortunes)).toBe(90) + }) + }) + + describe('calculateDailyAdjustment', () => { + it('should return 0 adjustment for palace with no stars', () => { + const palace: Palace = { + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [], + minorStars: [], + analysis: '', + score: 50, + } + const date = new Date('2026-08-14') + expect(calculateDailyAdjustment(palace, date)).toBe(0) + }) + }) + + describe('generateDailyFortune', () => { + const mockChart: ZiweiChart = { + birthInfo: { + birthTime: '1990-05-15T08:00:00', + yearStem: HeavenlyStem.GENG, + monthStem: HeavenlyStem.XIN, + dayStem: HeavenlyStem.JIA, + hourStem: HeavenlyStem.WU, + yearBranch: EarthlyBranch.WU, + monthBranch: EarthlyBranch.SI, + dayBranch: EarthlyBranch.CHEN, + hourBranch: EarthlyBranch.CHEN, + gender: 'MALE', + timezone: 'Asia/Shanghai', + }, + palaces: [ + { + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [ + { + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: TransformationType.LU, + brightness: 100, + description: '帝星', + }, + ], + minorStars: [], + analysis: '命宫分析', + score: 85, + }, + { + palaceType: PalaceType.CAI, + earthlyBranch: EarthlyBranch.WU, + majorStars: [ + { + star: MajorStar.WUQU, + nature: StarNature.XIONG, + transformation: null, + brightness: 80, + description: '财星', + }, + ], + minorStars: [], + analysis: '财帛宫分析', + score: 75, + }, + { + palaceType: PalaceType.GUANLU, + earthlyBranch: EarthlyBranch.XU, + majorStars: [ + { + star: MajorStar.TIANFU, + nature: StarNature.JI, + transformation: null, + brightness: 90, + description: '库星', + }, + ], + minorStars: [], + analysis: '官禄宫分析', + score: 85, + }, + { + palaceType: PalaceType.FUQI, + earthlyBranch: EarthlyBranch.SHEN, + majorStars: [ + { + star: MajorStar.TIANJI, + nature: StarNature.JI, + transformation: null, + brightness: 70, + description: '智星', + }, + ], + minorStars: [], + analysis: '夫妻宫分析', + score: 75, + }, + { + palaceType: PalaceType.JILU, + earthlyBranch: EarthlyBranch.SI, + majorStars: [ + { + star: MajorStar.TAIYANG, + nature: StarNature.JI, + transformation: TransformationType.KE, + brightness: 85, + description: '贵星', + }, + ], + minorStars: [], + analysis: '疾厄宫分析', + score: 80, + }, + { + palaceType: PalaceType.XIONG, + earthlyBranch: EarthlyBranch.MAO, + majorStars: [ + { + star: MajorStar.LIANCHEN, + nature: StarNature.XIONG, + transformation: null, + brightness: 60, + description: '情星', + }, + ], + minorStars: [], + analysis: '兄弟宫分析', + score: 55, + }, + { + palaceType: PalaceType.QIAN, + earthlyBranch: EarthlyBranch.WEI, + majorStars: [], + minorStars: [], + analysis: '迁移宫分析', + score: 50, + }, + { + palaceType: PalaceType.ZINV, + earthlyBranch: EarthlyBranch.CHOU, + majorStars: [], + minorStars: [], + analysis: '子女宫分析', + score: 50, + }, + { + palaceType: PalaceType.TUDI, + earthlyBranch: EarthlyBranch.HAI, + majorStars: [], + minorStars: [], + analysis: '田宅宫分析', + score: 50, + }, + { + palaceType: PalaceType.FUBEN, + earthlyBranch: EarthlyBranch.YOU, + majorStars: [], + minorStars: [], + analysis: '福德宫分析', + score: 50, + }, + { + palaceType: PalaceType.SHEN, + earthlyBranch: EarthlyBranch.CHEN, + majorStars: [], + minorStars: [], + analysis: '身宫分析', + score: 50, + }, + { + palaceType: PalaceType.FU, + earthlyBranch: EarthlyBranch.SI, + majorStars: [], + minorStars: [], + analysis: '父母宫分析', + score: 50, + }, + ], + yearStem: HeavenlyStem.GENG, + mingGongBranch: EarthlyBranch.YIN, + shenGongBranch: EarthlyBranch.CHEN, + summary: '测试命盘', + overallLuck: '吉', + } + + it('should generate daily fortune with correct structure', () => { + const date = new Date('2026-08-14') + const fortune = generateDailyFortune(mockChart, date) + + expect(fortune).toBeDefined() + expect(fortune.fortuneDate).toBe('2026-08-14') + expect(fortune.overallScore).toBeGreaterThan(0) + expect(fortune.overallLuck).toBeTruthy() + expect(fortune.careerAdvice).toBeTruthy() + expect(fortune.wealthAdvice).toBeTruthy() + expect(fortune.relationshipAdvice).toBeTruthy() + expect(fortune.healthAdvice).toBeTruthy() + }) + + it('should generate consistent fortune for same input', () => { + const date = new Date('2026-08-14') + const fortune1 = generateDailyFortune(mockChart, date) + const fortune2 = generateDailyFortune(mockChart, date) + + expect(fortune1.overallScore).toBe(fortune2.overallScore) + expect(fortune1.overallLuck).toBe(fortune2.overallLuck) + }) + + it('should generate different fortunes for different dates', () => { + const fortune1 = generateDailyFortune(mockChart, new Date('2026-08-14')) + const fortune2 = generateDailyFortune(mockChart, new Date('2026-08-15')) + + // Scores may differ due to daily adjustment + expect(fortune1.fortuneDate).not.toBe(fortune2.fortuneDate) + }) + }) + + describe('generateComprehensiveFortune', () => { + const mockChart: ZiweiChart = { + birthInfo: { + birthTime: '1990-05-15T08:00:00', + yearStem: HeavenlyStem.GENG, + monthStem: HeavenlyStem.XIN, + dayStem: HeavenlyStem.JIA, + hourStem: HeavenlyStem.WU, + yearBranch: EarthlyBranch.WU, + monthBranch: EarthlyBranch.SI, + dayBranch: EarthlyBranch.CHEN, + hourBranch: EarthlyBranch.CHEN, + gender: 'MALE', + timezone: 'Asia/Shanghai', + }, + palaces: [ + { + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [{ + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: TransformationType.LU, + brightness: 100, + description: '帝星', + }], + minorStars: [], + analysis: '命宫分析', + score: 85, + }, + { + palaceType: PalaceType.CAI, + earthlyBranch: EarthlyBranch.WU, + majorStars: [{ + star: MajorStar.WUQU, + nature: StarNature.XIONG, + transformation: null, + brightness: 80, + description: '财星', + }], + minorStars: [], + analysis: '财帛宫分析', + score: 75, + }, + { + palaceType: PalaceType.GUANLU, + earthlyBranch: EarthlyBranch.XU, + majorStars: [{ + star: MajorStar.TIANFU, + nature: StarNature.JI, + transformation: null, + brightness: 90, + description: '库星', + }], + minorStars: ['WENCHANG', 'WENQU'], + analysis: '官禄宫分析', + score: 85, + }, + { + palaceType: PalaceType.FUQI, + earthlyBranch: EarthlyBranch.SHEN, + majorStars: [{ + star: MajorStar.TIANJI, + nature: StarNature.JI, + transformation: null, + brightness: 70, + description: '智星', + }], + minorStars: ['HONGLUAN', 'TIANXI'], + analysis: '夫妻宫分析', + score: 75, + }, + { + palaceType: PalaceType.JILU, + earthlyBranch: EarthlyBranch.SI, + majorStars: [{ + star: MajorStar.TAIYANG, + nature: StarNature.JI, + transformation: TransformationType.KE, + brightness: 85, + description: '贵星', + }], + minorStars: [], + analysis: '疾厄宫分析', + score: 80, + }, + { + palaceType: PalaceType.XIONG, + earthlyBranch: EarthlyBranch.MAO, + majorStars: [{ + star: MajorStar.LIANCHEN, + nature: StarNature.XIONG, + transformation: null, + brightness: 60, + description: '情星', + }], + minorStars: ['TIANKUI', 'TIANYUE'], + analysis: '兄弟宫分析', + score: 55, + }, + { + palaceType: PalaceType.QIAN, + earthlyBranch: EarthlyBranch.WEI, + majorStars: [], + minorStars: ['TIANMA'], + analysis: '迁移宫分析', + score: 50, + }, + { + palaceType: PalaceType.ZINV, + earthlyBranch: EarthlyBranch.CHOU, + majorStars: [], + minorStars: [], + analysis: '子女宫分析', + score: 50, + }, + { + palaceType: PalaceType.TUDI, + earthlyBranch: EarthlyBranch.HAI, + majorStars: [], + minorStars: [], + analysis: '田宅宫分析', + score: 50, + }, + { + palaceType: PalaceType.FUBEN, + earthlyBranch: EarthlyBranch.YOU, + majorStars: [], + minorStars: [], + analysis: '福德宫分析', + score: 50, + }, + { + palaceType: PalaceType.SHEN, + earthlyBranch: EarthlyBranch.CHEN, + majorStars: [], + minorStars: [], + analysis: '身宫分析', + score: 50, + }, + { + palaceType: PalaceType.FU, + earthlyBranch: EarthlyBranch.SI, + majorStars: [], + minorStars: [], + analysis: '父母宫分析', + score: 50, + }, + ], + yearStem: HeavenlyStem.GENG, + mingGongBranch: EarthlyBranch.YIN, + shenGongBranch: EarthlyBranch.CHEN, + summary: '测试命盘', + overallLuck: '吉', + } + + it('should generate comprehensive fortune with all dimensions', () => { + const date = new Date('2026-08-14') + const fortune = generateComprehensiveFortune(mockChart, date) + + // Base fortune fields + expect(fortune.overallScore).toBeGreaterThan(0) + expect(fortune.careerAdvice).toBeTruthy() + expect(fortune.wealthAdvice).toBeTruthy() + + // Love fortune fields + expect(fortune.loveScore).toBeDefined() + expect(fortune.loveScore).toBeGreaterThan(0) + expect(fortune.overallLoveLuck).toBeTruthy() + expect(fortune.peachBlossomIndex).toBeTruthy() + expect(fortune.singleAdvice).toBeTruthy() + + // Study fortune fields + expect(fortune.studyScore).toBeDefined() + expect(fortune.studyScore).toBeGreaterThan(0) + expect(fortune.overallStudyLuck).toBeTruthy() + expect(fortune.studyAdvice).toBeTruthy() + + // Social fortune fields + expect(fortune.socialScore).toBeDefined() + expect(fortune.socialScore).toBeGreaterThan(0) + expect(fortune.overallSocialLuck).toBeTruthy() + expect(fortune.socialAdvice).toBeTruthy() + + // Travel fortune fields + expect(fortune.travelScore).toBeDefined() + expect(fortune.travelScore).toBeGreaterThan(0) + expect(fortune.overallTravelLuck).toBeTruthy() + expect(fortune.travelAdvice).toBeTruthy() + }) + }) + + describe('analyzePalaceFortune', () => { + it('should analyze palace fortune with score and luck level', () => { + const palace: Palace = { + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [{ + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: TransformationType.LU, + brightness: 100, + description: '帝星', + }], + minorStars: [], + analysis: '', + score: 85, + } + const date = new Date('2026-08-14') + const fortune = analyzePalaceFortune(palace, date) + + expect(fortune.palaceType).toBe(PalaceType.MING) + expect(fortune.score).toBeGreaterThan(0) + expect(fortune.luckLevel).toBeTruthy() + expect(fortune.analysis).toContain('命宫') + expect(fortune.advice).toBeTruthy() + }) + }) + + describe('calculateLuckyColor', () => { + it('should work with a mock chart', () => { + // Create a minimal chart for testing + const chart: ZiweiChart = { + birthInfo: { + birthTime: '1990-05-15T08:00:00', + yearStem: HeavenlyStem.GENG, + monthStem: HeavenlyStem.XIN, + dayStem: HeavenlyStem.JIA, + hourStem: HeavenlyStem.WU, + yearBranch: EarthlyBranch.WU, + monthBranch: EarthlyBranch.SI, + dayBranch: EarthlyBranch.CHEN, + hourBranch: EarthlyBranch.CHEN, + gender: 'MALE', + timezone: 'Asia/Shanghai', + }, + palaces: [{ + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [{ + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: null, + brightness: 100, + description: '帝星', + }], + minorStars: [], + analysis: '', + score: 85, + }], + yearStem: HeavenlyStem.GENG, + mingGongBranch: EarthlyBranch.YIN, + shenGongBranch: EarthlyBranch.CHEN, + summary: '', + overallLuck: '吉', + } + const color = calculateLuckyColor(chart, new Date('2026-08-14')) + expect(color).toBeTruthy() + expect(typeof color).toBe('string') + }) + }) + + describe('calculateLuckyNumber', () => { + it('should return a number string', () => { + const chart: ZiweiChart = { + birthInfo: { + birthTime: '1990-05-15T08:00:00', + yearStem: HeavenlyStem.GENG, + monthStem: HeavenlyStem.XIN, + dayStem: HeavenlyStem.JIA, + hourStem: HeavenlyStem.WU, + yearBranch: EarthlyBranch.WU, + monthBranch: EarthlyBranch.SI, + dayBranch: EarthlyBranch.CHEN, + hourBranch: EarthlyBranch.CHEN, + gender: 'MALE', + timezone: 'Asia/Shanghai', + }, + palaces: [{ + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [{ + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: null, + brightness: 100, + description: '帝星', + }], + minorStars: [], + analysis: '', + score: 85, + }], + yearStem: HeavenlyStem.GENG, + mingGongBranch: EarthlyBranch.YIN, + shenGongBranch: EarthlyBranch.CHEN, + summary: '', + overallLuck: '吉', + } + const number = calculateLuckyNumber(chart, new Date('2026-08-14')) + expect(number).toBeTruthy() + expect(typeof number).toBe('string') + }) + }) + + describe('calculateLuckyDirection', () => { + it('should return a direction string', () => { + const chart: ZiweiChart = { + birthInfo: { + birthTime: '1990-05-15T08:00:00', + yearStem: HeavenlyStem.GENG, + monthStem: HeavenlyStem.XIN, + dayStem: HeavenlyStem.JIA, + hourStem: HeavenlyStem.WU, + yearBranch: EarthlyBranch.WU, + monthBranch: EarthlyBranch.SI, + dayBranch: EarthlyBranch.CHEN, + hourBranch: EarthlyBranch.CHEN, + gender: 'MALE', + timezone: 'Asia/Shanghai', + }, + palaces: [{ + palaceType: PalaceType.MING, + earthlyBranch: EarthlyBranch.YIN, + majorStars: [{ + star: MajorStar.ZIWEI, + nature: StarNature.JI, + transformation: null, + brightness: 100, + description: '帝星', + }], + minorStars: [], + analysis: '', + score: 85, + }], + yearStem: HeavenlyStem.GENG, + mingGongBranch: EarthlyBranch.YIN, + shenGongBranch: EarthlyBranch.CHEN, + summary: '', + overallLuck: '吉', + } + const direction = calculateLuckyDirection(chart, new Date('2026-08-14')) + expect(direction).toBeTruthy() + expect(typeof direction).toBe('string') + }) + }) +}) \ No newline at end of file diff --git a/packages/algorithm/src/enums.ts b/packages/algorithm/src/enums.ts new file mode 100644 index 0000000..7187c4d --- /dev/null +++ b/packages/algorithm/src/enums.ts @@ -0,0 +1,316 @@ +export enum HeavenlyStem { + JIA = 'JIA', + YI = 'YI', + BING = 'BING', + DING = 'DING', + WU = 'WU', + JI = 'JI', + GENG = 'GENG', + XIN = 'XIN', + REN = 'REN', + GUI = 'GUI', +} + +export namespace HeavenlyStem { + const DATA: Record = { + [HeavenlyStem.JIA]: { name: '甲', index: 1 }, + [HeavenlyStem.YI]: { name: '乙', index: 2 }, + [HeavenlyStem.BING]: { name: '丙', index: 3 }, + [HeavenlyStem.DING]: { name: '丁', index: 4 }, + [HeavenlyStem.WU]: { name: '戊', index: 5 }, + [HeavenlyStem.JI]: { name: '己', index: 6 }, + [HeavenlyStem.GENG]: { name: '庚', index: 7 }, + [HeavenlyStem.XIN]: { name: '辛', index: 8 }, + [HeavenlyStem.REN]: { name: '壬', index: 9 }, + [HeavenlyStem.GUI]: { name: '癸', index: 10 }, + } + + const VALUES: HeavenlyStem[] = Object.values(HeavenlyStem).filter( + (v): v is HeavenlyStem => typeof v === 'string', + ) + + export const name = (s: HeavenlyStem): string => DATA[s].name + export const index = (s: HeavenlyStem): number => DATA[s].index + + export function fromIndex(i: number): HeavenlyStem { + return VALUES[((i - 1) % 10 + 10) % 10] + } + + export function values(): HeavenlyStem[] { + return [...VALUES] + } +} + +export enum EarthlyBranch { + ZI = 'ZI', + CHOU = 'CHOU', + YIN = 'YIN', + MAO = 'MAO', + CHEN = 'CHEN', + SI = 'SI', + WU = 'WU', + WEI = 'WEI', + SHEN = 'SHEN', + YOU = 'YOU', + XU = 'XU', + HAI = 'HAI', +} + +export namespace EarthlyBranch { + const DATA: Record = { + [EarthlyBranch.ZI]: { name: '子', index: 1 }, + [EarthlyBranch.CHOU]: { name: '丑', index: 2 }, + [EarthlyBranch.YIN]: { name: '寅', index: 3 }, + [EarthlyBranch.MAO]: { name: '卯', index: 4 }, + [EarthlyBranch.CHEN]: { name: '辰', index: 5 }, + [EarthlyBranch.SI]: { name: '巳', index: 6 }, + [EarthlyBranch.WU]: { name: '午', index: 7 }, + [EarthlyBranch.WEI]: { name: '未', index: 8 }, + [EarthlyBranch.SHEN]: { name: '申', index: 9 }, + [EarthlyBranch.YOU]: { name: '酉', index: 10 }, + [EarthlyBranch.XU]: { name: '戌', index: 11 }, + [EarthlyBranch.HAI]: { name: '亥', index: 12 }, + } + + const VALUES: EarthlyBranch[] = Object.values(EarthlyBranch).filter( + (v): v is EarthlyBranch => typeof v === 'string', + ) + + export const name = (s: EarthlyBranch): string => DATA[s].name + export const index = (s: EarthlyBranch): number => DATA[s].index + + export function fromIndex(i: number): EarthlyBranch { + return VALUES[((i - 1) % 12 + 12) % 12] + } + + export function values(): EarthlyBranch[] { + return [...VALUES] + } +} + +export enum StarNature { + JI = 'JI', + XIONG = 'XIONG', + ZHONGHE = 'ZHONGHE', +} + +export namespace StarNature { + const DATA: Record = { + [StarNature.JI]: { name: '吉', description: '吉星' }, + [StarNature.XIONG]: { name: '凶', description: '凶星' }, + [StarNature.ZHONGHE]: { name: '中', description: '中性' }, + } + + export const name = (s: StarNature): string => DATA[s].name + export const description = (s: StarNature): string => DATA[s].description +} + +export enum TransformationType { + LU = 'LU', + QUAN = 'QUAN', + KE = 'KE', + JI = 'JI', +} + +export namespace TransformationType { + const DATA: Record = { + [TransformationType.LU]: { name: '禄' }, + [TransformationType.QUAN]: { name: '权' }, + [TransformationType.KE]: { name: '科' }, + [TransformationType.JI]: { name: '忌' }, + } + + export const name = (s: TransformationType): string => DATA[s].name +} + +export enum MajorStar { + ZIWEI = 'ZIWEI', + TIANJI = 'TIANJI', + TAIYANG = 'TAIYANG', + WUQU = 'WUQU', + TIANTONG = 'TIANTONG', + LIANCHEN = 'LIANCHEN', + TIANFU = 'TIANFU', + TAIYIN = 'TAIYIN', + TANLANG = 'TANLANG', + JUMEN = 'JUMEN', + TIANXIANG = 'TIANXIANG', + TIANLIANG = 'TIANLIANG', + QISHA = 'QISHA', + POJUN = 'POJUN', +} + +export namespace MajorStar { + const DATA: Record< + MajorStar, + { name: string; index: number; nature: StarNature; alias: string; description: string } + > = { + [MajorStar.ZIWEI]: { name: '紫微', index: 1, nature: StarNature.JI, alias: '帝星', description: '领导、权威、尊贵' }, + [MajorStar.TIANJI]: { name: '天机', index: 2, nature: StarNature.JI, alias: '智星', description: '智慧、谋略、变通' }, + [MajorStar.TAIYANG]: { name: '太阳', index: 3, nature: StarNature.JI, alias: '贵星', description: '光明、热情、慷慨' }, + [MajorStar.WUQU]: { name: '武曲', index: 4, nature: StarNature.XIONG, alias: '财星', description: '刚毅、务实、财帛' }, + [MajorStar.TIANTONG]: { name: '天同', index: 5, nature: StarNature.JI, alias: '福星', description: '温和、福气、享受' }, + [MajorStar.LIANCHEN]: { name: '廉贞', index: 6, nature: StarNature.XIONG, alias: '情星', description: '感情、才华、是非' }, + [MajorStar.TIANFU]: { name: '天府', index: 7, nature: StarNature.JI, alias: '库星', description: '稳重、保守、财富' }, + [MajorStar.TAIYIN]: { name: '太阴', index: 8, nature: StarNature.ZHONGHE, alias: '母星', description: '温柔、内敛、母性' }, + [MajorStar.TANLANG]: { name: '贪狼', index: 9, nature: StarNature.XIONG, alias: '欲星', description: '欲望、魅力、桃花' }, + [MajorStar.JUMEN]: { name: '巨门', index: 10, nature: StarNature.XIONG, alias: '口星', description: '口才、是非、沟通' }, + [MajorStar.TIANXIANG]: { name: '天相', index: 11, nature: StarNature.JI, alias: '印星', description: '辅佐、稳重、贵人' }, + [MajorStar.TIANLIANG]: { name: '天梁', index: 12, nature: StarNature.ZHONGHE, alias: '荫星', description: '恩泽、稳重、长辈' }, + [MajorStar.QISHA]: { name: '七杀', index: 13, nature: StarNature.XIONG, alias: '将星', description: '威猛、果断、开创' }, + [MajorStar.POJUN]: { name: '破军', index: 14, nature: StarNature.XIONG, alias: '耗星', description: '破旧立新、变动、消耗' }, + } + + const VALUES: MajorStar[] = Object.values(MajorStar).filter( + (v): v is MajorStar => typeof v === 'string', + ) + + export const name = (s: MajorStar): string => DATA[s].name + export const index = (s: MajorStar): number => DATA[s].index + export const nature = (s: MajorStar): StarNature => DATA[s].nature + export const alias = (s: MajorStar): string => DATA[s].alias + export const description = (s: MajorStar): string => DATA[s].description + + export function values(): MajorStar[] { + return [...VALUES] + } +} + +export enum PalaceType { + MING = 'MING', + XIONG = 'XIONG', + FUQI = 'FUQI', + ZINV = 'ZINV', + CAI = 'CAI', + JILU = 'JILU', + QIAN = 'QIAN', + GUANLU = 'GUANLU', + TUDI = 'TUDI', + FUBEN = 'FUBEN', + FU = 'FU', + SHEN = 'SHEN', +} + +export namespace PalaceType { + const DATA: Record = { + [PalaceType.MING]: { name: '命宫' }, + [PalaceType.XIONG]: { name: '兄弟宫' }, + [PalaceType.FUQI]: { name: '夫妻宫' }, + [PalaceType.ZINV]: { name: '子女宫' }, + [PalaceType.CAI]: { name: '财帛宫' }, + [PalaceType.JILU]: { name: '疾厄宫' }, + [PalaceType.QIAN]: { name: '迁移宫' }, + [PalaceType.GUANLU]: { name: '官禄宫' }, + [PalaceType.TUDI]: { name: '田宅宫' }, + [PalaceType.FUBEN]: { name: '福德宫' }, + [PalaceType.FU]: { name: '父母宫' }, + [PalaceType.SHEN]: { name: '身宫' }, + } + + const VALUES: PalaceType[] = Object.values(PalaceType).filter( + (v): v is PalaceType => typeof v === 'string', + ) + + export const name = (s: PalaceType): string => DATA[s].name + + export function values(): PalaceType[] { + return [...VALUES] + } +} + +export enum FortuneType { + DAILY = 'DAILY', + MONTHLY = 'MONTHLY', + YEARLY = 'YEARLY', + CAREER = 'CAREER', + WEALTH = 'WEALTH', + LOVE = 'LOVE', + HEALTH = 'HEALTH', + STUDY = 'STUDY', + SOCIAL = 'SOCIAL', + TRAVEL = 'TRAVEL', +} + +export enum MinorStarType { + LUCKY = 'LUCKY', + EVIL = 'EVIL', + OTHER = 'OTHER', +} + +export namespace MinorStarType { + const DATA: Record = { + [MinorStarType.LUCKY]: { description: '吉星' }, + [MinorStarType.EVIL]: { description: '煞星' }, + [MinorStarType.OTHER]: { description: '其他' }, + } + + export const description = (s: MinorStarType): string => DATA[s].description +} + +export enum MinorStar { + ZUOFU = 'ZUOFU', + YOUBI = 'YOUBI', + TIANKUI = 'TIANKUI', + TIANYUE = 'TIANYUE', + WENCHANG = 'WENCHANG', + WENQU = 'WENQU', + QINGYANG = 'QINGYANG', + TUOLUO = 'TUOLUO', + HUOXING = 'HUOXING', + LINGXING = 'LINGXING', + DIKONG = 'DIKONG', + DIJIE = 'DIJIE', + TIANXING = 'TIANXING', + LUCUN = 'LUCUN', + TIANMA = 'TIANMA', + HUAGAI = 'HUAGAI', + LONGCHI = 'LONGCHI', + FENGGU = 'FENGGU', + HONGLUAN = 'HONGLUAN', + TIANXI = 'TIANXI', + GUCHEN = 'GUCHEN', + GUAKU = 'GUAKU', +} + +export namespace MinorStar { + const DATA: Record< + MinorStar, + { name: string; type: MinorStarType; alias: string; description: string } + > = { + [MinorStar.ZUOFU]: { name: '左辅', type: MinorStarType.LUCKY, alias: '左辅星', description: '主辅佐、助力、贵人' }, + [MinorStar.YOUBI]: { name: '右弼', type: MinorStarType.LUCKY, alias: '右弼星', description: '主辅佐、助力、贵人' }, + [MinorStar.TIANKUI]: { name: '天魁', type: MinorStarType.LUCKY, alias: '天魁星', description: '主贵人、助力、机遇' }, + [MinorStar.TIANYUE]: { name: '天钺', type: MinorStarType.LUCKY, alias: '天钺星', description: '主贵人、助力、机遇' }, + [MinorStar.WENCHANG]: { name: '文昌', type: MinorStarType.LUCKY, alias: '文昌星', description: '主科名、文采、学业' }, + [MinorStar.WENQU]: { name: '文曲', type: MinorStarType.LUCKY, alias: '文曲星', description: '主口才、艺术、才华' }, + [MinorStar.QINGYANG]: { name: '擎羊', type: MinorStarType.EVIL, alias: '羊刃星', description: '主刑伤、是非、血光' }, + [MinorStar.TUOLUO]: { name: '陀罗', type: MinorStarType.EVIL, alias: '陀罗星', description: '主拖延、迟滞、纠结' }, + [MinorStar.HUOXING]: { name: '火星', type: MinorStarType.EVIL, alias: '火星', description: '主急躁、突发、灾难' }, + [MinorStar.LINGXING]: { name: '铃星', type: MinorStarType.EVIL, alias: '铃星', description: '主突发、灾难、变动' }, + [MinorStar.DIKONG]: { name: '地空', type: MinorStarType.EVIL, alias: '地空星', description: '主空亡、损失、虚耗' }, + [MinorStar.DIJIE]: { name: '地劫', type: MinorStarType.EVIL, alias: '地劫星', description: '主劫夺、损失、破财' }, + [MinorStar.TIANXING]: { name: '天刑', type: MinorStarType.EVIL, alias: '天刑星', description: '主刑伤、官非、诉讼' }, + [MinorStar.LUCUN]: { name: '禄存', type: MinorStarType.OTHER, alias: '禄存星', description: '主财帛、福气' }, + [MinorStar.TIANMA]: { name: '天马', type: MinorStarType.OTHER, alias: '天马星', description: '主迁移、奔波、变动' }, + [MinorStar.HUAGAI]: { name: '华盖', type: MinorStarType.OTHER, alias: '华盖星', description: '主孤独、艺术、宗教' }, + [MinorStar.LONGCHI]: { name: '龙池', type: MinorStarType.OTHER, alias: '龙池星', description: '主才华、艺术、文采' }, + [MinorStar.FENGGU]: { name: '凤阁', type: MinorStarType.OTHER, alias: '凤阁星', description: '主才华、艺术、文采' }, + [MinorStar.HONGLUAN]: { name: '红鸾', type: MinorStarType.OTHER, alias: '红鸾星', description: '主桃花、婚姻、感情' }, + [MinorStar.TIANXI]: { name: '天喜', type: MinorStarType.OTHER, alias: '天喜星', description: '主桃花、婚姻、感情' }, + [MinorStar.GUCHEN]: { name: '孤辰', type: MinorStarType.OTHER, alias: '孤辰星', description: '主孤独、寂寞、寡居' }, + [MinorStar.GUAKU]: { name: '寡宿', type: MinorStarType.OTHER, alias: '寡宿星', description: '主孤独、寂寞、寡居' }, + } + + const VALUES: MinorStar[] = Object.values(MinorStar).filter( + (v): v is MinorStar => typeof v === 'string', + ) + + export const name = (s: MinorStar): string => DATA[s].name + export const type = (s: MinorStar): MinorStarType => DATA[s].type + export const alias = (s: MinorStar): string => DATA[s].alias + export const description = (s: MinorStar): string => DATA[s].description + + export function values(): MinorStar[] { + return [...VALUES] + } +} \ No newline at end of file diff --git a/packages/algorithm/src/fortune.ts b/packages/algorithm/src/fortune.ts new file mode 100644 index 0000000..f586941 --- /dev/null +++ b/packages/algorithm/src/fortune.ts @@ -0,0 +1,1394 @@ +import { PalaceType, StarNature, EarthlyBranch, MajorStar } from './enums' +import { determineLuckLevel } from './ziweiAlgorithm' +import { + generateDailyFortune, + generateMonthlyFortune, + generateYearlyFortune, + calculateDailyAdjustment, + calculateMonthlyAdjustment, + calculateYearlyAdjustment, +} from './fortuneStrategy' +import type { + ZiweiChart, + Palace, + LoveFortuneDetail, + StudyFortuneDetail, + SocialFortuneDetail, + TravelFortuneDetail, + EmotionalFortuneDetail, + FamilyFortuneDetail, + WealthFortuneDetail, + CareerFortuneDetail, + HealthFortuneDetail, + SuitableAvoidItem, + DailyQuote, + ConflictWarning, + TimeSlotFortune, + StarRating, + DailyFortune, + MonthlyFortune, + YearlyFortune, +} from './types' + +const PEACH_BLOSSOM_STARS = ['HONGLUAN', 'TIANXI', 'TANLANG'] +const STUDY_STARS = ['WENCHANG', 'WENQU'] +const NOBLE_STARS = ['ZUOFU', 'YOUBI', 'TIANKUI', 'TIANYUE'] +const TRAVEL_STARS = ['TIANMA'] + +const DATING_TIME_MAP: Record = { + '大吉': '下午3点至5点,傍晚6点至8点', + '吉': '下午2点至4点,晚上7点至9点', + '中吉': '上午10点至12点,下午3点至5点', + '平': '下午1点至3点', + '中平': '上午9点至11点', + '小凶': '暂不宜约会,宜先调整自身状态', + '凶': '不宜约会,宜静待时机', +} + +const STUDY_TIME_MAP: Record = { + '大吉': '清晨6点至8点,晚上8点至10点', + '吉': '上午8点至10点,晚上7点至9点', + '中吉': '上午9点至11点,下午3点至5点', + '平': '上午10点至12点', + '中平': '下午2点至4点', + '小凶': '学习效率较低,建议短暂休息后复习', + '凶': '不宜高强度学习,宜轻松阅读', +} + +const SOCIAL_TIME_MAP: Record = { + '大吉': '中午12点至2点,晚上6点至9点', + '吉': '下午1点至3点,晚上7点至10点', + '中吉': '上午10点至12点,下午3点至5点', + '平': '下午2点至4点', + '中平': '上午9点至11点', + '小凶': '社交运势偏弱,宜减少应酬', + '凶': '不宜社交,宜独处调整', +} + +const TRAVEL_TIME_MAP: Record = { + '大吉': '上午8点至10点出发最佳', + '吉': '上午9点至11点出发较佳', + '中吉': '上午10点至12点可出行', + '平': '中午前后出行即可', + '中平': '下午出行较为稳妥', + '小凶': '出行需谨慎,注意安全', + '凶': '不宜远行,近处活动为佳', +} + +const SUITABLE_SUBJECTS_MAP: Record = { + '大吉': '文学、哲学、历史等文科类,或管理、领导力相关', + '吉': '语言、艺术、社会科学等,或技术类基础学科', + '中吉': '数学、逻辑、计算机等理科类', + '平': '实用技能、职业培训等应用型学科', + '中平': '通识类课程,宜广泛涉猎', + '小凶': '宜复习巩固已有知识,不宜开启新领域', + '凶': '宜休息调整,轻松阅读即可', +} + +const SUITABLE_OCCASION_MAP: Record = { + '大吉': '商务宴请、高端社交、行业峰会', + '吉': '朋友聚会、团队活动、文化交流', + '中吉': '社区活动、兴趣小组、学习交流', + '平': '日常社交、同事聚餐', + '中平': '小范围聚会、线上交流', + '小凶': '减少社交活动,宜小范围相处', + '凶': '不宜参加社交活动,宜独处', +} + +const SUITABLE_TRANSPORT_MAP: Record = { + '大吉': '飞机、高铁等快速交通工具', + '吉': '高铁、自驾等舒适出行方式', + '中吉': '火车、大巴等常规交通', + '平': '公共交通、地铁等日常出行', + '中平': '短途公交、步行等', + '小凶': '出行需注意安全,避免自驾', + '凶': '不宜远行,就近活动为佳', +} + +const DIRECTION_LIST = ['东方', '南方', '西方', '北方', '东南', '西南', '东北', '西北'] + +function findPalace(chart: ZiweiChart, palaceType: PalaceType): Palace | undefined { + return chart.palaces.find(p => p.palaceType === palaceType) +} + +function countMinorStars(palace: Palace | undefined, starNames: string[]): number { + if (palace == null) return 0 + let count = 0 + for (const star of starNames) { + if (palace.minorStars.includes(star)) count++ + } + return count +} + +function countMajorStars(palace: Palace | undefined, starNames: string[]): number { + if (palace == null) return 0 + let count = 0 + for (const starInfo of palace.majorStars) { + if (starNames.includes(starInfo.star)) count++ + } + return count +} + +function clampScore(score: number): number { + return Math.max(0, Math.min(100, Math.floor(score))) +} + +export function analyzeLoveFortune(chart: ZiweiChart, date: Date): LoveFortuneDetail { + const fuqiPalace = findPalace(chart, PalaceType.FUQI) + const baseScore = fuqiPalace?.score ?? 50 + + const peachBlossomCount = + countMinorStars(fuqiPalace, PEACH_BLOSSOM_STARS) + + countMajorStars(fuqiPalace, PEACH_BLOSSOM_STARS) + const peachBonus = peachBlossomCount * 5 + + const dayAdj = fuqiPalace ? calculateDailyAdjustment(fuqiPalace, date) : 0 + const loveScore = clampScore(baseScore + peachBonus + dayAdj) + const luckLevel = determineLuckLevel(loveScore) + + const peachBlossomIndex = getPeachBlossomLevel(peachBlossomCount) + + return { + loveScore, + overallLoveLuck: luckLevel, + peachBlossomIndex, + singleAdvice: getSingleAdvice(luckLevel, peachBlossomCount), + datingAdvice: getDatingAdvice(luckLevel), + marriageAdvice: getMarriageAdvice(luckLevel), + bestDatingTime: DATING_TIME_MAP[luckLevel] ?? '下午2点至4点', + } +} + +function getPeachBlossomLevel(count: number): string { + if (count >= 3) return '极旺' + if (count >= 2) return '旺盛' + if (count >= 1) return '一般' + return '偏弱' +} + +function getSingleAdvice(luckLevel: string, peachCount: number): string { + if (peachCount >= 2) return '桃花旺盛,有望遇到心仪对象,可主动出击' + if (luckLevel === '大吉' || luckLevel === '吉') return '感情运势良好,可多参加社交活动,增加遇见良缘的机会' + if (luckLevel === '中吉' || luckLevel === '平') return '感情运势平稳,宜耐心等待,不宜急于求成' + return '感情运势偏弱,宜先提升自我,静待良缘' +} + +function getDatingAdvice(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '感情甜蜜,适合深入交流,增进彼此了解' + if (luckLevel === '中吉' || luckLevel === '平') return '感情平稳,保持日常沟通,多关心对方需求' + if (luckLevel === '中平') return '感情略显平淡,可制造小惊喜,增添情趣' + return '感情易生波折,需多包容理解,避免争执' +} + +function getMarriageAdvice(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '婚姻运势良好,适合商讨婚事或增进夫妻感情' + if (luckLevel === '中吉' || luckLevel === '平') return '婚姻运势平稳,宜相互扶持,共同经营家庭' + if (luckLevel === '中平') return '婚姻运势一般,需多沟通协调,避免误解' + return '婚姻运势欠佳,需多忍让包容,共同面对困难' +} + +export function analyzeStudyFortune(chart: ZiweiChart, date: Date): StudyFortuneDetail { + const guanluPalace = findPalace(chart, PalaceType.GUANLU) + const baseScore = guanluPalace?.score ?? 50 + + const studyStarCount = countMinorStars(guanluPalace, STUDY_STARS) + const studyBonus = studyStarCount * 6 + + const dayAdj = guanluPalace ? calculateDailyAdjustment(guanluPalace, date) : 0 + const studyScore = clampScore(baseScore + studyBonus + dayAdj) + const luckLevel = determineLuckLevel(studyScore) + + return { + studyScore, + overallStudyLuck: luckLevel, + focusIndex: getFocusLevel(studyScore), + memoryIndex: getMemoryLevel(studyScore), + studyAdvice: getStudyAdvice(luckLevel, studyStarCount), + examLuck: getExamLuck(luckLevel), + bestStudyTime: STUDY_TIME_MAP[luckLevel] ?? '上午9点至11点', + suitableSubjects: SUITABLE_SUBJECTS_MAP[luckLevel] ?? '通识类课程', + studyEnvironmentAdvice: getStudyEnvironmentAdvice(luckLevel), + } +} + +function getFocusLevel(score: number): string { + if (score >= 80) return '高度集中' + if (score >= 60) return '较为集中' + if (score >= 40) return '一般' + return '容易分心' +} + +function getMemoryLevel(score: number): string { + if (score >= 80) return '记忆力佳' + if (score >= 60) return '记忆力良好' + if (score >= 40) return '记忆力一般' + return '记忆力偏弱' +} + +function getStudyAdvice(luckLevel: string, studyStarCount: number): string { + if (studyStarCount >= 2) return '文昌文曲双星照耀,学业运势极佳,适合攻克难题和备考' + if (luckLevel === '大吉' || luckLevel === '吉') return '学业运势良好,适合深入学习新知识,效率较高' + if (luckLevel === '中吉' || luckLevel === '平') return '学业运势平稳,按计划学习即可,注意劳逸结合' + return '学业运势偏弱,宜复习巩固已有知识,不宜开启新领域' +} + +function getExamLuck(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '考试运佳,发挥出色' + if (luckLevel === '中吉' || luckLevel === '平') return '考试运平稳,正常发挥' + if (luckLevel === '中平') return '考试运一般,需多加准备' + return '考试运欠佳,需格外用心准备' +} + +function getStudyEnvironmentAdvice(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '宜在安静明亮的环境中学习,效率最佳' + if (luckLevel === '中吉' || luckLevel === '平') return '宜选择图书馆或自习室等安静场所' + return '宜在熟悉的环境中学习,减少外界干扰' +} + +export function analyzeSocialFortune(chart: ZiweiChart, date: Date): SocialFortuneDetail { + const xiongPalace = findPalace(chart, PalaceType.XIONG) + const baseScore = xiongPalace?.score ?? 50 + + const nobleStarCount = countMinorStars(xiongPalace, NOBLE_STARS) + const nobleBonus = nobleStarCount * 5 + + const dayAdj = xiongPalace ? calculateDailyAdjustment(xiongPalace, date) : 0 + const socialScore = clampScore(baseScore + nobleBonus + dayAdj) + const luckLevel = determineLuckLevel(socialScore) + + return { + socialScore, + overallSocialLuck: luckLevel, + popularityIndex: getPopularityLevel(socialScore), + communicationAbility: getCommunicationLevel(socialScore), + socialAdvice: getSocialAdvice(luckLevel, nobleStarCount), + nobleLuck: getNobleLuck(nobleStarCount), + bestSocialTime: SOCIAL_TIME_MAP[luckLevel] ?? '下午2点至4点', + suitableOccasion: SUITABLE_OCCASION_MAP[luckLevel] ?? '日常社交', + interpersonalAdvice: getInterpersonalAdvice(luckLevel), + } +} + +function getPopularityLevel(score: number): string { + if (score >= 80) return '人气旺盛' + if (score >= 60) return '人缘良好' + if (score >= 40) return '人缘一般' + return '人缘偏弱' +} + +function getCommunicationLevel(score: number): string { + if (score >= 80) return '表达流畅' + if (score >= 60) return '沟通顺畅' + if (score >= 40) return '沟通一般' + return '沟通不畅' +} + +function getSocialAdvice(luckLevel: string, nobleStarCount: number): string { + if (nobleStarCount >= 2) return '贵人星照耀,社交运势极佳,易获他人帮助与支持' + if (luckLevel === '大吉' || luckLevel === '吉') return '社交运势良好,适合拓展人脉,参加社交活动' + if (luckLevel === '中吉' || luckLevel === '平') return '社交运势平稳,保持正常交往即可' + return '社交运势偏弱,宜减少不必要的应酬,专注自身' +} + +function getNobleLuck(nobleStarCount: number): string { + if (nobleStarCount >= 3) return '贵人运极旺,多有助力' + if (nobleStarCount >= 2) return '贵人运旺盛,易遇贵人' + if (nobleStarCount >= 1) return '贵人运一般,偶有助力' + return '贵人运偏弱,需自力更生' +} + +function getInterpersonalAdvice(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '人际关系和谐,宜主动关心他人,增进感情' + if (luckLevel === '中吉' || luckLevel === '平') return '人际关系平稳,保持真诚待人即可' + if (luckLevel === '中平') return '人际关系略显平淡,可主动联络朋友' + return '人际关系易生摩擦,需多忍让,避免冲突' +} + +export function analyzeTravelFortune(chart: ZiweiChart, date: Date): TravelFortuneDetail { + const qianPalace = findPalace(chart, PalaceType.QIAN) + const baseScore = qianPalace?.score ?? 50 + + const travelStarCount = countMinorStars(qianPalace, TRAVEL_STARS) + const travelBonus = travelStarCount * 5 + + const dayAdj = qianPalace ? calculateDailyAdjustment(qianPalace, date) : 0 + const travelScore = clampScore(baseScore + travelBonus + dayAdj) + const luckLevel = determineLuckLevel(travelScore) + + return { + travelScore, + overallTravelLuck: luckLevel, + safetyIndex: getSafetyLevel(travelScore), + smoothnessIndex: getSmoothnessLevel(travelScore), + travelAdvice: getTravelAdvice(luckLevel, travelStarCount), + bestTravelTime: TRAVEL_TIME_MAP[luckLevel] ?? '中午前后', + suitableTransport: SUITABLE_TRANSPORT_MAP[luckLevel] ?? '常规交通', + precautions: getPrecautions(luckLevel), + nobleDirection: getNobleDirection(chart, date), + } +} + +function getSafetyLevel(score: number): string { + if (score >= 80) return '安全指数高' + if (score >= 60) return '安全指数良好' + if (score >= 40) return '安全指数一般' + return '安全指数偏低' +} + +function getSmoothnessLevel(score: number): string { + if (score >= 80) return '顺畅无阻' + if (score >= 60) return '较为顺畅' + if (score >= 40) return '一般' + return '可能受阻' +} + +function getTravelAdvice(luckLevel: string, travelStarCount: number): string { + if (travelStarCount >= 1) return '天马星动,出行运势佳,适合出差旅行' + if (luckLevel === '大吉' || luckLevel === '吉') return '出行运势良好,旅途顺利,可安排出行计划' + if (luckLevel === '中吉' || luckLevel === '平') return '出行运势平稳,按计划出行即可' + return '出行运势欠佳,建议减少不必要的外出' +} + +function getPrecautions(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '出行顺利,注意常规安全即可' + if (luckLevel === '中吉' || luckLevel === '平') return '出行注意交通安全,提前查看天气' + if (luckLevel === '中平') return '出行需谨慎,注意人身和财物安全' + return '出行风险较高,务必注意安全,避免夜间出行' +} + +function getNobleDirection(chart: ZiweiChart, _date: Date): string { + const mingGong = findPalace(chart, PalaceType.MING) + if (mingGong == null) return '东方' + + const hasJiStar = mingGong.majorStars.some(s => s.nature === StarNature.JI) + const branchIndex = EarthlyBranch.index(chart.mingGongBranch) - 1 + const directionIndex = hasJiStar + ? (branchIndex + 1) % DIRECTION_LIST.length + : (branchIndex + 3) % DIRECTION_LIST.length + + return DIRECTION_LIST[directionIndex] +} + +// ====== 新增维度分析函数 ====== + +// 煞星列表(用于冲突预警) +const EVIL_STARS = ['QINGYANG', 'TUOLUO', 'HUOXING', 'LINGXING', 'DIKONG', 'DIJIE'] + +const RELAXATION_METHODS: Record = { + '大吉': '户外运动、与朋友聚会', + '吉': '听音乐、阅读、散步', + '中吉': '冥想、瑜伽、深呼吸', + '平': '烹饪、手工、整理', + '中平': '独处、写日记、泡茶', + '小凶': '静坐休息、调整呼吸', + '凶': '寻求倾诉、心理咨询', +} + +const RELAXATION_TIME_MAP: Record = { + '大吉': '清晨6点至8点,精力最充沛时', + '吉': '上午9点至11点,心情最佳时', + '中吉': '午后1点至3点,适合放松', + '平': '傍晚5点至7点,工作结束后', + '中平': '晚上8点至10点,安静时分', + '小凶': '需要时随时调整,建议睡前静心', + '凶': '建议寻求专业帮助,不宜独自硬撑', +} + +const FAMILY_ACTIVITY_MAP: Record = { + '大吉': '家庭聚餐、户外郊游、全家旅行', + '吉': '一起做饭、看电影、散步', + '中吉': '在家聊天、一起读书、手工活动', + '平': '日常陪伴、简单家务', + '中平': '各自活动,保持适度距离', + '小凶': '减少接触,避免冲突', + '凶': '暂不宜家庭活动,各自冷静', +} + +const MAIN_WEALTH_MAP: Record = { + '大吉': '正财旺盛,收入稳定增长,宜把握主业机会', + '吉': '正财良好,工作收入可观,宜踏实进取', + '中吉': '正财平稳,按部就班即可', + '平': '正财一般,宜减少非必要开支', + '中平': '正财偏弱,守成为主', + '小凶': '正财不利,谨防工作变动影响收入', + '凶': '正财受损,需格外谨慎', +} + +const SIDE_WEALTH_MAP: Record = { + '大吉': '偏财旺盛,适合小额投资,有望获得意外之财', + '吉': '偏财良好,可适度参与理财', + '中吉': '偏财一般,宜谨慎投资', + '平': '偏财平平,不宜投机', + '中平': '偏财偏弱,避免风险投资', + '小凶': '偏财不利,谨防投资损失', + '凶': '偏财受损,远离投机风险', +} + +const SPENDING_TENDENCY_MAP: Record = { + '大吉': '消费理性,物有所值', + '吉': '适度消费,注重品质', + '中吉': '消费平稳,按需购买', + '平': '消费欲望一般,量入为出', + '中平': '消费偏谨慎,精打细算', + '小凶': '易冲动消费,需控制开支', + '凶': '消费欲望强烈,谨防过度支出', +} + +const INVESTMENT_TIMING_MAP: Record = { + '大吉': '非常适合投资理财,可把握良机', + '吉': '适合进行风险评估后的投资', + '中吉': '适合观望,不宜急于出手', + '平': '适合保守理财,避免高风险', + '中平': '不宜新投资,守好现有资产', + '小凶': '投资风险较高,建议暂缓', + '凶': '不宜投资,谨防损失', +} + +const COLLEAGUE_RELATION_MAP: Record = { + '大吉': '同事关系融洽,合作顺畅,宜主动协调', + '吉': '同事关系良好,沟通愉快', + '中吉': '同事关系一般,保持正常合作', + '平': '同事关系平平,做好本职工作', + '中平': '同事关系略显疏离,注意沟通方式', + '小凶': '易生误会,谨言慎行', + '凶': '同事关系紧张,避免冲突', +} + +const LEADERSHIP_VIEW_MAP: Record = { + '大吉': '领导赏识有加,宜把握表现机会', + '吉': '领导印象良好,可适度展示能力', + '中吉': '领导评价一般,稳扎稳打即可', + '平': '领导关注度一般,做好本职工作', + '中平': '领导意图不明,不宜贸然表现', + '小凶': '易被领导误解,注意沟通方式', + '凶': '领导关系紧张,谨言慎行', +} + +const PROJECT_DECISION_MAP: Record = { + '大吉': '适合推进重要项目,决策时机成熟', + '吉': '适合参与项目讨论,贡献想法', + '中吉': '项目进展平稳,按计划推进', + '平': '项目无大变化,做好分内事', + '中平': '项目推进缓慢,需耐心等待', + '小凶': '项目风险较高,需谨慎评估', + '凶': '不宜启动新项目,宜观望', +} + +const PROMOTION_LUCK_MAP: Record = { + '大吉': '晋升机会明显,宜主动争取', + '吉': '晋升有望,可适度表现', + '中吉': '晋升机会一般,积累资历', + '平': '晋升机遇平平,踏实工作', + '中平': '晋升时机未到,继续积累', + '小凶': '晋升阻力较大,暂缓争取', + '凶': '晋升困难,宜调整心态', +} + +const DIET_ADVICE_MAP: Record = { + '大吉': '宜清淡饮食,多吃蔬菜水果,补充维生素', + '吉': '宜均衡营养,适当增加蛋白质摄入', + '中吉': '宜规律饮食,避免暴饮暴食', + '平': '注意饮食卫生,少食生冷', + '中平': '饮食需节制,避免油腻', + '小凶': '肠胃敏感,宜清淡易消化食物', + '凶': '消化功能弱,忌辛辣刺激', +} + +const EXERCISE_ADVICE_MAP: Record = { + '大吉': '适合中等强度运动,如慢跑、游泳', + '吉': '适合轻度运动,如快走、瑜伽', + '中吉': '适度运动,以不疲劳为度', + '平': '轻微活动即可,如散步、拉伸', + '中平': '以休息为主,减少运动量', + '小凶': '避免剧烈运动,宜静养', + '凶': '不宜运动,充分休息', +} + +const REST_ADVICE_MAP: Record = { + '大吉': '作息规律,睡眠质量佳', + '吉': '保持良好作息,早睡早起', + '中吉': '注意劳逸结合,避免熬夜', + '平': '作息一般,需保证充足睡眠', + '中平': '易疲劳,需增加休息时间', + '小凶': '睡眠质量差,建议睡前放松', + '凶': '严重疲劳,需充分休养', +} + +const FOCUS_AREA_MAP: Record = { + '大吉': '各系统良好,注意常规保养', + '吉': '注意消化系统保养', + '中吉': '关注颈椎和腰椎健康', + '平': '注意预防感冒等季节性疾病', + '中平': '关注心血管健康,适度监测', + '小凶': '重点关注肠胃和消化系统', + '凶': '需全面体检,重点关注慢性病', +} + +// 命宫主星 → 每日金句 +const QUOTE_MAP: Record = { + 'ZIWEI': '帝星高照,今日宜把握领导机会,展现统帅风范', + 'TIANJI': '天机灵动,今日宜谋定后动,智取而非力敌', + 'TAIYANG': '阳光普照,今日宜以热情感染他人,收获信任', + 'WUQU': '武曲刚毅,今日宜坚韧不拔,攻坚克难', + 'TIANTONG': '天同福星,今日宜以和为贵,享受生活美好', + 'LIANCHEN': '廉贞明理,今日宜以诚待人,化解是非', + 'TIANFU': '天府守成,今日宜稳扎稳打,巩固根基', + 'TAIYIN': '太阴内敛,今日宜静心思考,沉淀自我', + 'TANLANG': '贪狼灵动,今日宜发挥魅力,拓展人脉资源', + 'JUMEN': '巨门善辩,今日宜谨言慎行,以理服人', + 'TIANXIANG': '天相辅佐,今日宜助人为乐,积累善缘', + 'TIANLIANG': '天梁荫护,今日宜关怀他人,必有福报', + 'QISHA': '七杀威猛,今日宜果断决策,开创局面', + 'POJUN': '破军勇进,今日宜破旧立新,勇于变革', +} + +const QUOTE_MAP_FLAT: Record = { + '大吉': '万事顺遂,乘势而上,必有所成', + '吉': '诸事顺利,用心经营,收获可期', + '中吉': '运势平稳,稳中求进,循序渐进', + '平': '平淡是真,守正出奇,静待时机', + '中平': '运势平平,宜静不宜动,蓄势待发', + '小凶': '运势偏弱,以守为攻,待时而动', + '凶': '运势欠佳,避其锋芒,养精蓄锐', +} + +// 宜忌映射 +const SUITABLE_MAP: Record> = { + love: { + '大吉': ['主动表达心意', '约会浪漫', '商讨婚事'], + '吉': ['增进感情交流', '共同规划未来', '制造小惊喜'], + '中吉': ['保持日常沟通', '关心对方需求', '适度陪伴'], + '平': ['维持现状', '彼此尊重', '适度关心'], + '中平': ['自我反思', '给对方空间', '培养共同兴趣'], + '小凶': ['冷静思考', '保持距离', '避免冲突'], + '凶': ['独处调整', '反思自我', '暂缓重大决定'], + }, + study: { + '大吉': ['攻克难题', '学习新知识', '参加考试'], + '吉': ['深入学习', '系统复习', '技能提升'], + '中吉': ['按计划学习', '巩固基础', '查漏补缺'], + '平': ['日常学习', '完成作业', '保持节奏'], + '中平': ['复习旧知识', '轻松阅读', '归纳总结'], + '小凶': ['暂缓新领域', '巩固已有', '调整心态'], + '凶': ['充分休息', '放松心情', '暂避学习压力'], + }, + social: { + '大吉': ['参加聚会', '拓展人脉', '主动交流'], + '吉': ['联络朋友', '参加活动', '团队合作'], + '中吉': ['保持联系', '适度社交', '线上交流'], + '平': ['日常交往', '礼貌回应', '完成社交任务'], + '中平': ['减少应酬', '小范围聚会', '独处充电'], + '小凶': ['避免冲突', '减少社交', '专注自我'], + '凶': ['独处调整', '远离是非', '修养身心'], + }, + travel: { + '大吉': ['长途旅行', '出差远行', '探索新地'], + '吉': ['短途出行', '休闲旅游', '探亲访友'], + '中吉': ['近郊活动', '日常外出', '适度的出行'], + '平': ['按计划出行', '公共交通', '提前规划'], + '中平': ['减少外出', '短途步行', '居家活动'], + '小凶': ['谨慎出行', '避免自驾', '注意安全'], + '凶': ['取消行程', '居家休息', '避免外出'], + }, + career: { + '大吉': ['争取项目', '展现能力', '主动沟通'], + '吉': ['推进工作', '团队协作', '提出建议'], + '中吉': ['按部就班', '完成本分', '稳扎稳打'], + '平': ['做好本职工作', '记录总结', '保持节奏'], + '中平': ['减少冒险', '谨慎行事', '谨言慎行'], + '小凶': ['避免重要决策', '低调行事', '保护自己'], + '凶': ['暂避风头', '减少工作变动', '注意情绪管理'], + }, + wealth: { + '大吉': ['投资理财', '规划财务', '拓展收入'], + '吉': ['适度理财', '储蓄规划', '精打细算'], + '中吉': ['保守理财', '按需消费', '记帐管理'], + '平': ['减少开支', '避免冲动消费', '货比三家'], + '中平': ['守住预算', '延迟消费', '精简开支'], + '小凶': ['避免投资', '减少大额支出', '谨慎消费'], + '凶': ['暂停投资', '做预算', '减少非必要开支'], + }, +} + +const AVOID_MAP: Record> = { + love: { + '大吉': ['翻旧账', '过度干涉对方', '忽视对方感受'], + '吉': ['冷漠对待', '敷衍了事', '忽视细节'], + '中吉': ['急于求成', '过度热情', '忽视自我'], + '平': ['无理取闹', '过分依赖', '情绪化表达'], + '中平': ['冷战', '猜疑', '说伤人的话'], + '小凶': ['做重大决定', '激化矛盾', '互相指责'], + '凶': ['争吵', '提分手', '翻旧账'], + }, + study: { + '大吉': ['拖延', '分心', '浅尝辄止'], + '吉': ['三心二意', '目标过高', '急于求成'], + '中吉': ['过度疲劳', '同时学多门', '突击式学习'], + '平': ['分心走神', '拖延作业', '眼高手低'], + '中平': ['开启新领域', '挑战高难度', '与他人比较'], + '小凶': ['熬夜学习', '高压学习', '过度焦虑'], + '凶': ['强迫学习', '过度责备', '与他人攀比'], + }, + social: { + '大吉': ['自我封闭', '拒绝好意', '冷漠待人'], + '吉': ['过度应酬', '应付了事', '忽视朋友'], + '中吉': ['勉强社交', '过度付出', '讨好他人'], + '平': ['说人是非', '过度热情', '轻信他人'], + '中平': ['参与冲突', '站队表态', '过度社交'], + '小凶': ['参加大型活动', '与陌生人深交', '涉及利益'], + '凶': ['参与是非', '与人争执', '重要社交'], + }, + travel: { + '大吉': ['无计划出行', '忽视安全', '过度疲劳驾驶'], + '吉': ['夜间出行', '独自远行', '冒险活动'], + '中吉': ['长途自驾', '野外探险', '极限运动'], + '平': ['夜间自驾', '恶劣天气出行', '复杂路况'], + '中平': ['不必要远行', '携带贵重物品', '匆忙出行'], + '小凶': ['自驾远行', '独自出行', '夜不归宿'], + '凶': ['任何出行', '独自外出', '夜间活动'], + }, + career: { + '大吉': ['傲慢自大', '独断专行', '抢功邀功'], + '吉': ['畏首畏尾', '推卸责任', '消极怠工'], + '中吉': ['冒险决策', '越级汇报', '挑战权威'], + '平': ['拖延工作', '敷衍了事', '推卸责任'], + '中平': ['主动揽活', '参与竞争', '站队表态'], + '小凶': ['顶撞领导', '重要决策', '参与矛盾'], + '凶': ['冲突争执', '重大变动', '签署重要文件'], + }, + wealth: { + '大吉': ['过度消费', '冲动投资', '炫耀财富'], + '吉': ['攀比消费', '跟风投资', '冲动购物'], + '中吉': ['高风险投资', '借钱消费', '透支信用'], + '平': ['大额消费', '超前消费', '借贷消费'], + '中平': ['投资新项目', '借钱给他人', '担保借贷'], + '小凶': ['高风险投资', '大额支出', '借钱'], + '凶': ['任何投资', '大额消费', '借贷担保'], + }, +} + +const CONFLICT_TYPE_MAP: Record = { + FUQI: '感情', + GUANLU: '事业', + XIONG: '人际', +} + +// 分时段运势映射 +const TIME_SLOT_MORNING_MAP: Record = { + '大吉': '上午精力充沛,思路清晰,适合处理重要事务', + '吉': '上午状态良好,适合规划一天的工作和生活', + '中吉': '上午较为平稳,逐步进入工作状态', + '平': '上午状态一般,宜从简单事务开始', + '中平': '上午略显疲惫,宜适度活动后投入工作', + '小凶': '上午状态不佳,宜放慢节奏', + '凶': '上午精神不振,宜充分休息后再行动', +} + +const TIME_SLOT_AFTERNOON_MAP: Record = { + '大吉': '下午运势走高,适合推进重要项目和沟通协调', + '吉': '下午状态良好,适合团队协作和会议讨论', + '中吉': '下午保持平稳,适合完成计划内工作', + '平': '下午稍有倦怠,注意劳逸结合', + '中平': '下午精力下降,宜处理简单事务', + '小凶': '下午易疲劳,注意适当休息', + '凶': '下午情绪低落,避免重要决策', +} + +const TIME_SLOT_EVENING_MAP: Record = { + '大吉': '晚上适合社交活动,享受美好时光', + '吉': '晚上适合休闲放松,与家人朋友相处', + '中吉': '晚上宜安静独处,阅读或学习', + '平': '晚上适度放松,不宜熬夜', + '中平': '晚上适合早早休息,为明日蓄力', + '小凶': '晚上情绪易波动,宜安静休息', + '凶': '晚上宜早睡,避免熬夜和情绪化', +} + +export function analyzeEmotionalFortune(chart: ZiweiChart, date: Date): EmotionalFortuneDetail { + const mingPalace = findPalace(chart, PalaceType.MING) + const fubenPalace = findPalace(chart, PalaceType.FUBEN) + const mingScore = mingPalace?.score ?? 50 + const fubenScore = fubenPalace?.score ?? 50 + const baseScore = Math.floor((mingScore + fubenScore) / 2) + + const dayAdjMing = mingPalace ? calculateDailyAdjustment(mingPalace, date) : 0 + const dayAdjFuben = fubenPalace ? calculateDailyAdjustment(fubenPalace, date) : 0 + const emotionalScore = clampScore(baseScore + Math.floor((dayAdjMing + dayAdjFuben) / 2)) + const luckLevel = determineLuckLevel(emotionalScore) + + // 情绪指数 + const moodIndex = getMoodLevel(emotionalScore) + + // 压力水平:基于煞星数量 + const evilMingCount = countMinorStars(mingPalace, EVIL_STARS) + countMajorStars(mingPalace, EVIL_STARS) + const evilFubenCount = countMinorStars(fubenPalace, EVIL_STARS) + countMajorStars(fubenPalace, EVIL_STARS) + const totalEvil = evilMingCount + evilFubenCount + const stressLevel = getStressLevel(totalEvil, emotionalScore) + + return { + emotionalScore, + overallEmotionalLuck: luckLevel, + moodIndex, + stressLevel, + emotionalAdvice: getEmotionalAdvice(luckLevel, totalEvil), + relaxationMethod: RELAXATION_METHODS[luckLevel] ?? '听音乐、散步', + bestRelaxationTime: RELAXATION_TIME_MAP[luckLevel] ?? '傍晚时分', + } +} + +function getMoodLevel(score: number): string { + if (score >= 85) return '心情愉悦' + if (score >= 70) return '心情良好' + if (score >= 60) return '心情平稳' + if (score >= 50) return '心情一般' + if (score >= 40) return '略显低落' + return '心情不佳' +} + +function getStressLevel(evilCount: number, score: number): string { + if (evilCount >= 3 && score < 50) return '压力较大' + if (evilCount >= 2 && score < 60) return '压力偏大' + if (evilCount >= 1 && score < 70) return '偶有压力' + if (score >= 80) return '轻松自在' + if (score >= 60) return '较为轻松' + return '略有压力' +} + +function getEmotionalAdvice(luckLevel: string, evilCount: number): string { + if (evilCount >= 3) return '煞星汇聚,情绪易受外界干扰,建议独处静心,避免冲动决策' + if (evilCount >= 2) return '负面能量较多,注意调节情绪,可通过运动或倾诉释放压力' + if (evilCount >= 1) return '稍有干扰,保持平常心,适度放松即可' + if (luckLevel === '大吉' || luckLevel === '吉') return '心情愉悦,适合积极行动,用正能量感染他人' + if (luckLevel === '中吉' || luckLevel === '平') return '情绪稳定,保持现状即可' + return '情绪偏弱,宜放松身心,避免过度思虑' +} + +export function analyzeFamilyFortune(chart: ZiweiChart, date: Date): FamilyFortuneDetail { + const fuPalace = findPalace(chart, PalaceType.FU) + const zinvPalace = findPalace(chart, PalaceType.ZINV) + const fuScore = fuPalace?.score ?? 50 + const zinvScore = zinvPalace?.score ?? 50 + const baseScore = Math.floor((fuScore + zinvScore) / 2) + + const dayAdjFu = fuPalace ? calculateDailyAdjustment(fuPalace, date) : 0 + const dayAdjZinv = zinvPalace ? calculateDailyAdjustment(zinvPalace, date) : 0 + const familyScore = clampScore(baseScore + Math.floor((dayAdjFu + dayAdjZinv) / 2)) + const luckLevel = determineLuckLevel(familyScore) + + return { + familyScore, + overallFamilyLuck: luckLevel, + familyHarmony: getFamilyHarmonyLevel(familyScore), + parentRelationship: getParentRelationship(fuPalace, luckLevel), + childRelationship: getChildRelationship(zinvPalace, luckLevel), + familyAdvice: getFamilyAdvice(luckLevel), + suitableFamilyActivity: FAMILY_ACTIVITY_MAP[luckLevel] ?? '日常陪伴', + } +} + +function getFamilyHarmonyLevel(score: number): string { + if (score >= 80) return '家庭和谐' + if (score >= 60) return '家庭和睦' + if (score >= 50) return '家庭一般' + if (score >= 40) return '略显紧张' + return '家庭关系紧张' +} + +function getParentRelationship(palace: Palace | undefined, luckLevel: string): string { + if (palace == null) return '与父母关系一般' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) return '与父母易生摩擦,需多包容理解' + if (evilCount >= 1) return '与父母偶有分歧,注意沟通方式' + if (luckLevel === '大吉' || luckLevel === '吉') return '与父母关系融洽,适合多沟通交流' + if (luckLevel === '中吉' || luckLevel === '平') return '与父母关系正常,保持联系即可' + return '与父母关系平淡,宜主动关心' +} + +function getChildRelationship(palace: Palace | undefined, luckLevel: string): string { + if (palace == null) return '与子女关系一般' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) return '与子女易生矛盾,需耐心沟通' + if (evilCount >= 1) return '与子女偶有分歧,注意引导方式' + if (luckLevel === '大吉' || luckLevel === '吉') return '与子女相处愉快,适合亲子活动' + if (luckLevel === '中吉' || luckLevel === '平') return '与子女关系正常,保持日常互动' + return '与子女关系平淡,宜多陪伴' +} + +function getFamilyAdvice(luckLevel: string): string { + if (luckLevel === '大吉' || luckLevel === '吉') return '家庭运势良好,是一家团聚增进感情的好时机' + if (luckLevel === '中吉' || luckLevel === '平') return '家庭运势平稳,保持日常的关心和陪伴即可' + if (luckLevel === '中平') return '家庭关系略显平淡,可主动关心家人' + return '家庭关系易生摩擦,需多忍让包容,避免争执' +} + +export function analyzeWealthFortune(chart: ZiweiChart, date: Date): WealthFortuneDetail { + const caiPalace = findPalace(chart, PalaceType.CAI) + const baseScore = caiPalace?.score ?? 50 + const dayAdj = caiPalace ? calculateDailyAdjustment(caiPalace, date) : 0 + const wealthScore = clampScore(baseScore + dayAdj) + const luckLevel = determineLuckLevel(wealthScore) + + return { + wealthScore, + overallWealthLuck: luckLevel, + mainWealth: MAIN_WEALTH_MAP[luckLevel] ?? '正财平稳', + sideWealth: SIDE_WEALTH_MAP[luckLevel] ?? '偏财一般', + spendingTendency: SPENDING_TENDENCY_MAP[luckLevel] ?? '消费平稳', + investmentTiming: INVESTMENT_TIMING_MAP[luckLevel] ?? '谨慎投资', + wealthAdvice: getWealthAdvice(luckLevel, caiPalace), + } +} + +function getWealthAdvice(luckLevel: string, palace: Palace | undefined): string { + if (palace == null) return '财运运势不明,谨慎理财为宜' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) return '财帛宫煞星汇聚,财运波动较大,需格外谨慎理财,避免高风险投资' + if (luckLevel === '大吉' || luckLevel === '吉') return '财运亨通,宜把握良机进行理财规划,收入有望增长' + if (luckLevel === '中吉' || luckLevel === '平') return '财运平稳,宜保守理财,量入为出' + return '财运欠佳,宜守不宜攻,避免大额支出和投资风险' +} + +export function analyzeCareerFortune(chart: ZiweiChart, date: Date): CareerFortuneDetail { + const guanluPalace = findPalace(chart, PalaceType.GUANLU) + const baseScore = guanluPalace?.score ?? 50 + const dayAdj = guanluPalace ? calculateDailyAdjustment(guanluPalace, date) : 0 + const careerScore = clampScore(baseScore + dayAdj) + const luckLevel = determineLuckLevel(careerScore) + + return { + careerScore, + overallCareerLuck: luckLevel, + colleagueRelation: COLLEAGUE_RELATION_MAP[luckLevel] ?? '同事关系一般', + leadershipView: LEADERSHIP_VIEW_MAP[luckLevel] ?? '领导评价一般', + projectDecision: PROJECT_DECISION_MAP[luckLevel] ?? '项目进展平稳', + promotionLuck: PROMOTION_LUCK_MAP[luckLevel] ?? '晋升机会一般', + careerAdvice: getCareerAdvice(luckLevel, guanluPalace), + } +} + +function getCareerAdvice(luckLevel: string, palace: Palace | undefined): string { + if (palace == null) return '事业运势不明,谨慎行事' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) return '官禄宫煞星汇聚,事业阻碍较多,谨言慎行,避免与人冲突' + if (luckLevel === '大吉' || luckLevel === '吉') return '事业运势吉显,适合处理重要事务,可主动争取机会,必有收获' + if (luckLevel === '中吉' || luckLevel === '平') return '事业运势平稳,按计划完成工作即可,避免冒险决策' + return '事业运势欠佳,建议处理常规工作,避免重要决策和冲突' +} + +export function analyzeHealthFortune(chart: ZiweiChart, date: Date): HealthFortuneDetail { + const jiluPalace = findPalace(chart, PalaceType.JILU) + const baseScore = jiluPalace?.score ?? 50 + const dayAdj = jiluPalace ? calculateDailyAdjustment(jiluPalace, date) : 0 + const healthScore = clampScore(baseScore + dayAdj) + const luckLevel = determineLuckLevel(healthScore) + + return { + healthScore, + overallHealthLuck: luckLevel, + dietAdvice: DIET_ADVICE_MAP[luckLevel] ?? '注意饮食均衡', + exerciseAdvice: EXERCISE_ADVICE_MAP[luckLevel] ?? '适度运动', + restAdvice: REST_ADVICE_MAP[luckLevel] ?? '保证充足休息', + focusArea: getFocusArea(jiluPalace, luckLevel), + healthAdvice: getHealthAdvice(luckLevel, jiluPalace), + } +} + +function getFocusArea(palace: Palace | undefined, luckLevel: string): string { + if (palace == null) return '注意常规保养' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 3) return '身体多处需关注,建议进行全面体检' + if (evilCount >= 2) return '重点关注消化系统和心血管健康' + if (evilCount >= 1) return '注意预防季节性疾病和慢性疲劳' + return FOCUS_AREA_MAP[luckLevel] ?? '注意常规保养' +} + +function getHealthAdvice(luckLevel: string, palace: Palace | undefined): string { + if (palace == null) return '健康运势不明,注意休息' + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) return '疾厄宫煞星汇聚,健康状况需格外关注,建议规律作息,定期体检' + if (luckLevel === '大吉' || luckLevel === '吉') return '身体状况良好,精力充沛,可进行适度运动,保持良好习惯' + if (luckLevel === '中吉' || luckLevel === '平') return '身体状况一般,注意劳逸结合,保持规律作息' + return '身体状况欠佳,注意休息,避免剧烈运动和熬夜' +} + +export function generateDailyQuote(chart: ZiweiChart, luckLevel: string): DailyQuote { + const mingPalace = findPalace(chart, PalaceType.MING) + if (mingPalace != null && mingPalace.majorStars.length > 0) { + const mainStar = mingPalace.majorStars[0].star + const starQuote = QUOTE_MAP[mainStar] + if (starQuote != null) { + return { + quote: starQuote, + source: MajorStar.name(mainStar), + } + } + } + return { + quote: QUOTE_MAP_FLAT[luckLevel] ?? '平淡是真,守正出奇', + source: '综合运势', + } +} + +export function generateSuitableAvoid(dimension: string, luckLevel: string): SuitableAvoidItem { + const suitable = SUITABLE_MAP[dimension]?.[luckLevel] ?? ['保持平常心'] + const avoid = AVOID_MAP[dimension]?.[luckLevel] ?? ['冲动行事'] + return { suitable, avoid } +} + +export function generateConflictWarning(chart: ZiweiChart): ConflictWarning { + // 检查夫妻宫、官禄宫、兄弟宫是否有煞星 + const checkPalaces: Array<{ palaceType: PalaceType; conflictType: string }> = [ + { palaceType: PalaceType.FUQI, conflictType: '感情' }, + { palaceType: PalaceType.GUANLU, conflictType: '事业' }, + { palaceType: PalaceType.XIONG, conflictType: '人际' }, + ] + + for (const { palaceType, conflictType } of checkPalaces) { + const palace = findPalace(chart, palaceType) + if (palace == null) continue + + const evilCount = countMinorStars(palace, EVIL_STARS) + countMajorStars(palace, EVIL_STARS) + if (evilCount >= 2) { + return { + hasConflict: true, + conflictType: `${conflictType}方面`, + conflictDetail: `${conflictType}宫位有${evilCount}颗煞星汇聚,易生是非争执`, + conflictAdvice: `今日${conflictType}方面需格外谨慎,谨言慎行,避免与人发生冲突`, + } + } + } + + return { + hasConflict: false, + conflictType: '', + conflictDetail: '今日无明显冲突预警', + conflictAdvice: '保持平和心态,正常社交即可', + } +} + +export function generateTimeSlotFortune(luckLevel: string): TimeSlotFortune { + return { + morning: TIME_SLOT_MORNING_MAP[luckLevel] ?? '上午状态平稳,按计划行事', + afternoon: TIME_SLOT_AFTERNOON_MAP[luckLevel] ?? '下午状态一般,注意劳逸结合', + evening: TIME_SLOT_EVENING_MAP[luckLevel] ?? '晚上适度放松,不宜熬夜', + } +} + +export function generateStarRating(scores: Record): StarRating { + const toStars = (score: number): number => { + if (score >= 90) return 5 + if (score >= 80) return 4 + if (score >= 70) return 3 + if (score >= 60) return 2 + return 1 + } + + return { + overall: toStars(scores.overall ?? 60), + love: toStars(scores.love ?? 60), + study: toStars(scores.study ?? 60), + social: toStars(scores.social ?? 60), + travel: toStars(scores.travel ?? 60), + wealth: toStars(scores.wealth ?? 60), + career: toStars(scores.career ?? 60), + health: toStars(scores.health ?? 60), + emotional: toStars(scores.emotional ?? 60), + family: toStars(scores.family ?? 60), + } +} + +export function generateComprehensiveFortune(chart: ZiweiChart, date: Date): DailyFortune { + const baseFortune = generateDailyFortune(chart, date) + + const loveFortune = analyzeLoveFortune(chart, date) + const studyFortune = analyzeStudyFortune(chart, date) + const socialFortune = analyzeSocialFortune(chart, date) + const travelFortune = analyzeTravelFortune(chart, date) + + // 新增维度(A) + const emotionalFortune = analyzeEmotionalFortune(chart, date) + const familyFortune = analyzeFamilyFortune(chart, date) + const wealthFortune = analyzeWealthFortune(chart, date) + const careerFortune = analyzeCareerFortune(chart, date) + const healthFortune = analyzeHealthFortune(chart, date) + + const luckLevel = baseFortune.overallLuck + + // 新增维度(B) + const dailyQuote = generateDailyQuote(chart, luckLevel) + const loveSuitable = generateSuitableAvoid('love', loveFortune.overallLoveLuck) + const studySuitable = generateSuitableAvoid('study', studyFortune.overallStudyLuck) + const socialSuitable = generateSuitableAvoid('social', socialFortune.overallSocialLuck) + const travelSuitable = generateSuitableAvoid('travel', travelFortune.overallTravelLuck) + const careerSuitable = generateSuitableAvoid('career', careerFortune.overallCareerLuck) + const wealthSuitable = generateSuitableAvoid('wealth', wealthFortune.overallWealthLuck) + const conflictWarning = generateConflictWarning(chart) + + // 新增维度(D) + const timeSlotFortune = generateTimeSlotFortune(luckLevel) + const starRating = generateStarRating({ + overall: baseFortune.overallScore, + love: loveFortune.loveScore, + study: studyFortune.studyScore, + social: socialFortune.socialScore, + travel: travelFortune.travelScore, + wealth: wealthFortune.wealthScore, + career: careerFortune.careerScore, + health: healthFortune.healthScore, + emotional: emotionalFortune.emotionalScore, + family: familyFortune.familyScore, + }) + + return { + ...baseFortune, + loveScore: loveFortune.loveScore, + overallLoveLuck: loveFortune.overallLoveLuck, + peachBlossomIndex: loveFortune.peachBlossomIndex, + singleAdvice: loveFortune.singleAdvice, + datingAdvice: loveFortune.datingAdvice, + marriageAdvice: loveFortune.marriageAdvice, + bestDatingTime: loveFortune.bestDatingTime, + studyScore: studyFortune.studyScore, + overallStudyLuck: studyFortune.overallStudyLuck, + focusIndex: studyFortune.focusIndex, + memoryIndex: studyFortune.memoryIndex, + studyAdvice: studyFortune.studyAdvice, + examLuck: studyFortune.examLuck, + bestStudyTime: studyFortune.bestStudyTime, + suitableSubjects: studyFortune.suitableSubjects, + studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice, + socialScore: socialFortune.socialScore, + overallSocialLuck: socialFortune.overallSocialLuck, + popularityIndex: socialFortune.popularityIndex, + communicationAbility: socialFortune.communicationAbility, + socialAdvice: socialFortune.socialAdvice, + nobleLuck: socialFortune.nobleLuck, + bestSocialTime: socialFortune.bestSocialTime, + suitableOccasion: socialFortune.suitableOccasion, + interpersonalAdvice: socialFortune.interpersonalAdvice, + travelScore: travelFortune.travelScore, + overallTravelLuck: travelFortune.overallTravelLuck, + safetyIndex: travelFortune.safetyIndex, + smoothnessIndex: travelFortune.smoothnessIndex, + travelAdvice: travelFortune.travelAdvice, + bestTravelTime: travelFortune.bestTravelTime, + suitableTransport: travelFortune.suitableTransport, + precautions: travelFortune.precautions, + nobleDirection: travelFortune.nobleDirection, + + // 新增维度(A) + emotionalScore: emotionalFortune.emotionalScore, + overallEmotionalLuck: emotionalFortune.overallEmotionalLuck, + moodIndex: emotionalFortune.moodIndex, + stressLevel: emotionalFortune.stressLevel, + emotionalAdvice: emotionalFortune.emotionalAdvice, + relaxationMethod: emotionalFortune.relaxationMethod, + bestRelaxationTime: emotionalFortune.bestRelaxationTime, + familyScore: familyFortune.familyScore, + overallFamilyLuck: familyFortune.overallFamilyLuck, + familyHarmony: familyFortune.familyHarmony, + parentRelationship: familyFortune.parentRelationship, + childRelationship: familyFortune.childRelationship, + familyAdvice: familyFortune.familyAdvice, + suitableFamilyActivity: familyFortune.suitableFamilyActivity, + wealthScore: wealthFortune.wealthScore, + overallWealthLuck: wealthFortune.overallWealthLuck, + mainWealth: wealthFortune.mainWealth, + sideWealth: wealthFortune.sideWealth, + spendingTendency: wealthFortune.spendingTendency, + investmentTiming: wealthFortune.investmentTiming, + careerScore: careerFortune.careerScore, + overallCareerLuck: careerFortune.overallCareerLuck, + colleagueRelation: careerFortune.colleagueRelation, + leadershipView: careerFortune.leadershipView, + projectDecision: careerFortune.projectDecision, + promotionLuck: careerFortune.promotionLuck, + healthScore: healthFortune.healthScore, + overallHealthLuck: healthFortune.overallHealthLuck, + dietAdvice: healthFortune.dietAdvice, + exerciseAdvice: healthFortune.exerciseAdvice, + restAdvice: healthFortune.restAdvice, + focusArea: healthFortune.focusArea, + + // 新增维度(B) + dailyQuote, + loveSuitable, + studySuitable, + socialSuitable, + travelSuitable, + careerSuitable, + wealthSuitable, + conflictWarning, + + // 新增维度(D) + timeSlotFortune, + starRating, + } +} + +export function generateComprehensiveMonthlyFortune( + chart: ZiweiChart, + year: number, + month: number, +): MonthlyFortune { + const baseFortune = generateMonthlyFortune(chart, year, month) + + const midMonthDate = new Date(year, month - 1, 15) + const loveFortune = analyzeLoveFortune(chart, midMonthDate) + const studyFortune = analyzeStudyFortune(chart, midMonthDate) + const socialFortune = analyzeSocialFortune(chart, midMonthDate) + const travelFortune = analyzeTravelFortune(chart, midMonthDate) + + // 新增维度(A) + const emotionalFortune = analyzeEmotionalFortune(chart, midMonthDate) + const familyFortune = analyzeFamilyFortune(chart, midMonthDate) + const wealthFortune = analyzeWealthFortune(chart, midMonthDate) + const careerFortune = analyzeCareerFortune(chart, midMonthDate) + const healthFortune = analyzeHealthFortune(chart, midMonthDate) + + const luckLevel = baseFortune.overallLuck + + // 新增维度(B) + const dailyQuote = generateDailyQuote(chart, luckLevel) + const loveSuitable = generateSuitableAvoid('love', loveFortune.overallLoveLuck) + const studySuitable = generateSuitableAvoid('study', studyFortune.overallStudyLuck) + const socialSuitable = generateSuitableAvoid('social', socialFortune.overallSocialLuck) + const travelSuitable = generateSuitableAvoid('travel', travelFortune.overallTravelLuck) + const careerSuitable = generateSuitableAvoid('career', careerFortune.overallCareerLuck) + const wealthSuitable = generateSuitableAvoid('wealth', wealthFortune.overallWealthLuck) + const conflictWarning = generateConflictWarning(chart) + + // 新增维度(D) + const timeSlotFortune = generateTimeSlotFortune(luckLevel) + const starRating = generateStarRating({ + overall: baseFortune.overallScore, + love: loveFortune.loveScore, + study: studyFortune.studyScore, + social: socialFortune.socialScore, + travel: travelFortune.travelScore, + wealth: wealthFortune.wealthScore, + career: careerFortune.careerScore, + health: healthFortune.healthScore, + emotional: emotionalFortune.emotionalScore, + family: familyFortune.familyScore, + }) + + return { + ...baseFortune, + loveScore: loveFortune.loveScore, + overallLoveLuck: loveFortune.overallLoveLuck, + peachBlossomIndex: loveFortune.peachBlossomIndex, + singleAdvice: loveFortune.singleAdvice, + datingAdvice: loveFortune.datingAdvice, + marriageAdvice: loveFortune.marriageAdvice, + bestDatingTime: loveFortune.bestDatingTime, + studyScore: studyFortune.studyScore, + overallStudyLuck: studyFortune.overallStudyLuck, + focusIndex: studyFortune.focusIndex, + memoryIndex: studyFortune.memoryIndex, + studyAdvice: studyFortune.studyAdvice, + examLuck: studyFortune.examLuck, + bestStudyTime: studyFortune.bestStudyTime, + suitableSubjects: studyFortune.suitableSubjects, + studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice, + socialScore: socialFortune.socialScore, + overallSocialLuck: socialFortune.overallSocialLuck, + popularityIndex: socialFortune.popularityIndex, + communicationAbility: socialFortune.communicationAbility, + socialAdvice: socialFortune.socialAdvice, + nobleLuck: socialFortune.nobleLuck, + bestSocialTime: socialFortune.bestSocialTime, + suitableOccasion: socialFortune.suitableOccasion, + interpersonalAdvice: socialFortune.interpersonalAdvice, + travelScore: travelFortune.travelScore, + overallTravelLuck: travelFortune.overallTravelLuck, + safetyIndex: travelFortune.safetyIndex, + smoothnessIndex: travelFortune.smoothnessIndex, + travelAdvice: travelFortune.travelAdvice, + bestTravelTime: travelFortune.bestTravelTime, + suitableTransport: travelFortune.suitableTransport, + precautions: travelFortune.precautions, + nobleDirection: travelFortune.nobleDirection, + + // 新增维度(A) + emotionalScore: emotionalFortune.emotionalScore, + overallEmotionalLuck: emotionalFortune.overallEmotionalLuck, + moodIndex: emotionalFortune.moodIndex, + stressLevel: emotionalFortune.stressLevel, + emotionalAdvice: emotionalFortune.emotionalAdvice, + relaxationMethod: emotionalFortune.relaxationMethod, + bestRelaxationTime: emotionalFortune.bestRelaxationTime, + familyScore: familyFortune.familyScore, + overallFamilyLuck: familyFortune.overallFamilyLuck, + familyHarmony: familyFortune.familyHarmony, + parentRelationship: familyFortune.parentRelationship, + childRelationship: familyFortune.childRelationship, + familyAdvice: familyFortune.familyAdvice, + suitableFamilyActivity: familyFortune.suitableFamilyActivity, + wealthScore: wealthFortune.wealthScore, + overallWealthLuck: wealthFortune.overallWealthLuck, + mainWealth: wealthFortune.mainWealth, + sideWealth: wealthFortune.sideWealth, + spendingTendency: wealthFortune.spendingTendency, + investmentTiming: wealthFortune.investmentTiming, + careerScore: careerFortune.careerScore, + overallCareerLuck: careerFortune.overallCareerLuck, + colleagueRelation: careerFortune.colleagueRelation, + leadershipView: careerFortune.leadershipView, + projectDecision: careerFortune.projectDecision, + promotionLuck: careerFortune.promotionLuck, + healthScore: healthFortune.healthScore, + overallHealthLuck: healthFortune.overallHealthLuck, + dietAdvice: healthFortune.dietAdvice, + exerciseAdvice: healthFortune.exerciseAdvice, + restAdvice: healthFortune.restAdvice, + focusArea: healthFortune.focusArea, + + // 新增维度(B) + dailyQuote, + loveSuitable, + studySuitable, + socialSuitable, + travelSuitable, + careerSuitable, + wealthSuitable, + conflictWarning, + + // 新增维度(D) + timeSlotFortune, + starRating, + } +} + +export function generateComprehensiveYearlyFortune( + chart: ZiweiChart, + year: number, +): YearlyFortune { + const baseFortune = generateYearlyFortune(chart, year) + + const midYearDate = new Date(year, 6, 1) + const loveFortune = analyzeLoveFortune(chart, midYearDate) + const studyFortune = analyzeStudyFortune(chart, midYearDate) + const socialFortune = analyzeSocialFortune(chart, midYearDate) + const travelFortune = analyzeTravelFortune(chart, midYearDate) + + // 新增维度(A) + const emotionalFortune = analyzeEmotionalFortune(chart, midYearDate) + const familyFortune = analyzeFamilyFortune(chart, midYearDate) + const wealthFortune = analyzeWealthFortune(chart, midYearDate) + const careerFortune = analyzeCareerFortune(chart, midYearDate) + const healthFortune = analyzeHealthFortune(chart, midYearDate) + + const luckLevel = baseFortune.overallLuck + + // 新增维度(B) + const dailyQuote = generateDailyQuote(chart, luckLevel) + const loveSuitable = generateSuitableAvoid('love', loveFortune.overallLoveLuck) + const studySuitable = generateSuitableAvoid('study', studyFortune.overallStudyLuck) + const socialSuitable = generateSuitableAvoid('social', socialFortune.overallSocialLuck) + const travelSuitable = generateSuitableAvoid('travel', travelFortune.overallTravelLuck) + const careerSuitable = generateSuitableAvoid('career', careerFortune.overallCareerLuck) + const wealthSuitable = generateSuitableAvoid('wealth', wealthFortune.overallWealthLuck) + const conflictWarning = generateConflictWarning(chart) + + // 新增维度(D) + const timeSlotFortune = generateTimeSlotFortune(luckLevel) + const starRating = generateStarRating({ + overall: baseFortune.overallScore, + love: loveFortune.loveScore, + study: studyFortune.studyScore, + social: socialFortune.socialScore, + travel: travelFortune.travelScore, + wealth: wealthFortune.wealthScore, + career: careerFortune.careerScore, + health: healthFortune.healthScore, + emotional: emotionalFortune.emotionalScore, + family: familyFortune.familyScore, + }) + + return { + ...baseFortune, + loveScore: loveFortune.loveScore, + overallLoveLuck: loveFortune.overallLoveLuck, + peachBlossomIndex: loveFortune.peachBlossomIndex, + singleAdvice: loveFortune.singleAdvice, + datingAdvice: loveFortune.datingAdvice, + marriageAdvice: loveFortune.marriageAdvice, + bestDatingTime: loveFortune.bestDatingTime, + studyScore: studyFortune.studyScore, + overallStudyLuck: studyFortune.overallStudyLuck, + focusIndex: studyFortune.focusIndex, + memoryIndex: studyFortune.memoryIndex, + studyAdvice: studyFortune.studyAdvice, + examLuck: studyFortune.examLuck, + bestStudyTime: studyFortune.bestStudyTime, + suitableSubjects: studyFortune.suitableSubjects, + studyEnvironmentAdvice: studyFortune.studyEnvironmentAdvice, + socialScore: socialFortune.socialScore, + overallSocialLuck: socialFortune.overallSocialLuck, + popularityIndex: socialFortune.popularityIndex, + communicationAbility: socialFortune.communicationAbility, + socialAdvice: socialFortune.socialAdvice, + nobleLuck: socialFortune.nobleLuck, + bestSocialTime: socialFortune.bestSocialTime, + suitableOccasion: socialFortune.suitableOccasion, + interpersonalAdvice: socialFortune.interpersonalAdvice, + travelScore: travelFortune.travelScore, + overallTravelLuck: travelFortune.overallTravelLuck, + safetyIndex: travelFortune.safetyIndex, + smoothnessIndex: travelFortune.smoothnessIndex, + travelAdvice: travelFortune.travelAdvice, + bestTravelTime: travelFortune.bestTravelTime, + suitableTransport: travelFortune.suitableTransport, + precautions: travelFortune.precautions, + nobleDirection: travelFortune.nobleDirection, + + // 新增维度(A) + emotionalScore: emotionalFortune.emotionalScore, + overallEmotionalLuck: emotionalFortune.overallEmotionalLuck, + moodIndex: emotionalFortune.moodIndex, + stressLevel: emotionalFortune.stressLevel, + emotionalAdvice: emotionalFortune.emotionalAdvice, + relaxationMethod: emotionalFortune.relaxationMethod, + bestRelaxationTime: emotionalFortune.bestRelaxationTime, + familyScore: familyFortune.familyScore, + overallFamilyLuck: familyFortune.overallFamilyLuck, + familyHarmony: familyFortune.familyHarmony, + parentRelationship: familyFortune.parentRelationship, + childRelationship: familyFortune.childRelationship, + familyAdvice: familyFortune.familyAdvice, + suitableFamilyActivity: familyFortune.suitableFamilyActivity, + wealthScore: wealthFortune.wealthScore, + overallWealthLuck: wealthFortune.overallWealthLuck, + mainWealth: wealthFortune.mainWealth, + sideWealth: wealthFortune.sideWealth, + spendingTendency: wealthFortune.spendingTendency, + investmentTiming: wealthFortune.investmentTiming, + careerScore: careerFortune.careerScore, + overallCareerLuck: careerFortune.overallCareerLuck, + colleagueRelation: careerFortune.colleagueRelation, + leadershipView: careerFortune.leadershipView, + projectDecision: careerFortune.projectDecision, + promotionLuck: careerFortune.promotionLuck, + healthScore: healthFortune.healthScore, + overallHealthLuck: healthFortune.overallHealthLuck, + dietAdvice: healthFortune.dietAdvice, + exerciseAdvice: healthFortune.exerciseAdvice, + restAdvice: healthFortune.restAdvice, + focusArea: healthFortune.focusArea, + + // 新增维度(B) + dailyQuote, + loveSuitable, + studySuitable, + socialSuitable, + travelSuitable, + careerSuitable, + wealthSuitable, + conflictWarning, + + // 新增维度(D) + timeSlotFortune, + starRating, + } +} \ No newline at end of file diff --git a/packages/algorithm/src/fortuneStrategy.ts b/packages/algorithm/src/fortuneStrategy.ts new file mode 100644 index 0000000..9b7c352 --- /dev/null +++ b/packages/algorithm/src/fortuneStrategy.ts @@ -0,0 +1,347 @@ +import { PalaceType, StarNature, TransformationType } from './enums' +import { calculatePalaceScore, determineLuckLevel } from './ziweiAlgorithm' +import type { ZiweiChart, Palace, PalaceFortune, DailyFortune, MonthlyFortune, YearlyFortune } from './types' + +export function analyzePalaceFortune(palace: Palace, fortuneDate: Date): PalaceFortune { + const baseScore = palace.score + const adjustment = calculateDailyAdjustment(palace, fortuneDate) + const finalScore = Math.max(0, Math.min(100, baseScore + adjustment)) + + return { + palaceType: palace.palaceType, + score: finalScore, + luckLevel: determineLuckLevel(finalScore), + analysis: generatePalaceAnalysis(palace, finalScore), + advice: generatePalaceAdvice(palace.palaceType, finalScore), + } +} + +export function calculateDailyAdjustment(palace: Palace, fortuneDate: Date): number { + let adjustment = 0 + const dayOfMonth = fortuneDate.getDate() + const month = fortuneDate.getMonth() + 1 + + for (const starInfo of palace.majorStars) { + if (starInfo.transformation != null) { + adjustment += calculateTransformationAdjustment(starInfo.transformation, dayOfMonth) + } + + if (starInfo.nature === StarNature.JI) { + adjustment += (month === 1 || month === 4 || month === 7 || month === 10) ? 3 : 1 + } else if (starInfo.nature === StarNature.XIONG) { + adjustment -= (month === 2 || month === 5 || month === 8 || month === 11) ? 3 : 1 + } + } + + return adjustment +} + +export function calculateMonthlyAdjustment(palace: Palace, month: number): number { + let adjustment = 0 + + for (const starInfo of palace.majorStars) { + if (starInfo.transformation != null) { + adjustment += calculateMonthlyTransformationAdjustment(starInfo.transformation, month) + } + + if (starInfo.nature === StarNature.JI) { + adjustment += (month % 3 === 1) ? 4 : 2 + } else if (starInfo.nature === StarNature.XIONG) { + adjustment -= (month % 3 === 2) ? 4 : 2 + } + } + + return adjustment +} + +export function calculateYearlyAdjustment(palace: Palace, year: number): number { + let adjustment = 0 + + for (const starInfo of palace.majorStars) { + if (starInfo.transformation != null) { + adjustment += calculateYearlyTransformationAdjustment(starInfo.transformation, year) + } + + if (starInfo.nature === StarNature.JI) { + adjustment += 3 + } else if (starInfo.nature === StarNature.XIONG) { + adjustment -= 3 + } + } + + return adjustment +} + +export function calculateTransformationAdjustment(transformation: TransformationType, dayOfMonth: number): number { + switch (transformation) { + case TransformationType.LU: + return (dayOfMonth % 3 === 0) ? 5 : 2 + case TransformationType.QUAN: + return (dayOfMonth % 7 === 0) ? 5 : 2 + case TransformationType.KE: + return (dayOfMonth % 5 === 0) ? 3 : 1 + case TransformationType.JI: + return (dayOfMonth % 4 === 0) ? -8 : -3 + default: + return 0 + } +} + +export function calculateMonthlyTransformationAdjustment(transformation: TransformationType, month: number): number { + switch (transformation) { + case TransformationType.LU: + return (month % 3 === 1) ? 6 : 3 + case TransformationType.QUAN: + return (month % 3 === 2) ? 6 : 3 + case TransformationType.KE: + return (month % 2 === 0) ? 4 : 2 + case TransformationType.JI: + return (month % 4 === 0) ? -10 : -5 + default: + return 0 + } +} + +export function calculateYearlyTransformationAdjustment(transformation: TransformationType, year: number): number { + switch (transformation) { + case TransformationType.LU: + return (year % 3 === 0) ? 8 : 4 + case TransformationType.QUAN: + return (year % 3 === 1) ? 8 : 4 + case TransformationType.KE: + return (year % 2 === 0) ? 5 : 3 + case TransformationType.JI: + return (year % 4 === 0) ? -12 : -6 + default: + return 0 + } +} + +export function generatePalaceAnalysis(palace: Palace, score: number): string { + const parts: string[] = [] + parts.push(`${PalaceType.name(palace.palaceType)}位于${palace.earthlyBranch}宫`) + + if (palace.majorStars.length > 0) { + parts.push(',主星配置:') + for (const starInfo of palace.majorStars) { + parts.push(starInfo.star) + if (starInfo.transformation != null) { + parts.push(`(${TransformationType.name(starInfo.transformation)})`) + } + parts.push(' ') + } + } + + parts.push(`。运势评分:${score}分`) + return parts.join('') +} + +export function generatePalaceAdvice(palaceType: PalaceType, score: number): string { + if (score >= 70) { + return getPositiveAdvice(palaceType) + } else if (score >= 50) { + return getNeutralAdvice(palaceType) + } else { + return getNegativeAdvice(palaceType) + } +} + +function getPositiveAdvice(palaceType: PalaceType): string { + const adviceMap: Record = { + [PalaceType.MING]: '今日状态极佳,适合开展新计划', + [PalaceType.XIONG]: '与朋友相处融洽,可拓展人脉', + [PalaceType.FUQI]: '感情运势良好,适合表达爱意', + [PalaceType.ZINV]: '与子女关系和谐,可增进感情', + [PalaceType.CAI]: '财运亨通,可进行投资理财', + [PalaceType.JILU]: '身体健康,精力充沛', + [PalaceType.QIAN]: '出行顺利,可安排外出计划', + [PalaceType.GUANLU]: '工作顺利,可争取更多机会', + [PalaceType.TUDI]: '事业运佳,适合拓展业务', + [PalaceType.FUBEN]: '贵人运旺,可获得他人帮助', + [PalaceType.SHEN]: '心情愉悦,适合社交活动', + [PalaceType.FU]: '与父母关系和谐,可多沟通', + } + return adviceMap[palaceType] ?? '运势良好,保持积极心态' +} + +function getNeutralAdvice(palaceType: PalaceType): string { + const adviceMap: Record = { + [PalaceType.MING]: '状态平稳,按计划行事即可', + [PalaceType.XIONG]: '与朋友关系一般,保持正常交往', + [PalaceType.FUQI]: '感情平稳,多关心对方', + [PalaceType.ZINV]: '与子女关系正常,保持沟通', + [PalaceType.CAI]: '财运平稳,谨慎理财', + [PalaceType.JILU]: '身体状况一般,注意休息', + [PalaceType.QIAN]: '出行运势一般,谨慎安排', + [PalaceType.GUANLU]: '工作平稳,做好本职工作', + [PalaceType.TUDI]: '事业运一般,稳扎稳打', + [PalaceType.FUBEN]: '贵人运一般,依靠自身努力', + [PalaceType.SHEN]: '心情平稳,保持平常心', + [PalaceType.FU]: '与父母关系正常,保持联系', + } + return adviceMap[palaceType] ?? '运势平稳,保持现状' +} + +function getNegativeAdvice(palaceType: PalaceType): string { + const adviceMap: Record = { + [PalaceType.MING]: '状态欠佳,避免重要决策', + [PalaceType.XIONG]: '与朋友关系紧张,避免冲突', + [PalaceType.FUQI]: '感情运势欠佳,多包容理解', + [PalaceType.ZINV]: '与子女关系紧张,耐心沟通', + [PalaceType.CAI]: '财运不佳,避免大额投资', + [PalaceType.JILU]: '身体欠佳,注意保养', + [PalaceType.QIAN]: '出行不利,减少外出', + [PalaceType.GUANLU]: '工作压力大,调整心态', + [PalaceType.TUDI]: '事业运不佳,谨慎行事', + [PalaceType.FUBEN]: '贵人运弱,依靠自己', + [PalaceType.SHEN]: '心情不佳,调节情绪', + [PalaceType.FU]: '与父母关系紧张,多理解包容', + } + return adviceMap[palaceType] ?? '运势欠佳,谨慎行事' +} + +export function calculateOverallScore(palaceFortunes: Map | Record): number { + const values = palaceFortunes instanceof Map + ? [...palaceFortunes.values()] + : Object.values(palaceFortunes) + + if (values.length === 0) return 60 + + const totalScore = values.reduce((sum, pf) => sum + pf.score, 0) + return Math.floor(totalScore / values.length) +} + +export function generateDailyFortune(chart: ZiweiChart, date: Date): DailyFortune { + const palaceFortunes: Record = {} + + for (const palace of chart.palaces) { + palaceFortunes[palace.palaceType] = analyzePalaceFortune(palace, date) + } + + const overallScore = calculateOverallScore(palaceFortunes) + + return { + fortuneDate: date.toISOString().slice(0, 10), + palaceFortunes, + overallScore, + overallLuck: determineLuckLevel(overallScore), + careerAdvice: generateDailyCareerAdvice(chart, date), + wealthAdvice: generateDailyWealthAdvice(chart, date), + relationshipAdvice: generateDailyRelationshipAdvice(chart, date), + healthAdvice: generateDailyHealthAdvice(chart, date), + luckyColor: '', + luckyNumber: '', + luckyDirection: '', + } +} + +function generateDailyCareerAdvice(chart: ZiweiChart, date: Date): string { + const careerPalace = chart.palaces.find(p => p.palaceType === PalaceType.GUANLU) + if (careerPalace == null) return '事业运势不明,谨慎行事' + if (careerPalace.score >= 70) { + return '今日事业运势吉显,适合处理重要工作事务,可主动争取机会,宜把握良机,必有收获' + } else if (careerPalace.score >= 60) { + return '今日事业运势平吉,按计划完成工作即可,避免冒险决策,宜稳中求进,循序渐进' + } else { + return '今日事业运势欠佳,建议处理常规工作,避免重要决策和冲突,宜趋吉避凶,待时而动' + } +} + +function generateDailyWealthAdvice(chart: ZiweiChart, date: Date): string { + const wealthPalace = chart.palaces.find(p => p.palaceType === PalaceType.CAI) + if (wealthPalace == null) return '财运不明,谨慎理财' + if (wealthPalace.score >= 70) { + return '今日财运吉显,适合进行理财规划,可考虑小额投资,宜把握良机,必有收获' + } else if (wealthPalace.score >= 60) { + return '今日财运平吉,建议保持理性消费,谨慎理财,宜稳中求进,循序渐进' + } else { + return '今日财运欠佳,建议避免大额支出和投资,保守理财,宜趋吉避凶,待时而动' + } +} + +function generateDailyRelationshipAdvice(chart: ZiweiChart, date: Date): string { + const relationshipPalace = chart.palaces.find(p => p.palaceType === PalaceType.FUQI) + if (relationshipPalace == null) return '感情运势不明,保持平常心' + if (relationshipPalace.score >= 70) { + return '今日感情运势吉显,适合表达爱意,增进感情交流,宜把握良机,必有收获' + } else if (relationshipPalace.score >= 60) { + return '今日感情运势平吉,保持正常沟通,多关心对方,宜稳中求进,循序渐进' + } else { + return '今日感情运势欠佳,避免争吵,多包容理解对方,宜趋吉避凶,待时而动' + } +} + +function generateDailyHealthAdvice(chart: ZiweiChart, date: Date): string { + const healthPalace = chart.palaces.find(p => p.palaceType === PalaceType.JILU) + if (healthPalace == null) return '健康运势不明,注意休息' + if (healthPalace.score >= 70) { + return '今日身体状况吉显,精力充沛,可进行适度运动,宜保持良好习惯,适度锻炼' + } else if (healthPalace.score >= 60) { + return '今日身体状况平吉,注意劳逸结合,保持规律作息,宜稳中求进,循序渐进' + } else { + return '今日身体状况欠佳,注意休息,避免剧烈运动和熬夜,宜修身养性,待时而动' + } +} + +export function generateMonthlyFortune(chart: ZiweiChart, year: number, month: number): MonthlyFortune { + const palaceFortunes: Record = {} + + for (const palace of chart.palaces) { + const baseScore = palace.score + const adjustment = calculateMonthlyAdjustment(palace, month) + const finalScore = Math.max(0, Math.min(100, baseScore + adjustment)) + + palaceFortunes[palace.palaceType] = { + palaceType: palace.palaceType, + score: finalScore, + luckLevel: determineLuckLevel(finalScore), + analysis: generatePalaceAnalysis(palace, finalScore), + advice: generatePalaceAdvice(palace.palaceType, finalScore), + } + } + + const overallScore = calculateOverallScore(palaceFortunes) + + return { + year, + month, + palaceFortunes, + overallScore, + overallLuck: determineLuckLevel(overallScore), + careerAdvice: '', + wealthAdvice: '', + relationshipAdvice: '', + healthAdvice: '', + } +} + +export function generateYearlyFortune(chart: ZiweiChart, year: number): YearlyFortune { + const palaceFortunes: Record = {} + + for (const palace of chart.palaces) { + const baseScore = palace.score + const adjustment = calculateYearlyAdjustment(palace, year) + const finalScore = Math.max(0, Math.min(100, baseScore + adjustment)) + + palaceFortunes[palace.palaceType] = { + palaceType: palace.palaceType, + score: finalScore, + luckLevel: determineLuckLevel(finalScore), + analysis: generatePalaceAnalysis(palace, finalScore), + advice: generatePalaceAdvice(palace.palaceType, finalScore), + } + } + + const overallScore = calculateOverallScore(palaceFortunes) + + return { + year, + palaceFortunes, + overallScore, + overallLuck: determineLuckLevel(overallScore), + careerAdvice: '', + wealthAdvice: '', + relationshipAdvice: '', + healthAdvice: '', + } +} \ No newline at end of file diff --git a/packages/algorithm/src/index.ts b/packages/algorithm/src/index.ts new file mode 100644 index 0000000..3600cb1 --- /dev/null +++ b/packages/algorithm/src/index.ts @@ -0,0 +1,88 @@ +// @everything-suitable/algorithm 入口 +// 导出所有纯函数算法,供小程序和云函数共用 + +// 类型导出 +export type { + ZiweiChart, + Palace, + StarInfo, + PalaceFortune, + DailyFortune, + MonthlyFortune, + YearlyFortune, + LoveFortuneDetail, + StudyFortuneDetail, + SocialFortuneDetail, + TravelFortuneDetail, + EmotionalFortuneDetail, + FamilyFortuneDetail, + WealthFortuneDetail, + CareerFortuneDetail, + HealthFortuneDetail, + SuitableAvoidItem, + DailyQuote, + ConflictWarning, + TimeSlotFortune, + StarRating, + BirthInfoInput, + BirthInfo, + SanFangSiZheng, + Pattern, +} from './types' + +// 枚举导出 +export { + HeavenlyStem, + EarthlyBranch, + StarNature, + TransformationType, + MajorStar, + PalaceType, + MinorStar, + MinorStarType, + FortuneType, +} from './enums' + +// 紫微算法基础 +export { + calculatePalaceScore, + calculateLuckyColor, + calculateLuckyNumber, + calculateLuckyDirection, + determineLuckLevel, + calculateStarScore, + getStarNature, +} from './ziweiAlgorithm' + +// 运势策略 +export { + generateDailyFortune, + generateMonthlyFortune, + generateYearlyFortune, + analyzePalaceFortune, + calculateDailyAdjustment, + calculateMonthlyAdjustment, + calculateYearlyAdjustment, + calculateOverallScore, +} from './fortuneStrategy' + +// 综合运势 +export { + generateComprehensiveFortune, + generateComprehensiveMonthlyFortune, + generateComprehensiveYearlyFortune, + analyzeLoveFortune, + analyzeStudyFortune, + analyzeSocialFortune, + analyzeTravelFortune, + analyzeEmotionalFortune, + analyzeFamilyFortune, + analyzeWealthFortune, + analyzeCareerFortune, + analyzeHealthFortune, + generateDailyQuote, + generateSuitableAvoid, + generateConflictWarning, + generateTimeSlotFortune, + generateStarRating, +} from './fortune' \ No newline at end of file diff --git a/packages/algorithm/src/types.ts b/packages/algorithm/src/types.ts new file mode 100644 index 0000000..f9312cb --- /dev/null +++ b/packages/algorithm/src/types.ts @@ -0,0 +1,530 @@ +import type { + HeavenlyStem, + EarthlyBranch, + MajorStar, + StarNature, + TransformationType, + PalaceType, +} from './enums' + +export interface BirthInfoInput { + birthDate: string + birthHour: number + birthMinute: number + birthPlace: string + longitude: number + latitude: number + gender: 'MALE' | 'FEMALE' +} + +export interface LunarDate { + lunarYear: string + lunarMonth: string + lunarDay: string + isLeapMonth: boolean + zodiac: string + solarTerm: string + weekday: string +} + +export interface BirthInfo { + birthTime: string + yearStem: HeavenlyStem + monthStem: HeavenlyStem + dayStem: HeavenlyStem + hourStem: HeavenlyStem + yearBranch: EarthlyBranch + monthBranch: EarthlyBranch + dayBranch: EarthlyBranch + hourBranch: EarthlyBranch + gender: 'MALE' | 'FEMALE' + timezone: string + longitude?: number + latitude?: number + birthPlace?: string + trueSolarTime?: string + lunarDate?: LunarDate +} + +export interface StarInfo { + star: MajorStar + nature: StarNature + transformation: TransformationType | null + brightness: number + description: string +} + +export interface Palace { + palaceType: PalaceType + earthlyBranch: EarthlyBranch + majorStars: StarInfo[] + minorStars: string[] + analysis: string + score: number +} + +export interface SanFangSiZheng { + mingGong: Palace + caiBoGong: Palace + guanLuGong: Palace + qianYiGong: Palace + sanFangScore: number + siZhengScore: number +} + +export interface Pattern { + name: string + description: string + type: string +} + +export interface ZiweiChart { + birthInfo: BirthInfo + palaces: Palace[] + yearStem: HeavenlyStem + mingGongBranch: EarthlyBranch + shenGongBranch: EarthlyBranch + summary: string + overallLuck: string + sanFangSiZheng?: SanFangSiZheng + patterns?: Pattern[] +} + +export interface PalaceFortune { + palaceType: PalaceType + score: number + luckLevel: string + analysis: string + advice: string +} + +export interface LoveFortuneDetail { + loveScore: number + overallLoveLuck: string + peachBlossomIndex: string + singleAdvice: string + datingAdvice: string + marriageAdvice: string + bestDatingTime: string +} + +export interface StudyFortuneDetail { + studyScore: number + overallStudyLuck: string + focusIndex: string + memoryIndex: string + studyAdvice: string + examLuck: string + bestStudyTime: string + suitableSubjects: string + studyEnvironmentAdvice: string +} + +export interface SocialFortuneDetail { + socialScore: number + overallSocialLuck: string + popularityIndex: string + communicationAbility: string + socialAdvice: string + nobleLuck: string + bestSocialTime: string + suitableOccasion: string + interpersonalAdvice: string +} + +export interface TravelFortuneDetail { + travelScore: number + overallTravelLuck: string + safetyIndex: string + smoothnessIndex: string + travelAdvice: string + bestTravelTime: string + suitableTransport: string + precautions: string + nobleDirection: string +} + +// === 新增维度类型 === + +export interface EmotionalFortuneDetail { + emotionalScore: number + overallEmotionalLuck: string + moodIndex: string + stressLevel: string + emotionalAdvice: string + relaxationMethod: string + bestRelaxationTime: string +} + +export interface FamilyFortuneDetail { + familyScore: number + overallFamilyLuck: string + familyHarmony: string + parentRelationship: string + childRelationship: string + familyAdvice: string + suitableFamilyActivity: string +} + +export interface WealthFortuneDetail { + wealthScore: number + overallWealthLuck: string + mainWealth: string + sideWealth: string + spendingTendency: string + investmentTiming: string + wealthAdvice: string +} + +export interface CareerFortuneDetail { + careerScore: number + overallCareerLuck: string + colleagueRelation: string + leadershipView: string + projectDecision: string + promotionLuck: string + careerAdvice: string +} + +export interface HealthFortuneDetail { + healthScore: number + overallHealthLuck: string + dietAdvice: string + exerciseAdvice: string + restAdvice: string + focusArea: string + healthAdvice: string +} + +export interface SuitableAvoidItem { + suitable: string[] + avoid: string[] +} + +export interface DailyQuote { + quote: string + source: string +} + +export interface ConflictWarning { + hasConflict: boolean + conflictType: string + conflictDetail: string + conflictAdvice: string +} + +export interface TimeSlotFortune { + morning: string + afternoon: string + evening: string +} + +export interface StarRating { + overall: number + love: number + study: number + social: number + travel: number + wealth: number + career: number + health: number + emotional: number + family: number +} + +export interface DailyFortune { + fortuneDate: string + overallLuck: string + overallScore: number + palaceFortunes: Record + careerAdvice: string + wealthAdvice: string + relationshipAdvice: string + healthAdvice: string + luckyColor: string + luckyNumber: string + luckyDirection: string + loveScore?: number + overallLoveLuck?: string + peachBlossomIndex?: string + singleAdvice?: string + datingAdvice?: string + marriageAdvice?: string + bestDatingTime?: string + studyScore?: number + overallStudyLuck?: string + focusIndex?: string + memoryIndex?: string + studyAdvice?: string + examLuck?: string + bestStudyTime?: string + suitableSubjects?: string + studyEnvironmentAdvice?: string + socialScore?: number + overallSocialLuck?: string + popularityIndex?: string + communicationAbility?: string + socialAdvice?: string + nobleLuck?: string + bestSocialTime?: string + suitableOccasion?: string + interpersonalAdvice?: string + travelScore?: number + overallTravelLuck?: string + safetyIndex?: string + smoothnessIndex?: string + travelAdvice?: string + bestTravelTime?: string + suitableTransport?: string + precautions?: string + nobleDirection?: string + + // 新增维度(A) + emotionalScore?: number + overallEmotionalLuck?: string + moodIndex?: string + stressLevel?: string + emotionalAdvice?: string + relaxationMethod?: string + bestRelaxationTime?: string + familyScore?: number + overallFamilyLuck?: string + familyHarmony?: string + parentRelationship?: string + childRelationship?: string + familyAdvice?: string + suitableFamilyActivity?: string + wealthScore?: number + overallWealthLuck?: string + mainWealth?: string + sideWealth?: string + spendingTendency?: string + investmentTiming?: string + careerScore?: number + overallCareerLuck?: string + colleagueRelation?: string + leadershipView?: string + projectDecision?: string + promotionLuck?: string + healthScore?: number + overallHealthLuck?: string + dietAdvice?: string + exerciseAdvice?: string + restAdvice?: string + focusArea?: string + + // 新增维度(B) + dailyQuote?: DailyQuote + loveSuitable?: SuitableAvoidItem + studySuitable?: SuitableAvoidItem + socialSuitable?: SuitableAvoidItem + travelSuitable?: SuitableAvoidItem + careerSuitable?: SuitableAvoidItem + wealthSuitable?: SuitableAvoidItem + conflictWarning?: ConflictWarning + + // 新增维度(D) + timeSlotFortune?: TimeSlotFortune + starRating?: StarRating +} + +export interface MonthlyFortune { + fortuneMonth: string + overallLuck: string + overallScore: number + palaceFortunes: Record + dailyFortunes: DailyFortune[] + careerAdvice: string + wealthAdvice: string + relationshipAdvice: string + healthAdvice: string + luckyColor: string + luckyNumber: string + luckyDirection: string + keyFocus: string + cautionAdvice: string + loveScore?: number + overallLoveLuck?: string + peachBlossomIndex?: string + singleAdvice?: string + datingAdvice?: string + marriageAdvice?: string + bestDatingTime?: string + studyScore?: number + overallStudyLuck?: string + focusIndex?: string + memoryIndex?: string + studyAdvice?: string + examLuck?: string + bestStudyTime?: string + suitableSubjects?: string + studyEnvironmentAdvice?: string + socialScore?: number + overallSocialLuck?: string + popularityIndex?: string + communicationAbility?: string + socialAdvice?: string + nobleLuck?: string + bestSocialTime?: string + suitableOccasion?: string + interpersonalAdvice?: string + travelScore?: number + overallTravelLuck?: string + safetyIndex?: string + smoothnessIndex?: string + travelAdvice?: string + bestTravelTime?: string + suitableTransport?: string + precautions?: string + nobleDirection?: string + + // 新增维度(A) + emotionalScore?: number + overallEmotionalLuck?: string + moodIndex?: string + stressLevel?: string + emotionalAdvice?: string + relaxationMethod?: string + bestRelaxationTime?: string + familyScore?: number + overallFamilyLuck?: string + familyHarmony?: string + parentRelationship?: string + childRelationship?: string + familyAdvice?: string + suitableFamilyActivity?: string + wealthScore?: number + overallWealthLuck?: string + mainWealth?: string + sideWealth?: string + spendingTendency?: string + investmentTiming?: string + careerScore?: number + overallCareerLuck?: string + colleagueRelation?: string + leadershipView?: string + projectDecision?: string + promotionLuck?: string + healthScore?: number + overallHealthLuck?: string + dietAdvice?: string + exerciseAdvice?: string + restAdvice?: string + focusArea?: string + + // 新增维度(B) + dailyQuote?: DailyQuote + loveSuitable?: SuitableAvoidItem + studySuitable?: SuitableAvoidItem + socialSuitable?: SuitableAvoidItem + travelSuitable?: SuitableAvoidItem + careerSuitable?: SuitableAvoidItem + wealthSuitable?: SuitableAvoidItem + conflictWarning?: ConflictWarning + + // 新增维度(D) + timeSlotFortune?: TimeSlotFortune + starRating?: StarRating +} + +export interface YearlyFortune { + fortuneYear: number + overallLuck: string + overallScore: number + palaceFortunes: Record + monthlyFortunes: MonthlyFortune[] + careerAdvice: string + wealthAdvice: string + relationshipAdvice: string + healthAdvice: string + luckyColor: string + luckyNumber: string + luckyDirection: string + keyFocus: string + cautionAdvice: string + yearlyTheme: string + majorOpportunity: string + majorChallenge: string + loveScore?: number + overallLoveLuck?: string + peachBlossomIndex?: string + singleAdvice?: string + datingAdvice?: string + marriageAdvice?: string + bestDatingTime?: string + studyScore?: number + overallStudyLuck?: string + focusIndex?: string + memoryIndex?: string + studyAdvice?: string + examLuck?: string + bestStudyTime?: string + suitableSubjects?: string + studyEnvironmentAdvice?: string + socialScore?: number + overallSocialLuck?: string + popularityIndex?: string + communicationAbility?: string + socialAdvice?: string + nobleLuck?: string + bestSocialTime?: string + suitableOccasion?: string + interpersonalAdvice?: string + travelScore?: number + overallTravelLuck?: string + safetyIndex?: string + smoothnessIndex?: string + travelAdvice?: string + bestTravelTime?: string + suitableTransport?: string + precautions?: string + nobleDirection?: string + + // 新增维度(A) + emotionalScore?: number + overallEmotionalLuck?: string + moodIndex?: string + stressLevel?: string + emotionalAdvice?: string + relaxationMethod?: string + bestRelaxationTime?: string + familyScore?: number + overallFamilyLuck?: string + familyHarmony?: string + parentRelationship?: string + childRelationship?: string + familyAdvice?: string + suitableFamilyActivity?: string + wealthScore?: number + overallWealthLuck?: string + mainWealth?: string + sideWealth?: string + spendingTendency?: string + investmentTiming?: string + careerScore?: number + overallCareerLuck?: string + colleagueRelation?: string + leadershipView?: string + projectDecision?: string + promotionLuck?: string + healthScore?: number + overallHealthLuck?: string + dietAdvice?: string + exerciseAdvice?: string + restAdvice?: string + focusArea?: string + + // 新增维度(B) + dailyQuote?: DailyQuote + loveSuitable?: SuitableAvoidItem + studySuitable?: SuitableAvoidItem + socialSuitable?: SuitableAvoidItem + travelSuitable?: SuitableAvoidItem + careerSuitable?: SuitableAvoidItem + wealthSuitable?: SuitableAvoidItem + conflictWarning?: ConflictWarning + + // 新增维度(D) + timeSlotFortune?: TimeSlotFortune + starRating?: StarRating +} \ No newline at end of file diff --git a/packages/algorithm/src/ziweiAlgorithm.ts b/packages/algorithm/src/ziweiAlgorithm.ts new file mode 100644 index 0000000..45e4b77 --- /dev/null +++ b/packages/algorithm/src/ziweiAlgorithm.ts @@ -0,0 +1,274 @@ +import { MajorStar, StarNature, TransformationType, EarthlyBranch, PalaceType } from './enums' +import type { StarInfo, Palace, ZiweiChart } from './types' + +const STAR_NATURE_MAP = new Map([ + [MajorStar.ZIWEI, StarNature.JI], + [MajorStar.TIANJI, StarNature.JI], + [MajorStar.TAIYANG, StarNature.JI], + [MajorStar.WUQU, StarNature.XIONG], + [MajorStar.TIANTONG, StarNature.JI], + [MajorStar.LIANCHEN, StarNature.XIONG], + [MajorStar.TIANFU, StarNature.JI], + [MajorStar.TAIYIN, StarNature.ZHONGHE], + [MajorStar.TANLANG, StarNature.XIONG], + [MajorStar.JUMEN, StarNature.XIONG], + [MajorStar.TIANXIANG, StarNature.JI], + [MajorStar.TIANLIANG, StarNature.ZHONGHE], + [MajorStar.QISHA, StarNature.XIONG], + [MajorStar.POJUN, StarNature.XIONG], +]) + +const STAR_BASE_SCORE_MAP = new Map([ + [MajorStar.ZIWEI, 85], + [MajorStar.TIANJI, 75], + [MajorStar.TAIYANG, 80], + [MajorStar.WUQU, 75], + [MajorStar.TIANTONG, 70], + [MajorStar.LIANCHEN, 55], + [MajorStar.TIANFU, 85], + [MajorStar.TAIYIN, 75], + [MajorStar.TANLANG, 60], + [MajorStar.JUMEN, 55], + [MajorStar.TIANXIANG, 75], + [MajorStar.TIANLIANG, 75], + [MajorStar.QISHA, 60], + [MajorStar.POJUN, 55], +]) + +const TRANSFORMATION_SCORE_MAP = new Map([ + [TransformationType.LU, 15], + [TransformationType.QUAN, 10], + [TransformationType.KE, 8], + [TransformationType.JI, -15], +]) + +const BRANCH_COLOR_MAP = new Map([ + [EarthlyBranch.ZI, '黑色'], + [EarthlyBranch.CHOU, '黄色'], + [EarthlyBranch.YIN, '绿色'], + [EarthlyBranch.MAO, '绿色'], + [EarthlyBranch.CHEN, '黄色'], + [EarthlyBranch.SI, '红色'], + [EarthlyBranch.WU, '红色'], + [EarthlyBranch.WEI, '黄色'], + [EarthlyBranch.SHEN, '白色'], + [EarthlyBranch.YOU, '白色'], + [EarthlyBranch.XU, '黄色'], + [EarthlyBranch.HAI, '黑色'], +]) + +const BRANCH_DIRECTION_MAP = new Map([ + [EarthlyBranch.ZI, '正北'], + [EarthlyBranch.CHOU, '东北偏北'], + [EarthlyBranch.YIN, '东北偏东'], + [EarthlyBranch.MAO, '正东'], + [EarthlyBranch.CHEN, '东南偏东'], + [EarthlyBranch.SI, '东南偏南'], + [EarthlyBranch.WU, '正南'], + [EarthlyBranch.WEI, '西南偏南'], + [EarthlyBranch.SHEN, '西南偏西'], + [EarthlyBranch.YOU, '正西'], + [EarthlyBranch.XU, '西北偏西'], + [EarthlyBranch.HAI, '西北偏北'], +]) + +const LUCKY_COLORS = ['红色', '黄色', '蓝色', '绿色', '紫色', '金色', '白色', '黑色'] +const LUCKY_DIRECTIONS = ['东方', '南方', '西方', '北方', '东南', '西南', '东北', '西北'] + +export function getStarNature(star: MajorStar): StarNature { + return STAR_NATURE_MAP.get(star) ?? StarNature.ZHONGHE +} + +export function calculateStarScore(starInfo: StarInfo | null): number { + if (starInfo == null || starInfo.star == null) { + return 50 + } + + const star = starInfo.star + let baseScore = STAR_BASE_SCORE_MAP.get(star) ?? 50 + + const transformation = starInfo.transformation + if (transformation != null) { + const transScore = TRANSFORMATION_SCORE_MAP.get(transformation) ?? 0 + baseScore += transScore + } + + const brightness = starInfo.brightness + if (brightness > 0 && brightness <= 100) { + const brightnessFactor = brightness / 100.0 + baseScore = Math.floor(baseScore * brightnessFactor) + } + + return Math.max(0, Math.min(100, baseScore)) +} + +export function calculatePalaceScore(palace: Palace | null): number { + if (palace == null) { + return 50 + } + + let baseScore = 50 + const majorStars = palace.majorStars + + if (majorStars == null || majorStars.length === 0) { + return baseScore + } + + let starScoreSum = 0 + let starCount = 0 + + for (const starInfo of majorStars) { + if (starInfo == null || starInfo.star == null) { + continue + } + + const starScore = calculateStarScore(starInfo) + starScoreSum += starScore + starCount++ + } + + if (starCount > 0) { + baseScore = Math.floor(starScoreSum / starCount) + } + + const finalScore = Math.max(0, Math.min(100, baseScore)) + palace.score = finalScore + + return finalScore +} + +export function calculateLuckyColor(chart: ZiweiChart, fortuneDate: Date): string { + if (chart == null || fortuneDate == null) { + return '红色' + } + + const mingGongBranch = chart.mingGongBranch + if (mingGongBranch == null) { + return '红色' + } + + const baseColor = BRANCH_COLOR_MAP.get(mingGongBranch) ?? '红色' + + const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING) + if (mingGong == null || mingGong.majorStars.length === 0) { + return baseColor + } + + const mainStar = mingGong.majorStars[0] + const starNature = MajorStar.nature(mainStar.star) + + let baseIndex = 0 + for (let i = 0; i < LUCKY_COLORS.length; i++) { + if (LUCKY_COLORS[i] === baseColor) { + baseIndex = i + break + } + } + + let natureOffset = 0 + if (starNature === StarNature.JI) { + natureOffset = 1 + } else if (starNature === StarNature.XIONG) { + natureOffset = -1 + } + + const dayOfMonth = fortuneDate.getDate() + const dateOffset = dayOfMonth % 3 + + const colorIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_COLORS.length + LUCKY_COLORS.length) % LUCKY_COLORS.length + + return LUCKY_COLORS[colorIndex] +} + +export function calculateLuckyNumber(chart: ZiweiChart, fortuneDate: Date): string { + if (chart == null || fortuneDate == null) { + return '8' + } + + const dayOfMonth = fortuneDate.getDate() + const monthValue = fortuneDate.getMonth() + 1 + + let baseNumber = (dayOfMonth + monthValue) % 9 + if (baseNumber === 0) { + baseNumber = 9 + } + + const mingGongBranch = chart.mingGongBranch + if (mingGongBranch == null) { + return String(baseNumber) + } + + const branchIndex = EarthlyBranch.index(mingGongBranch) + + const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING) + let starFactor = 0 + if (mingGong != null && mingGong.majorStars.length > 0) { + const mainStar = mingGong.majorStars[0] + const starNature = MajorStar.nature(mainStar.star) + if (starNature === StarNature.JI) { + starFactor = 1 + } else if (starNature === StarNature.XIONG) { + starFactor = -1 + } + } + + let luckyNumber = (baseNumber + branchIndex + starFactor) % 9 + if (luckyNumber === 0) { + luckyNumber = 9 + } + + return String(luckyNumber) +} + +export function calculateLuckyDirection(chart: ZiweiChart, fortuneDate: Date): string { + if (chart == null || fortuneDate == null) { + return '东方' + } + + const mingGongBranch = chart.mingGongBranch + if (mingGongBranch == null) { + return '东方' + } + + const baseDirection = BRANCH_DIRECTION_MAP.get(mingGongBranch) ?? '东方' + + const mingGong = chart.palaces.find(p => p.palaceType === PalaceType.MING) + if (mingGong == null || mingGong.majorStars.length === 0) { + return baseDirection + } + + const mainStar = mingGong.majorStars[0] + const starNature = MajorStar.nature(mainStar.star) + + let baseIndex = 0 + for (let i = 0; i < LUCKY_DIRECTIONS.length; i++) { + if (LUCKY_DIRECTIONS[i] === baseDirection) { + baseIndex = i + break + } + } + + let natureOffset = 0 + if (starNature === StarNature.JI) { + natureOffset = 1 + } else if (starNature === StarNature.XIONG) { + natureOffset = -1 + } + + const dayOfMonth = fortuneDate.getDate() + const dateOffset = dayOfMonth % 2 + + const directionIndex = ((baseIndex + natureOffset + dateOffset) % LUCKY_DIRECTIONS.length + LUCKY_DIRECTIONS.length) % LUCKY_DIRECTIONS.length + + return LUCKY_DIRECTIONS[directionIndex] +} + +export function determineLuckLevel(score: number): string { + if (score >= 90) return '大吉' + if (score >= 80) return '吉' + if (score >= 70) return '中吉' + if (score >= 60) return '平' + if (score >= 50) return '中平' + if (score >= 40) return '小凶' + return '凶' +} \ No newline at end of file diff --git a/packages/algorithm/tsconfig.json b/packages/algorithm/tsconfig.json new file mode 100644 index 0000000..c6b56d6 --- /dev/null +++ b/packages/algorithm/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "**/__tests__/**"] +} \ No newline at end of file