@gracefullight/saju 0.3.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +6 -10
- package/README.md +6 -10
- package/dist/__tests__/date-fns-adapter.test.js +1 -1
- package/dist/__tests__/four-pillars.test.js +10 -9
- package/dist/__tests__/luck.test.js +2 -2
- package/dist/__tests__/lunar.test.js +1 -1
- package/dist/__tests__/luxon-adapter.test.js +1 -1
- package/dist/__tests__/relations.test.js +2 -2
- package/dist/__tests__/saju.test.js +3 -3
- package/dist/__tests__/sinsals.test.d.ts +2 -0
- package/dist/__tests__/sinsals.test.d.ts.map +1 -0
- package/dist/__tests__/sinsals.test.js +64 -0
- package/dist/__tests__/solar-terms.test.js +2 -2
- package/dist/__tests__/strength.test.js +2 -2
- package/dist/__tests__/ten-gods.test.js +4 -4
- package/dist/__tests__/twelve-stages.test.d.ts +2 -0
- package/dist/__tests__/twelve-stages.test.d.ts.map +1 -0
- package/dist/__tests__/twelve-stages.test.js +86 -0
- package/dist/__tests__/utils.test.d.ts +2 -0
- package/dist/__tests__/utils.test.d.ts.map +1 -0
- package/dist/__tests__/utils.test.js +130 -0
- package/dist/__tests__/yongshen.test.js +2 -2
- package/dist/adapters/date-fns.d.ts +1 -1
- package/dist/adapters/luxon.d.ts +1 -1
- package/dist/core/four-pillars.d.ts +6 -6
- package/dist/core/four-pillars.d.ts.map +1 -1
- package/dist/core/four-pillars.js +10 -26
- package/dist/core/luck.d.ts +22 -3
- package/dist/core/luck.d.ts.map +1 -1
- package/dist/core/luck.js +60 -19
- package/dist/core/relations.d.ts.map +1 -1
- package/dist/core/relations.js +5 -1
- package/dist/core/sinsals.d.ts +19 -0
- package/dist/core/sinsals.d.ts.map +1 -0
- package/dist/core/sinsals.js +339 -0
- package/dist/core/solar-terms.d.ts +1 -1
- package/dist/core/strength.d.ts.map +1 -1
- package/dist/core/strength.js +1 -1
- package/dist/core/ten-gods.d.ts +4 -7
- package/dist/core/ten-gods.d.ts.map +1 -1
- package/dist/core/ten-gods.js +2 -6
- package/dist/core/twelve-stages.d.ts +17 -0
- package/dist/core/twelve-stages.d.ts.map +1 -0
- package/dist/core/twelve-stages.js +77 -0
- package/dist/core/yongshen.d.ts.map +1 -1
- package/dist/core/yongshen.js +2 -2
- package/dist/index.d.ts +26 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -15
- package/dist/types/common.d.ts +12 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +1 -0
- package/dist/utils/constants.d.ts +13 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +59 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +1 -0
- package/package.json +3 -2
package/README.en.md
CHANGED
|
@@ -65,7 +65,7 @@ pnpm add date-fns date-fns-tz
|
|
|
65
65
|
```typescript
|
|
66
66
|
import { DateTime } from "luxon";
|
|
67
67
|
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
68
|
-
import { getSaju
|
|
68
|
+
import { getSaju } from "@gracefullight/saju";
|
|
69
69
|
|
|
70
70
|
const adapter = await createLuxonAdapter();
|
|
71
71
|
|
|
@@ -76,11 +76,10 @@ const birthDateTime = DateTime.fromObject(
|
|
|
76
76
|
|
|
77
77
|
// getSaju: Calculate pillars, ten gods, strength, relations, yongshen, solar terms, major luck, yearly luck all at once
|
|
78
78
|
const result = getSaju(adapter, birthDateTime, {
|
|
79
|
-
longitudeDeg: 126.9778,
|
|
80
79
|
gender: "male", // Required: needed for major luck calculation
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
yearlyLuckRange: { from: 2024, to: 2030 }, //
|
|
80
|
+
// longitudeDeg: 126.9778, // Optional: uses timezone-based longitude if omitted
|
|
81
|
+
// preset: STANDARD_PRESET, // Optional: defaults to STANDARD_PRESET
|
|
82
|
+
// yearlyLuckRange: { from: 2024, to: 2030 }, // Optional: specify yearly luck range
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
console.log(result.pillars); // { year: "己卯", month: "丙子", ... }
|
|
@@ -98,7 +97,7 @@ console.log(result.yearlyLuck); // Yearly luck info
|
|
|
98
97
|
```typescript
|
|
99
98
|
import { DateTime } from "luxon";
|
|
100
99
|
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
101
|
-
import { getFourPillars
|
|
100
|
+
import { getFourPillars } from "@gracefullight/saju";
|
|
102
101
|
|
|
103
102
|
const adapter = await createLuxonAdapter();
|
|
104
103
|
|
|
@@ -107,10 +106,7 @@ const birthDateTime = DateTime.fromObject(
|
|
|
107
106
|
{ zone: "Asia/Seoul" }
|
|
108
107
|
);
|
|
109
108
|
|
|
110
|
-
const result = getFourPillars(adapter, birthDateTime
|
|
111
|
-
longitudeDeg: 126.9778, // Seoul longitude
|
|
112
|
-
preset: STANDARD_PRESET,
|
|
113
|
-
});
|
|
109
|
+
const result = getFourPillars(adapter, birthDateTime);
|
|
114
110
|
|
|
115
111
|
console.log(result);
|
|
116
112
|
```
|
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ pnpm add date-fns date-fns-tz
|
|
|
65
65
|
```typescript
|
|
66
66
|
import { DateTime } from "luxon";
|
|
67
67
|
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
68
|
-
import { getSaju
|
|
68
|
+
import { getSaju } from "@gracefullight/saju";
|
|
69
69
|
|
|
70
70
|
const adapter = await createLuxonAdapter();
|
|
71
71
|
|
|
@@ -76,11 +76,10 @@ const birthDateTime = DateTime.fromObject(
|
|
|
76
76
|
|
|
77
77
|
// getSaju: 사주 팔자, 십신, 신강약, 합충, 용신, 절기, 대운, 세운을 한 번에 계산
|
|
78
78
|
const result = getSaju(adapter, birthDateTime, {
|
|
79
|
-
longitudeDeg: 126.9778,
|
|
80
79
|
gender: "male", // 필수: 대운 계산에 필요
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
yearlyLuckRange: { from: 2024, to: 2030 }, // 세운 범위
|
|
80
|
+
// longitudeDeg: 126.9778, // 선택: 생략 시 타임존 기준 경도 사용
|
|
81
|
+
// preset: STANDARD_PRESET, // 선택: 기본값은 STANDARD_PRESET
|
|
82
|
+
// yearlyLuckRange: { from: 2024, to: 2030 }, // 선택: 세운 범위 지정
|
|
84
83
|
});
|
|
85
84
|
|
|
86
85
|
console.log(result.pillars); // { year: "己卯", month: "丙子", ... }
|
|
@@ -98,7 +97,7 @@ console.log(result.yearlyLuck); // 세운 정보
|
|
|
98
97
|
```typescript
|
|
99
98
|
import { DateTime } from "luxon";
|
|
100
99
|
import { createLuxonAdapter } from "@gracefullight/saju/adapters/luxon";
|
|
101
|
-
import { getFourPillars
|
|
100
|
+
import { getFourPillars } from "@gracefullight/saju";
|
|
102
101
|
|
|
103
102
|
const adapter = await createLuxonAdapter();
|
|
104
103
|
|
|
@@ -107,10 +106,7 @@ const birthDateTime = DateTime.fromObject(
|
|
|
107
106
|
{ zone: "Asia/Seoul" }
|
|
108
107
|
);
|
|
109
108
|
|
|
110
|
-
const result = getFourPillars(adapter, birthDateTime
|
|
111
|
-
longitudeDeg: 126.9778, // 서울 경도
|
|
112
|
-
preset: STANDARD_PRESET,
|
|
113
|
-
});
|
|
109
|
+
const result = getFourPillars(adapter, birthDateTime);
|
|
114
110
|
|
|
115
111
|
console.log(result);
|
|
116
112
|
```
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
2
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
3
|
-
import { createLuxonAdapter } from "
|
|
4
|
-
import { BRANCHES, dayPillarFromDate, effectiveDayDate, getFourPillars, hourPillar, monthPillar, presetA, presetB, STEMS, yearPillar, } from "
|
|
3
|
+
import { createLuxonAdapter } from "../adapters/luxon";
|
|
4
|
+
import { BRANCHES, dayPillarFromDate, effectiveDayDate, getFourPillars, hourPillar, monthPillar, presetA, presetB, STEMS, yearPillar, } from "../core/four-pillars";
|
|
5
5
|
describe("Four Pillars Core", () => {
|
|
6
6
|
let adapter;
|
|
7
7
|
beforeAll(async () => {
|
|
@@ -240,14 +240,15 @@ describe("Four Pillars Core", () => {
|
|
|
240
240
|
});
|
|
241
241
|
expect(resultA.meta.effectiveDayDate.day).toBe(1);
|
|
242
242
|
});
|
|
243
|
-
it("should
|
|
243
|
+
it("should use default longitude from timezone offset when longitudeDeg is omitted", () => {
|
|
244
244
|
const dt = DateTime.fromObject({ year: 2000, month: 1, day: 1, hour: 18, minute: 0 }, { zone: "Asia/Seoul" });
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
245
|
+
const result = getFourPillars(adapter, dt, {
|
|
246
|
+
preset: presetA,
|
|
247
|
+
});
|
|
248
|
+
expect(result.year).toBeDefined();
|
|
249
|
+
expect(result.month).toBeDefined();
|
|
250
|
+
expect(result.day).toBeDefined();
|
|
251
|
+
expect(result.hour).toBeDefined();
|
|
251
252
|
});
|
|
252
253
|
it("should handle leap year dates", () => {
|
|
253
254
|
const dt = DateTime.fromObject({ year: 2000, month: 2, day: 29 }, { zone: "UTC" });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { calculateYearlyLuck, getYearPillar } from "
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { calculateYearlyLuck, getYearPillar } from "../core/luck";
|
|
3
3
|
describe("luck", () => {
|
|
4
4
|
describe("getYearPillar", () => {
|
|
5
5
|
it("returns correct pillar for 1984 (甲子)", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { getLunarDate, getSolarDate } from "
|
|
2
|
+
import { getLunarDate, getSolarDate } from "../core/lunar";
|
|
3
3
|
describe("Lunar Calendar", () => {
|
|
4
4
|
describe("getLunarDate", () => {
|
|
5
5
|
it("should convert solar date to lunar date (2000-01-01)", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
2
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
3
|
-
import { createLuxonAdapter } from "
|
|
3
|
+
import { createLuxonAdapter } from "../adapters/luxon";
|
|
4
4
|
describe("Luxon Adapter", () => {
|
|
5
5
|
let adapter;
|
|
6
6
|
beforeAll(async () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { analyzeRelations,
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeRelations, findBranchClash, findBranchSixCombination, findStemCombination, } from "../core/relations";
|
|
3
3
|
describe("relations", () => {
|
|
4
4
|
describe("findStemCombination", () => {
|
|
5
5
|
it("finds 甲己 combination (earth)", () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
1
|
import { DateTime } from "luxon";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { createLuxonAdapter } from "../adapters/luxon";
|
|
4
|
+
import { getSaju, STANDARD_PRESET } from "../index";
|
|
5
5
|
describe("getSaju integration", () => {
|
|
6
6
|
it("returns complete saju analysis with all required fields", async () => {
|
|
7
7
|
const adapter = await createLuxonAdapter();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sinsals.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sinsals.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeSinsals, SINSAL_INFO, SINSALS } from "../core/sinsals";
|
|
3
|
+
describe("sinsals", () => {
|
|
4
|
+
describe("analyzeSinsals", () => {
|
|
5
|
+
it("should detect peach blossom (도화살)", () => {
|
|
6
|
+
const result = analyzeSinsals("甲寅", "丙寅", "戊卯", "庚午");
|
|
7
|
+
const peachBlossoms = result.matches.filter((m) => m.sinsal === "peachBlossom");
|
|
8
|
+
expect(peachBlossoms.length).toBeGreaterThan(0);
|
|
9
|
+
});
|
|
10
|
+
it("should detect sky horse (역마살)", () => {
|
|
11
|
+
const result = analyzeSinsals("甲寅", "丙寅", "戊申", "庚午");
|
|
12
|
+
const skyHorses = result.matches.filter((m) => m.sinsal === "skyHorse");
|
|
13
|
+
expect(skyHorses.length).toBeGreaterThan(0);
|
|
14
|
+
});
|
|
15
|
+
it("should detect flowery canopy (화개살)", () => {
|
|
16
|
+
const result = analyzeSinsals("甲寅", "丙戌", "戊子", "庚午");
|
|
17
|
+
const floweryCanopies = result.matches.filter((m) => m.sinsal === "floweryCanopy");
|
|
18
|
+
expect(floweryCanopies.length).toBeGreaterThan(0);
|
|
19
|
+
});
|
|
20
|
+
it("should return summary grouped by sinsal", () => {
|
|
21
|
+
const result = analyzeSinsals("甲子", "丙寅", "戊辰", "庚午");
|
|
22
|
+
expect(result.summary).toBeDefined();
|
|
23
|
+
for (const [sinsal, positions] of Object.entries(result.summary)) {
|
|
24
|
+
expect(SINSALS).toContain(sinsal);
|
|
25
|
+
expect(Array.isArray(positions)).toBe(true);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
it("should not have duplicate matches", () => {
|
|
29
|
+
const result = analyzeSinsals("甲子", "丙寅", "戊辰", "庚午");
|
|
30
|
+
const seen = new Set();
|
|
31
|
+
for (const match of result.matches) {
|
|
32
|
+
const key = `${match.sinsal}-${match.position}`;
|
|
33
|
+
expect(seen.has(key)).toBe(false);
|
|
34
|
+
seen.add(key);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
it("should detect sky noble (천을귀인)", () => {
|
|
38
|
+
const result = analyzeSinsals("甲丑", "丙寅", "甲子", "庚午");
|
|
39
|
+
const skyNobles = result.matches.filter((m) => m.sinsal === "skyNoble");
|
|
40
|
+
expect(skyNobles.length).toBeGreaterThan(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("SINSAL_INFO", () => {
|
|
44
|
+
it("should have info for all sinsals", () => {
|
|
45
|
+
for (const sinsal of SINSALS) {
|
|
46
|
+
expect(SINSAL_INFO[sinsal]).toBeDefined();
|
|
47
|
+
expect(SINSAL_INFO[sinsal].korean).toBeDefined();
|
|
48
|
+
expect(SINSAL_INFO[sinsal].hanja).toBeDefined();
|
|
49
|
+
expect(SINSAL_INFO[sinsal].meaning).toBeDefined();
|
|
50
|
+
expect(["auspicious", "inauspicious", "neutral"]).toContain(SINSAL_INFO[sinsal].type);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it("should have correct korean names", () => {
|
|
54
|
+
expect(SINSAL_INFO.peachBlossom.korean).toBe("도화살");
|
|
55
|
+
expect(SINSAL_INFO.skyHorse.korean).toBe("역마살");
|
|
56
|
+
expect(SINSAL_INFO.skyNoble.korean).toBe("천을귀인");
|
|
57
|
+
});
|
|
58
|
+
it("should have correct type classifications", () => {
|
|
59
|
+
expect(SINSAL_INFO.peachBlossom.type).toBe("neutral");
|
|
60
|
+
expect(SINSAL_INFO.skyNoble.type).toBe("auspicious");
|
|
61
|
+
expect(SINSAL_INFO.ghostGate.type).toBe("inauspicious");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DateTime } from "luxon";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
|
-
import { createLuxonAdapter } from "
|
|
4
|
-
import { analyzeSolarTerms, getSolarTermsForYear, SOLAR_TERMS } from "
|
|
3
|
+
import { createLuxonAdapter } from "../adapters/luxon";
|
|
4
|
+
import { analyzeSolarTerms, getSolarTermsForYear, SOLAR_TERMS } from "../core/solar-terms";
|
|
5
5
|
describe("solar-terms", async () => {
|
|
6
6
|
const adapter = await createLuxonAdapter();
|
|
7
7
|
describe("SOLAR_TERMS", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { analyzeStrength } from "
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeStrength } from "../core/strength";
|
|
3
3
|
describe("strength", () => {
|
|
4
4
|
describe("analyzeStrength", () => {
|
|
5
5
|
it("returns a strength level", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import {
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeTenGods, countElements, countTenGods, getBranchElement, getHiddenStems, getStemElement, getStemPolarity, getTenGod, } from "../core/ten-gods";
|
|
3
3
|
describe("ten-gods", () => {
|
|
4
4
|
describe("getStemElement", () => {
|
|
5
5
|
it("returns correct element for each stem", () => {
|
|
@@ -104,8 +104,8 @@ describe("ten-gods", () => {
|
|
|
104
104
|
it("counts ten gods correctly", () => {
|
|
105
105
|
const analysis = analyzeTenGods("甲子", "丙寅", "甲辰", "乙亥");
|
|
106
106
|
const counts = countTenGods(analysis);
|
|
107
|
-
expect(counts
|
|
108
|
-
expect(counts
|
|
107
|
+
expect(counts.비견).toBeGreaterThanOrEqual(1);
|
|
108
|
+
expect(counts.식신).toBeGreaterThanOrEqual(1);
|
|
109
109
|
});
|
|
110
110
|
});
|
|
111
111
|
describe("countElements", () => {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twelve-stages.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/twelve-stages.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeTwelveStages, getTwelveStage, STAGE_INFO, TWELVE_STAGES, } from "../core/twelve-stages";
|
|
3
|
+
describe("twelve-stages", () => {
|
|
4
|
+
describe("getTwelveStage", () => {
|
|
5
|
+
it("should return longLife for 甲 at 亥", () => {
|
|
6
|
+
expect(getTwelveStage("甲", "亥")).toBe("longLife");
|
|
7
|
+
});
|
|
8
|
+
it("should return imperial for 甲 at 卯", () => {
|
|
9
|
+
expect(getTwelveStage("甲", "卯")).toBe("imperial");
|
|
10
|
+
});
|
|
11
|
+
it("should return tomb for 甲 at 未", () => {
|
|
12
|
+
expect(getTwelveStage("甲", "未")).toBe("tomb");
|
|
13
|
+
});
|
|
14
|
+
it("should return longLife for 乙 at 午", () => {
|
|
15
|
+
expect(getTwelveStage("乙", "午")).toBe("longLife");
|
|
16
|
+
});
|
|
17
|
+
it("should return imperial for 乙 at 寅", () => {
|
|
18
|
+
expect(getTwelveStage("乙", "寅")).toBe("imperial");
|
|
19
|
+
});
|
|
20
|
+
it("should return longLife for 丙 at 寅", () => {
|
|
21
|
+
expect(getTwelveStage("丙", "寅")).toBe("longLife");
|
|
22
|
+
});
|
|
23
|
+
it("should return imperial for 丙 at 午", () => {
|
|
24
|
+
expect(getTwelveStage("丙", "午")).toBe("imperial");
|
|
25
|
+
});
|
|
26
|
+
it("should return longLife for 庚 at 巳", () => {
|
|
27
|
+
expect(getTwelveStage("庚", "巳")).toBe("longLife");
|
|
28
|
+
});
|
|
29
|
+
it("should return imperial for 庚 at 酉", () => {
|
|
30
|
+
expect(getTwelveStage("庚", "酉")).toBe("imperial");
|
|
31
|
+
});
|
|
32
|
+
it("should return longLife for 壬 at 申", () => {
|
|
33
|
+
expect(getTwelveStage("壬", "申")).toBe("longLife");
|
|
34
|
+
});
|
|
35
|
+
it("should return imperial for 壬 at 子", () => {
|
|
36
|
+
expect(getTwelveStage("壬", "子")).toBe("imperial");
|
|
37
|
+
});
|
|
38
|
+
it("should throw error for invalid stem", () => {
|
|
39
|
+
expect(() => getTwelveStage("X", "子")).toThrow("Invalid stem: X");
|
|
40
|
+
});
|
|
41
|
+
it("should throw error for invalid branch", () => {
|
|
42
|
+
expect(() => getTwelveStage("甲", "X")).toThrow("Invalid branch: X");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("analyzeTwelveStages", () => {
|
|
46
|
+
it("should analyze all four pillars", () => {
|
|
47
|
+
const result = analyzeTwelveStages("甲子", "丙寅", "戊辰", "庚午");
|
|
48
|
+
expect(result.year).toBeDefined();
|
|
49
|
+
expect(result.month).toBeDefined();
|
|
50
|
+
expect(result.day).toBeDefined();
|
|
51
|
+
expect(result.hour).toBeDefined();
|
|
52
|
+
expect(TWELVE_STAGES).toContain(result.year);
|
|
53
|
+
expect(TWELVE_STAGES).toContain(result.month);
|
|
54
|
+
expect(TWELVE_STAGES).toContain(result.day);
|
|
55
|
+
expect(TWELVE_STAGES).toContain(result.hour);
|
|
56
|
+
});
|
|
57
|
+
it("should correctly analyze 戊 day master", () => {
|
|
58
|
+
const result = analyzeTwelveStages("甲子", "丙寅", "戊辰", "庚午");
|
|
59
|
+
expect(result.month).toBe("longLife");
|
|
60
|
+
expect(result.hour).toBe("imperial");
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
describe("STAGE_INFO", () => {
|
|
64
|
+
it("should have info for all twelve stages", () => {
|
|
65
|
+
for (const stage of TWELVE_STAGES) {
|
|
66
|
+
expect(STAGE_INFO[stage]).toBeDefined();
|
|
67
|
+
expect(STAGE_INFO[stage].korean).toBeDefined();
|
|
68
|
+
expect(STAGE_INFO[stage].hanja).toBeDefined();
|
|
69
|
+
expect(STAGE_INFO[stage].meaning).toBeDefined();
|
|
70
|
+
expect(["strong", "neutral", "weak"]).toContain(STAGE_INFO[stage].strength);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
it("should have correct korean names", () => {
|
|
74
|
+
expect(STAGE_INFO.longLife.korean).toBe("장생");
|
|
75
|
+
expect(STAGE_INFO.bathing.korean).toBe("목욕");
|
|
76
|
+
expect(STAGE_INFO.imperial.korean).toBe("제왕");
|
|
77
|
+
expect(STAGE_INFO.death.korean).toBe("사");
|
|
78
|
+
});
|
|
79
|
+
it("should have correct strength classifications", () => {
|
|
80
|
+
expect(STAGE_INFO.longLife.strength).toBe("strong");
|
|
81
|
+
expect(STAGE_INFO.imperial.strength).toBe("strong");
|
|
82
|
+
expect(STAGE_INFO.decline.strength).toBe("weak");
|
|
83
|
+
expect(STAGE_INFO.tomb.strength).toBe("neutral");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/utils.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { BRANCHES, dayPillarIndexFromJdn, ELEMENTS, getBranchIndex, getPillarIndex, getStemIndex, isYangStem, isYinStem, jdnFromDate, pillarFromIndex, STEMS, } from "../utils";
|
|
3
|
+
describe("constants", () => {
|
|
4
|
+
describe("STEMS", () => {
|
|
5
|
+
it("should have 10 stems", () => {
|
|
6
|
+
expect(STEMS).toHaveLength(10);
|
|
7
|
+
});
|
|
8
|
+
it("should start with 甲 and end with 癸", () => {
|
|
9
|
+
expect(STEMS[0]).toBe("甲");
|
|
10
|
+
expect(STEMS[9]).toBe("癸");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("BRANCHES", () => {
|
|
14
|
+
it("should have 12 branches", () => {
|
|
15
|
+
expect(BRANCHES).toHaveLength(12);
|
|
16
|
+
});
|
|
17
|
+
it("should start with 子 and end with 亥", () => {
|
|
18
|
+
expect(BRANCHES[0]).toBe("子");
|
|
19
|
+
expect(BRANCHES[11]).toBe("亥");
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe("ELEMENTS", () => {
|
|
23
|
+
it("should have 5 elements", () => {
|
|
24
|
+
expect(ELEMENTS).toHaveLength(5);
|
|
25
|
+
});
|
|
26
|
+
it("should contain wood, fire, earth, metal, water", () => {
|
|
27
|
+
expect(ELEMENTS).toContain("wood");
|
|
28
|
+
expect(ELEMENTS).toContain("fire");
|
|
29
|
+
expect(ELEMENTS).toContain("earth");
|
|
30
|
+
expect(ELEMENTS).toContain("metal");
|
|
31
|
+
expect(ELEMENTS).toContain("water");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
describe("getStemIndex", () => {
|
|
36
|
+
it("should return correct index for each stem", () => {
|
|
37
|
+
expect(getStemIndex("甲")).toBe(0);
|
|
38
|
+
expect(getStemIndex("乙")).toBe(1);
|
|
39
|
+
expect(getStemIndex("癸")).toBe(9);
|
|
40
|
+
});
|
|
41
|
+
it("should return -1 for invalid stem", () => {
|
|
42
|
+
expect(getStemIndex("X")).toBe(-1);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
describe("getBranchIndex", () => {
|
|
46
|
+
it("should return correct index for each branch", () => {
|
|
47
|
+
expect(getBranchIndex("子")).toBe(0);
|
|
48
|
+
expect(getBranchIndex("丑")).toBe(1);
|
|
49
|
+
expect(getBranchIndex("亥")).toBe(11);
|
|
50
|
+
});
|
|
51
|
+
it("should return -1 for invalid branch", () => {
|
|
52
|
+
expect(getBranchIndex("X")).toBe(-1);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
describe("pillarFromIndex", () => {
|
|
56
|
+
it("should return 甲子 for index 0", () => {
|
|
57
|
+
expect(pillarFromIndex(0)).toBe("甲子");
|
|
58
|
+
});
|
|
59
|
+
it("should return 乙丑 for index 1", () => {
|
|
60
|
+
expect(pillarFromIndex(1)).toBe("乙丑");
|
|
61
|
+
});
|
|
62
|
+
it("should handle negative indices", () => {
|
|
63
|
+
expect(pillarFromIndex(-1)).toBe("癸亥");
|
|
64
|
+
});
|
|
65
|
+
it("should wrap around for indices >= 60", () => {
|
|
66
|
+
expect(pillarFromIndex(60)).toBe("甲子");
|
|
67
|
+
expect(pillarFromIndex(61)).toBe("乙丑");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("getPillarIndex", () => {
|
|
71
|
+
it("should return 0 for 甲子", () => {
|
|
72
|
+
expect(getPillarIndex("甲子")).toBe(0);
|
|
73
|
+
});
|
|
74
|
+
it("should return correct index for 乙丑", () => {
|
|
75
|
+
expect(getPillarIndex("乙丑")).toBe(1);
|
|
76
|
+
});
|
|
77
|
+
it("should be inverse of pillarFromIndex", () => {
|
|
78
|
+
for (let i = 0; i < 60; i++) {
|
|
79
|
+
const pillar = pillarFromIndex(i);
|
|
80
|
+
expect(getPillarIndex(pillar)).toBe(i);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
describe("jdnFromDate", () => {
|
|
85
|
+
it("should return correct JDN for known date", () => {
|
|
86
|
+
expect(jdnFromDate(2000, 1, 1)).toBe(2451545);
|
|
87
|
+
});
|
|
88
|
+
it("should return correct JDN for epoch date", () => {
|
|
89
|
+
expect(jdnFromDate(1970, 1, 1)).toBe(2440588);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe("dayPillarIndexFromJdn", () => {
|
|
93
|
+
it("should return value between 0 and 59", () => {
|
|
94
|
+
const idx = dayPillarIndexFromJdn(2451545);
|
|
95
|
+
expect(idx).toBeGreaterThanOrEqual(0);
|
|
96
|
+
expect(idx).toBeLessThan(60);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("isYangStem", () => {
|
|
100
|
+
it("should return true for yang stems", () => {
|
|
101
|
+
expect(isYangStem("甲")).toBe(true);
|
|
102
|
+
expect(isYangStem("丙")).toBe(true);
|
|
103
|
+
expect(isYangStem("戊")).toBe(true);
|
|
104
|
+
expect(isYangStem("庚")).toBe(true);
|
|
105
|
+
expect(isYangStem("壬")).toBe(true);
|
|
106
|
+
});
|
|
107
|
+
it("should return false for yin stems", () => {
|
|
108
|
+
expect(isYangStem("乙")).toBe(false);
|
|
109
|
+
expect(isYangStem("丁")).toBe(false);
|
|
110
|
+
expect(isYangStem("己")).toBe(false);
|
|
111
|
+
expect(isYangStem("辛")).toBe(false);
|
|
112
|
+
expect(isYangStem("癸")).toBe(false);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
describe("isYinStem", () => {
|
|
116
|
+
it("should return true for yin stems", () => {
|
|
117
|
+
expect(isYinStem("乙")).toBe(true);
|
|
118
|
+
expect(isYinStem("丁")).toBe(true);
|
|
119
|
+
expect(isYinStem("己")).toBe(true);
|
|
120
|
+
expect(isYinStem("辛")).toBe(true);
|
|
121
|
+
expect(isYinStem("癸")).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
it("should return false for yang stems", () => {
|
|
124
|
+
expect(isYinStem("甲")).toBe(false);
|
|
125
|
+
expect(isYinStem("丙")).toBe(false);
|
|
126
|
+
expect(isYinStem("戊")).toBe(false);
|
|
127
|
+
expect(isYinStem("庚")).toBe(false);
|
|
128
|
+
expect(isYinStem("壬")).toBe(false);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { describe,
|
|
2
|
-
import { analyzeYongShen, getElementRecommendations } from "
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { analyzeYongShen, getElementRecommendations } from "../core/yongshen";
|
|
3
3
|
describe("yongshen", () => {
|
|
4
4
|
describe("analyzeYongShen", () => {
|
|
5
5
|
it("returns yongshen analysis result", () => {
|
package/dist/adapters/luxon.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { DateAdapter } from "
|
|
2
|
-
import { type LunarDate } from "
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
import type { DateAdapter } from "../adapters/date-adapter";
|
|
2
|
+
import { type LunarDate } from "../core/lunar";
|
|
3
|
+
import { BRANCHES, STEMS } from "../utils";
|
|
4
|
+
export { STEMS, BRANCHES };
|
|
5
5
|
export declare const STANDARD_PRESET: {
|
|
6
6
|
dayBoundary: "midnight";
|
|
7
7
|
useMeanSolarTimeForHour: boolean;
|
|
@@ -65,8 +65,8 @@ export declare function hourPillar<T>(adapter: DateAdapter<T>, dtLocal: T, { lon
|
|
|
65
65
|
day: number;
|
|
66
66
|
};
|
|
67
67
|
};
|
|
68
|
-
export declare function getFourPillars<T>(adapter: DateAdapter<T>, dtLocal: T, { longitudeDeg, tzOffsetHours, preset, }
|
|
69
|
-
longitudeDeg
|
|
68
|
+
export declare function getFourPillars<T>(adapter: DateAdapter<T>, dtLocal: T, { longitudeDeg, tzOffsetHours, preset, }?: {
|
|
69
|
+
longitudeDeg?: number;
|
|
70
70
|
tzOffsetHours?: number;
|
|
71
71
|
preset?: typeof presetA | typeof presetB;
|
|
72
72
|
}): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"four-pillars.d.ts","sourceRoot":"","sources":["../../src/core/four-pillars.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"four-pillars.d.ts","sourceRoot":"","sources":["../../src/core/four-pillars.ts"],"names":[],"mappings":"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"}
|