@openfate/bazi-engine 1.0.0 → 1.1.1
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.md +33 -5
- package/dist/core/cycles.js +20 -2
- package/dist/core/interactions.js +10 -2
- package/dist/core/pillars.d.ts +33 -7
- package/dist/core/pillars.js +67 -11
- package/dist/core/tenGods.d.ts +4 -0
- package/dist/core/tenGods.js +42 -0
- package/dist/core/validation.d.ts +8 -0
- package/dist/core/validation.js +83 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +105 -13
- package/dist/types.d.ts +55 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
> **
|
|
6
|
+
> **A deterministic, production-oriented Four Pillars (八字) engine for JavaScript and TypeScript.**
|
|
7
7
|
> Powered by [OpenFate.ai](https://openfate.ai) — the AI-native metaphysical analysis platform.
|
|
8
8
|
|
|
9
|
-
Getting the Four Pillars right is hard. Solar Term boundaries (节气), True Solar Time correction (真太阳时), day-change boundaries,
|
|
9
|
+
Getting the Four Pillars right is hard. Solar Term boundaries (节气), True Solar Time correction (真太阳时), day-change boundaries, and lunar conversion all require explicit, testable policy. This package exposes those calculations and the metadata needed to audit them.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
@@ -155,6 +155,10 @@ Main entry point. Returns a `BaziChart` with:
|
|
|
155
155
|
- `daYun` — 9 Major Luck Cycles with start year/age
|
|
156
156
|
- `interactions` — All detected branch interactions (7 types)
|
|
157
157
|
- `solarTimeInfo` — True Solar Time details (or null if disabled)
|
|
158
|
+
- `calendar` — Civil solar input, calculation solar time, converted lunar date, and zodiac
|
|
159
|
+
- `metadata` — Applied True Solar Time, timezone, DST, and day-boundary policy
|
|
160
|
+
|
|
161
|
+
Each pillar preserves the simple `stem`, `branch`, and `element` fields and also includes Ten Gods, hidden stems, Na Yin, Xun, void branches, and growth stage.
|
|
158
162
|
|
|
159
163
|
### `detectInteractions(natal, annualBranch?): BranchInteraction[]`
|
|
160
164
|
Detect interactions in a natal chart, optionally against an annual branch (太岁).
|
|
@@ -166,13 +170,15 @@ Low-level pillar generator — use when you've already handled time correction y
|
|
|
166
170
|
|
|
167
171
|
## 🛡️ Testing & Reliability
|
|
168
172
|
|
|
169
|
-
Bazi calculations are notoriously prone to edge-case bugs. We maintain a
|
|
173
|
+
Bazi calculations are notoriously prone to edge-case bugs. We maintain a regression suite of 100+ calendrical cases plus focused public-contract tests:
|
|
170
174
|
|
|
171
175
|
- **24 Solar Terms**: Minute-level precision for *Li Chun*, *Jing Zhe*, etc.
|
|
172
176
|
- **Early/Late Zi Hour**: Handles the 23:00 day-rollover across month/year boundaries.
|
|
173
177
|
- **Global Distortion**: Extreme longitudes (e.g., Xinjiang, Iceland) and fractional timezones (e.g., India UTC+5.5).
|
|
174
|
-
- **Historical DST**:
|
|
175
|
-
- **Century Boundaries**:
|
|
178
|
+
- **Historical DST**: Explicit `dstOffset` input or IANA timezone rules; the engine does not guess historical policy.
|
|
179
|
+
- **Century Boundaries**: Regression coverage for dates across 1800, 1900, 2000, and 2100.
|
|
180
|
+
- **Da Yun**: Exact start date, elapsed-age convention, direction, and cycle boundaries.
|
|
181
|
+
- **Factual Enrichment**: Ten Gods, hidden stems, Na Yin, Xun, void branches, and growth stages.
|
|
176
182
|
|
|
177
183
|
Run the tests yourself:
|
|
178
184
|
```bash
|
|
@@ -201,6 +207,28 @@ Birth input (civil time)
|
|
|
201
207
|
|
|
202
208
|
`@openfate/bazi-engine` gives you the data. **[OpenFate.ai](https://openfate.ai/bazi)** gives you the complete AI-powered interpretation — relationship analysis, career forecasting, and interactive AI chat with your chart.
|
|
203
209
|
|
|
210
|
+
## GitHub Pages Site
|
|
211
|
+
|
|
212
|
+
This repository now includes a static GitHub Pages microsite in [`docs/index.html`](docs/index.html).
|
|
213
|
+
|
|
214
|
+
To publish it:
|
|
215
|
+
|
|
216
|
+
1. Push the repository to GitHub on the `main` branch.
|
|
217
|
+
2. In the repository settings, open `Pages`.
|
|
218
|
+
3. Under `Build and deployment`, select `GitHub Actions`.
|
|
219
|
+
4. Push a new commit or manually run the `Deploy GitHub Pages` workflow.
|
|
220
|
+
|
|
221
|
+
If the workflow fails with `Get Pages site failed`:
|
|
222
|
+
|
|
223
|
+
- The repository does not have GitHub Pages enabled yet.
|
|
224
|
+
- The fastest fix is to open `Settings` -> `Pages` and set `Source` to `GitHub Actions`, then rerun the workflow.
|
|
225
|
+
- Optional: create a repository secret named `PAGES_TOKEN` with a token that can administer Pages. The workflow will then try to enable Pages automatically on the first run.
|
|
226
|
+
|
|
227
|
+
Once enabled, the site will publish at:
|
|
228
|
+
|
|
229
|
+
- `https://openfate-ai.github.io/bazi-engine/` for the current repository name
|
|
230
|
+
- or `https://<owner>.github.io/<repo>/` for forks and renamed repositories
|
|
231
|
+
|
|
204
232
|
## License
|
|
205
233
|
|
|
206
234
|
MIT — Free to use, modify, and distribute commercially.
|
package/dist/core/cycles.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// ============================================================================
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.calculateDaYun = calculateDaYun;
|
|
7
|
+
const pillars_1 = require("./pillars");
|
|
8
|
+
const tenGods_1 = require("./tenGods");
|
|
7
9
|
/**
|
|
8
10
|
* calculateDaYun
|
|
9
11
|
*
|
|
@@ -21,6 +23,7 @@ exports.calculateDaYun = calculateDaYun;
|
|
|
21
23
|
function calculateDaYun(eightChar, gender, birthYear) {
|
|
22
24
|
const genderInt = gender === 'male' ? 1 : 0;
|
|
23
25
|
const yun = eightChar.getYun(genderInt);
|
|
26
|
+
const dayStem = eightChar.getDayGan();
|
|
24
27
|
const rawCycles = yun.getDaYun();
|
|
25
28
|
const cycles = [];
|
|
26
29
|
// Index 0 is the natal chart segment — cycles start at index 1
|
|
@@ -31,6 +34,7 @@ function calculateDaYun(eightChar, gender, birthYear) {
|
|
|
31
34
|
const stem = ganZhi.substring(0, 1);
|
|
32
35
|
const branch = ganZhi.substring(1, 2);
|
|
33
36
|
const startYear = dy.getStartYear();
|
|
37
|
+
const endYear = dy.getEndYear();
|
|
34
38
|
cycles.push({
|
|
35
39
|
index: i,
|
|
36
40
|
stem,
|
|
@@ -38,12 +42,26 @@ function calculateDaYun(eightChar, gender, birthYear) {
|
|
|
38
42
|
ganZhi,
|
|
39
43
|
startYear,
|
|
40
44
|
startAge: startYear - birthYear,
|
|
45
|
+
endYear,
|
|
46
|
+
endAge: endYear - birthYear,
|
|
47
|
+
stemTenGod: (0, tenGods_1.calculateTenGod)(dayStem, stem),
|
|
48
|
+
branchTenGod: (0, tenGods_1.calculateTenGod)(dayStem, (0, pillars_1.getMainQi)(branch)),
|
|
41
49
|
});
|
|
42
50
|
}
|
|
51
|
+
const firstCycle = cycles[0];
|
|
52
|
+
if (!firstCycle)
|
|
53
|
+
throw new Error('Unable to calculate Da Yun cycles.');
|
|
43
54
|
return {
|
|
44
55
|
cycles,
|
|
45
56
|
isForward: yun.isForward(),
|
|
46
|
-
startYear:
|
|
47
|
-
startAge:
|
|
57
|
+
startYear: firstCycle.startYear,
|
|
58
|
+
startAge: firstCycle.startAge,
|
|
59
|
+
startDate: yun.getStartSolar().toYmdHms(),
|
|
60
|
+
startOffset: {
|
|
61
|
+
years: yun.getStartYear(),
|
|
62
|
+
months: yun.getStartMonth(),
|
|
63
|
+
days: yun.getStartDay(),
|
|
64
|
+
hours: yun.getStartHour(),
|
|
65
|
+
},
|
|
48
66
|
};
|
|
49
67
|
}
|
|
@@ -59,8 +59,16 @@ function hasBranch(branches, target) {
|
|
|
59
59
|
return branches.find(b => b.branch === target);
|
|
60
60
|
}
|
|
61
61
|
function groupHas(branches, group) {
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
const remaining = [...branches];
|
|
63
|
+
const found = [];
|
|
64
|
+
for (const target of group) {
|
|
65
|
+
const index = remaining.findIndex(item => item.branch === target);
|
|
66
|
+
if (index === -1)
|
|
67
|
+
return null;
|
|
68
|
+
found.push(remaining[index]);
|
|
69
|
+
remaining.splice(index, 1);
|
|
70
|
+
}
|
|
71
|
+
return found;
|
|
64
72
|
}
|
|
65
73
|
// ── Main Detector ────────────────────────────────────────────────────────────
|
|
66
74
|
/**
|
package/dist/core/pillars.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FourPillars, DayBoundaryMode } from '../types';
|
|
1
|
+
import { FourPillars, DayBoundaryMode, StemInfo } from '../types';
|
|
2
2
|
/** Typed interface for the EightChar object returned by lunar-javascript */
|
|
3
3
|
export interface LunarEightChar {
|
|
4
4
|
setSect(sect: number): void;
|
|
@@ -10,6 +10,30 @@ export interface LunarEightChar {
|
|
|
10
10
|
getDayZhi(): string;
|
|
11
11
|
getTimeGan(): string;
|
|
12
12
|
getTimeZhi(): string;
|
|
13
|
+
getYearNaYin(): string;
|
|
14
|
+
getYearShiShenGan(): string;
|
|
15
|
+
getYearShiShenZhi(): string[];
|
|
16
|
+
getYearDiShi(): string;
|
|
17
|
+
getYearXun(): string;
|
|
18
|
+
getYearXunKong(): string;
|
|
19
|
+
getMonthNaYin(): string;
|
|
20
|
+
getMonthShiShenGan(): string;
|
|
21
|
+
getMonthShiShenZhi(): string[];
|
|
22
|
+
getMonthDiShi(): string;
|
|
23
|
+
getMonthXun(): string;
|
|
24
|
+
getMonthXunKong(): string;
|
|
25
|
+
getDayNaYin(): string;
|
|
26
|
+
getDayShiShenGan(): string;
|
|
27
|
+
getDayShiShenZhi(): string[];
|
|
28
|
+
getDayDiShi(): string;
|
|
29
|
+
getDayXun(): string;
|
|
30
|
+
getDayXunKong(): string;
|
|
31
|
+
getTimeNaYin(): string;
|
|
32
|
+
getTimeShiShenGan(): string;
|
|
33
|
+
getTimeShiShenZhi(): string[];
|
|
34
|
+
getTimeDiShi(): string;
|
|
35
|
+
getTimeXun(): string;
|
|
36
|
+
getTimeXunKong(): string;
|
|
13
37
|
getYun(gender: number): LunarYun;
|
|
14
38
|
}
|
|
15
39
|
/** Typed interface for the Yun (Luck Cycles) manager */
|
|
@@ -18,13 +42,20 @@ export interface LunarYun {
|
|
|
18
42
|
getStartYear(): number;
|
|
19
43
|
getStartMonth(): number;
|
|
20
44
|
getStartDay(): number;
|
|
45
|
+
getStartHour(): number;
|
|
46
|
+
getStartSolar(): LunarSolar;
|
|
21
47
|
getDaYun(): LunarDaYun[];
|
|
22
48
|
}
|
|
49
|
+
export interface LunarSolar {
|
|
50
|
+
toYmdHms(): string;
|
|
51
|
+
}
|
|
23
52
|
/** Typed interface for a single Da Yun period */
|
|
24
53
|
export interface LunarDaYun {
|
|
25
54
|
getGanZhi(): string;
|
|
26
55
|
getStartYear(): number;
|
|
27
56
|
getEndYear(): number;
|
|
57
|
+
getStartAge(): number;
|
|
58
|
+
getEndAge(): number;
|
|
28
59
|
}
|
|
29
60
|
export interface PillarResult {
|
|
30
61
|
pillars: FourPillars;
|
|
@@ -53,9 +84,4 @@ export declare function getMainQi(branch: string): string;
|
|
|
53
84
|
/**
|
|
54
85
|
* getStemInfo - Returns enriched stem details
|
|
55
86
|
*/
|
|
56
|
-
export declare function getStemInfo(stem: string):
|
|
57
|
-
char: string;
|
|
58
|
-
pinyin: string;
|
|
59
|
-
element: import("../types").FiveElement;
|
|
60
|
-
polarity: import("../types").Polarity;
|
|
61
|
-
};
|
|
87
|
+
export declare function getStemInfo(stem: string): StemInfo;
|
package/dist/core/pillars.js
CHANGED
|
@@ -8,12 +8,41 @@ exports.getMainQi = getMainQi;
|
|
|
8
8
|
exports.getStemInfo = getStemInfo;
|
|
9
9
|
const lunar_javascript_1 = require("lunar-javascript");
|
|
10
10
|
const constants_1 = require("../constants");
|
|
11
|
-
function
|
|
12
|
-
|
|
11
|
+
function getRequiredElement(value, mapping, label) {
|
|
12
|
+
const result = mapping[value];
|
|
13
|
+
if (!result)
|
|
14
|
+
throw new Error(`Unsupported ${label}: ${value}`);
|
|
15
|
+
return result;
|
|
16
|
+
}
|
|
17
|
+
function getRequiredPolarity(stem) {
|
|
18
|
+
const polarity = constants_1.STEM_TO_POLARITY[stem];
|
|
19
|
+
if (!polarity)
|
|
20
|
+
throw new Error(`Unsupported Heavenly Stem: ${stem}`);
|
|
21
|
+
return polarity;
|
|
22
|
+
}
|
|
23
|
+
function buildPillar(stem, branch, details) {
|
|
24
|
+
const hiddenStemDefinitions = constants_1.BRANCH_HIDDEN_STEMS[branch];
|
|
25
|
+
if (!hiddenStemDefinitions)
|
|
26
|
+
throw new Error(`Unsupported Earthly Branch: ${branch}`);
|
|
13
27
|
return {
|
|
14
28
|
stem,
|
|
15
29
|
branch,
|
|
16
|
-
element: (
|
|
30
|
+
element: getRequiredElement(stem, constants_1.STEM_TO_ELEMENT, 'Heavenly Stem'),
|
|
31
|
+
ganZhi: `${stem}${branch}`,
|
|
32
|
+
stemPolarity: getRequiredPolarity(stem),
|
|
33
|
+
stemTenGod: details.stemTenGod,
|
|
34
|
+
branchElement: getRequiredElement(branch, constants_1.BRANCH_TO_ELEMENT, 'Earthly Branch'),
|
|
35
|
+
hiddenStems: hiddenStemDefinitions.map((hiddenStem, index) => ({
|
|
36
|
+
stem: hiddenStem.stem,
|
|
37
|
+
element: getRequiredElement(hiddenStem.stem, constants_1.STEM_TO_ELEMENT, 'Hidden Stem'),
|
|
38
|
+
polarity: getRequiredPolarity(hiddenStem.stem),
|
|
39
|
+
tenGod: details.hiddenStemTenGods[index],
|
|
40
|
+
isMain: Boolean(hiddenStem.isMain),
|
|
41
|
+
})),
|
|
42
|
+
naYin: details.naYin,
|
|
43
|
+
xun: details.xun,
|
|
44
|
+
voidBranches: details.voidBranches.split(''),
|
|
45
|
+
growthStage: details.growthStage,
|
|
17
46
|
};
|
|
18
47
|
}
|
|
19
48
|
/**
|
|
@@ -46,10 +75,38 @@ function generatePillarsFromSolar(year, month, day, hour, minute, second, dayBou
|
|
|
46
75
|
const dayStem = eightChar.getDayGan();
|
|
47
76
|
return {
|
|
48
77
|
pillars: {
|
|
49
|
-
year: buildPillar(eightChar.getYearGan(), eightChar.getYearZhi()
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
78
|
+
year: buildPillar(eightChar.getYearGan(), eightChar.getYearZhi(), {
|
|
79
|
+
stemTenGod: eightChar.getYearShiShenGan(),
|
|
80
|
+
hiddenStemTenGods: eightChar.getYearShiShenZhi(),
|
|
81
|
+
naYin: eightChar.getYearNaYin(),
|
|
82
|
+
xun: eightChar.getYearXun(),
|
|
83
|
+
voidBranches: eightChar.getYearXunKong(),
|
|
84
|
+
growthStage: eightChar.getYearDiShi(),
|
|
85
|
+
}),
|
|
86
|
+
month: buildPillar(eightChar.getMonthGan(), eightChar.getMonthZhi(), {
|
|
87
|
+
stemTenGod: eightChar.getMonthShiShenGan(),
|
|
88
|
+
hiddenStemTenGods: eightChar.getMonthShiShenZhi(),
|
|
89
|
+
naYin: eightChar.getMonthNaYin(),
|
|
90
|
+
xun: eightChar.getMonthXun(),
|
|
91
|
+
voidBranches: eightChar.getMonthXunKong(),
|
|
92
|
+
growthStage: eightChar.getMonthDiShi(),
|
|
93
|
+
}),
|
|
94
|
+
day: buildPillar(dayStem, eightChar.getDayZhi(), {
|
|
95
|
+
stemTenGod: eightChar.getDayShiShenGan(),
|
|
96
|
+
hiddenStemTenGods: eightChar.getDayShiShenZhi(),
|
|
97
|
+
naYin: eightChar.getDayNaYin(),
|
|
98
|
+
xun: eightChar.getDayXun(),
|
|
99
|
+
voidBranches: eightChar.getDayXunKong(),
|
|
100
|
+
growthStage: eightChar.getDayDiShi(),
|
|
101
|
+
}),
|
|
102
|
+
hour: hasTime ? buildPillar(eightChar.getTimeGan(), eightChar.getTimeZhi(), {
|
|
103
|
+
stemTenGod: eightChar.getTimeShiShenGan(),
|
|
104
|
+
hiddenStemTenGods: eightChar.getTimeShiShenZhi(),
|
|
105
|
+
naYin: eightChar.getTimeNaYin(),
|
|
106
|
+
xun: eightChar.getTimeXun(),
|
|
107
|
+
voidBranches: eightChar.getTimeXunKong(),
|
|
108
|
+
growthStage: eightChar.getTimeDiShi(),
|
|
109
|
+
}) : null,
|
|
53
110
|
},
|
|
54
111
|
eightChar,
|
|
55
112
|
dayStem,
|
|
@@ -68,11 +125,10 @@ function getMainQi(branch) {
|
|
|
68
125
|
* getStemInfo - Returns enriched stem details
|
|
69
126
|
*/
|
|
70
127
|
function getStemInfo(stem) {
|
|
71
|
-
var _a, _b, _c;
|
|
72
128
|
return {
|
|
73
129
|
char: stem,
|
|
74
|
-
pinyin:
|
|
75
|
-
element: (
|
|
76
|
-
polarity: (
|
|
130
|
+
pinyin: constants_1.STEM_TO_PINYIN[stem],
|
|
131
|
+
element: getRequiredElement(stem, constants_1.STEM_TO_ELEMENT, 'Heavenly Stem'),
|
|
132
|
+
polarity: getRequiredPolarity(stem),
|
|
77
133
|
};
|
|
78
134
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.calculateTenGod = calculateTenGod;
|
|
4
|
+
const constants_1 = require("../constants");
|
|
5
|
+
const ELEMENTS = ['wood', 'fire', 'earth', 'metal', 'water'];
|
|
6
|
+
const TEN_GOD_NAMES = {
|
|
7
|
+
self: { same: '比肩', different: '劫财' },
|
|
8
|
+
output: { same: '食神', different: '伤官' },
|
|
9
|
+
wealth: { same: '偏财', different: '正财' },
|
|
10
|
+
power: { same: '七杀', different: '正官' },
|
|
11
|
+
resource: { same: '偏印', different: '正印' },
|
|
12
|
+
};
|
|
13
|
+
function getRelationship(dayMaster, target) {
|
|
14
|
+
const dayMasterIndex = ELEMENTS.indexOf(dayMaster);
|
|
15
|
+
const targetIndex = ELEMENTS.indexOf(target);
|
|
16
|
+
const difference = (targetIndex - dayMasterIndex + ELEMENTS.length) % ELEMENTS.length;
|
|
17
|
+
if (difference === 0)
|
|
18
|
+
return 'self';
|
|
19
|
+
if (difference === 1)
|
|
20
|
+
return 'output';
|
|
21
|
+
if (difference === 2)
|
|
22
|
+
return 'wealth';
|
|
23
|
+
if (difference === 3)
|
|
24
|
+
return 'power';
|
|
25
|
+
return 'resource';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* calculateTenGod - Returns the classical Ten God relationship between two stems.
|
|
29
|
+
*/
|
|
30
|
+
function calculateTenGod(dayMaster, targetStem) {
|
|
31
|
+
const dayMasterElement = constants_1.STEM_TO_ELEMENT[dayMaster];
|
|
32
|
+
const targetElement = constants_1.STEM_TO_ELEMENT[targetStem];
|
|
33
|
+
const dayMasterPolarity = constants_1.STEM_TO_POLARITY[dayMaster];
|
|
34
|
+
const targetPolarity = constants_1.STEM_TO_POLARITY[targetStem];
|
|
35
|
+
if (!dayMasterElement || !dayMasterPolarity)
|
|
36
|
+
throw new Error(`Unsupported Day Master: ${dayMaster}`);
|
|
37
|
+
if (!targetElement || !targetPolarity)
|
|
38
|
+
throw new Error(`Unsupported target stem: ${targetStem}`);
|
|
39
|
+
const relationship = getRelationship(dayMasterElement, targetElement);
|
|
40
|
+
const names = TEN_GOD_NAMES[relationship];
|
|
41
|
+
return dayMasterPolarity === targetPolarity ? names.same : names.different;
|
|
42
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaziInput } from '../types';
|
|
2
|
+
export declare class BaziInputError extends RangeError {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* validateBaziInput - Validates the public calculation contract before invoking calendar libraries.
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateBaziInput(input: BaziInput): void;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaziInputError = void 0;
|
|
4
|
+
exports.validateBaziInput = validateBaziInput;
|
|
5
|
+
const lunar_javascript_1 = require("lunar-javascript");
|
|
6
|
+
const MIN_YEAR = 1800;
|
|
7
|
+
const MAX_YEAR = 2100;
|
|
8
|
+
class BaziInputError extends RangeError {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'BaziInputError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.BaziInputError = BaziInputError;
|
|
15
|
+
function assertIntegerInRange(value, minimum, maximum, field) {
|
|
16
|
+
if (!Number.isInteger(value) || value < minimum || value > maximum) {
|
|
17
|
+
throw new BaziInputError(`${field} must be an integer between ${minimum} and ${maximum}.`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function assertNumberInRange(value, minimum, maximum, field) {
|
|
21
|
+
if (!Number.isFinite(value) || value < minimum || value > maximum) {
|
|
22
|
+
throw new BaziInputError(`${field} must be between ${minimum} and ${maximum}.`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function assertSolarDate(input) {
|
|
26
|
+
const date = new Date(Date.UTC(input.year, input.month - 1, input.day));
|
|
27
|
+
const isSameDate = date.getUTCFullYear() === input.year
|
|
28
|
+
&& date.getUTCMonth() === input.month - 1
|
|
29
|
+
&& date.getUTCDate() === input.day;
|
|
30
|
+
if (!isSameDate)
|
|
31
|
+
throw new BaziInputError('The supplied solar date does not exist.');
|
|
32
|
+
if (input.isLeapMonth)
|
|
33
|
+
throw new BaziInputError('isLeapMonth is only valid for lunar calendar input.');
|
|
34
|
+
}
|
|
35
|
+
function assertLunarDate(input) {
|
|
36
|
+
const lunarYear = lunar_javascript_1.LunarYear.fromYear(input.year);
|
|
37
|
+
const requestedMonth = input.isLeapMonth ? -input.month : input.month;
|
|
38
|
+
const lunarMonth = lunarYear.getMonth(requestedMonth);
|
|
39
|
+
if (!lunarMonth) {
|
|
40
|
+
const label = input.isLeapMonth ? 'leap lunar month' : 'lunar month';
|
|
41
|
+
throw new BaziInputError(`The supplied ${label} does not exist in ${input.year}.`);
|
|
42
|
+
}
|
|
43
|
+
if (input.day > lunarMonth.getDayCount()) {
|
|
44
|
+
throw new BaziInputError('The supplied lunar date does not exist.');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* validateBaziInput - Validates the public calculation contract before invoking calendar libraries.
|
|
49
|
+
*/
|
|
50
|
+
function validateBaziInput(input) {
|
|
51
|
+
var _a, _b;
|
|
52
|
+
assertIntegerInRange(input.year, MIN_YEAR, MAX_YEAR, 'year');
|
|
53
|
+
assertIntegerInRange(input.month, 1, 12, 'month');
|
|
54
|
+
assertIntegerInRange(input.day, 1, 31, 'day');
|
|
55
|
+
if (input.hour !== undefined)
|
|
56
|
+
assertIntegerInRange(input.hour, 0, 23, 'hour');
|
|
57
|
+
if (input.minute !== undefined)
|
|
58
|
+
assertIntegerInRange(input.minute, 0, 59, 'minute');
|
|
59
|
+
if (input.minute !== undefined && input.hour === undefined) {
|
|
60
|
+
throw new BaziInputError('hour is required when minute is supplied.');
|
|
61
|
+
}
|
|
62
|
+
if (input.longitude !== undefined)
|
|
63
|
+
assertNumberInRange(input.longitude, -180, 180, 'longitude');
|
|
64
|
+
if (input.timezone !== undefined)
|
|
65
|
+
assertNumberInRange(input.timezone, -14, 14, 'timezone');
|
|
66
|
+
if (input.dstOffset !== undefined)
|
|
67
|
+
assertNumberInRange(input.dstOffset, -2, 2, 'dstOffset');
|
|
68
|
+
if (input.timezoneId !== undefined && input.timezoneId.trim().length === 0) {
|
|
69
|
+
throw new BaziInputError('timezoneId cannot be empty.');
|
|
70
|
+
}
|
|
71
|
+
const trueSolarTimeRequested = ((_a = input.enableTrueSolarTime) !== null && _a !== void 0 ? _a : true)
|
|
72
|
+
&& input.longitude !== undefined
|
|
73
|
+
&& input.hour !== undefined;
|
|
74
|
+
if (trueSolarTimeRequested && input.timezone === undefined && input.timezoneId === undefined) {
|
|
75
|
+
throw new BaziInputError('timezone or timezoneId is required for True Solar Time correction.');
|
|
76
|
+
}
|
|
77
|
+
if (((_b = input.calendarType) !== null && _b !== void 0 ? _b : 'solar') === 'lunar') {
|
|
78
|
+
assertLunarDate(input);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
assertSolarDate(input);
|
|
82
|
+
}
|
|
83
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export * from './constants';
|
|
|
4
4
|
export { detectInteractions } from './core/interactions';
|
|
5
5
|
export { generatePillarsFromSolar, getStemInfo, getMainQi } from './core/pillars';
|
|
6
6
|
export { calculateDaYun } from './core/cycles';
|
|
7
|
+
export { calculateTenGod } from './core/tenGods';
|
|
8
|
+
export { BaziInputError, validateBaziInput } from './core/validation';
|
|
7
9
|
/**
|
|
8
10
|
* calculateBaziChart
|
|
9
11
|
*
|
package/dist/index.js
CHANGED
|
@@ -17,13 +17,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
17
17
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.calculateDaYun = exports.getMainQi = exports.getStemInfo = exports.generatePillarsFromSolar = exports.detectInteractions = void 0;
|
|
20
|
+
exports.validateBaziInput = exports.BaziInputError = exports.calculateTenGod = exports.calculateDaYun = exports.getMainQi = exports.getStemInfo = exports.generatePillarsFromSolar = exports.detectInteractions = void 0;
|
|
21
21
|
exports.calculateBaziChart = calculateBaziChart;
|
|
22
22
|
const true_solar_time_1 = require("@openfate/true-solar-time");
|
|
23
23
|
const lunar_javascript_1 = require("lunar-javascript");
|
|
24
24
|
const pillars_1 = require("./core/pillars");
|
|
25
25
|
const cycles_1 = require("./core/cycles");
|
|
26
26
|
const interactions_1 = require("./core/interactions");
|
|
27
|
+
const validation_1 = require("./core/validation");
|
|
27
28
|
// Re-export all public types and constants
|
|
28
29
|
__exportStar(require("./types"), exports);
|
|
29
30
|
__exportStar(require("./constants"), exports);
|
|
@@ -35,6 +36,41 @@ Object.defineProperty(exports, "getStemInfo", { enumerable: true, get: function
|
|
|
35
36
|
Object.defineProperty(exports, "getMainQi", { enumerable: true, get: function () { return pillars_2.getMainQi; } });
|
|
36
37
|
var cycles_2 = require("./core/cycles");
|
|
37
38
|
Object.defineProperty(exports, "calculateDaYun", { enumerable: true, get: function () { return cycles_2.calculateDaYun; } });
|
|
39
|
+
var tenGods_1 = require("./core/tenGods");
|
|
40
|
+
Object.defineProperty(exports, "calculateTenGod", { enumerable: true, get: function () { return tenGods_1.calculateTenGod; } });
|
|
41
|
+
var validation_2 = require("./core/validation");
|
|
42
|
+
Object.defineProperty(exports, "BaziInputError", { enumerable: true, get: function () { return validation_2.BaziInputError; } });
|
|
43
|
+
Object.defineProperty(exports, "validateBaziInput", { enumerable: true, get: function () { return validation_2.validateBaziInput; } });
|
|
44
|
+
function createDateTime(year, month, day, hour, minute, second) {
|
|
45
|
+
const hasTime = hour !== undefined;
|
|
46
|
+
return {
|
|
47
|
+
year,
|
|
48
|
+
month,
|
|
49
|
+
day,
|
|
50
|
+
hour: hasTime ? hour : null,
|
|
51
|
+
minute: hasTime ? minute !== null && minute !== void 0 ? minute : 0 : null,
|
|
52
|
+
second: hasTime ? second !== null && second !== void 0 ? second : 0 : null,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function createLunarDateTime(solarDateTime) {
|
|
56
|
+
const solar = solarDateTime.hour === null
|
|
57
|
+
? lunar_javascript_1.Solar.fromYmd(solarDateTime.year, solarDateTime.month, solarDateTime.day)
|
|
58
|
+
: lunar_javascript_1.Solar.fromYmdHms(solarDateTime.year, solarDateTime.month, solarDateTime.day, solarDateTime.hour, solarDateTime.minute, solarDateTime.second);
|
|
59
|
+
const lunar = solar.getLunar();
|
|
60
|
+
const lunarMonth = lunar.getMonth();
|
|
61
|
+
return {
|
|
62
|
+
lunar: {
|
|
63
|
+
year: lunar.getYear(),
|
|
64
|
+
month: Math.abs(lunarMonth),
|
|
65
|
+
day: lunar.getDay(),
|
|
66
|
+
hour: solarDateTime.hour,
|
|
67
|
+
minute: solarDateTime.minute,
|
|
68
|
+
second: solarDateTime.second,
|
|
69
|
+
isLeapMonth: lunarMonth < 0,
|
|
70
|
+
},
|
|
71
|
+
zodiac: lunar.getYearShengXiao(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
38
74
|
/**
|
|
39
75
|
* calculateBaziChart
|
|
40
76
|
*
|
|
@@ -56,7 +92,8 @@ Object.defineProperty(exports, "calculateDaYun", { enumerable: true, get: functi
|
|
|
56
92
|
* ```
|
|
57
93
|
*/
|
|
58
94
|
function calculateBaziChart(input) {
|
|
59
|
-
var _a, _b, _c, _d, _e, _f;
|
|
95
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
96
|
+
(0, validation_1.validateBaziInput)(input);
|
|
60
97
|
let solarYear = input.year;
|
|
61
98
|
let solarMonth = input.month;
|
|
62
99
|
let solarDay = input.day;
|
|
@@ -64,6 +101,7 @@ function calculateBaziChart(input) {
|
|
|
64
101
|
let solarMinute = (_a = input.minute) !== null && _a !== void 0 ? _a : 0;
|
|
65
102
|
let solarSecond = 0;
|
|
66
103
|
let solarTimeInfo = null;
|
|
104
|
+
const inputType = (_b = input.calendarType) !== null && _b !== void 0 ? _b : 'solar';
|
|
67
105
|
// ── 1. Lunar → Solar Calendar Conversion ────────────────────────────────
|
|
68
106
|
if (input.calendarType === 'lunar') {
|
|
69
107
|
// lunar-javascript: negative month represents a leap month
|
|
@@ -74,18 +112,31 @@ function calculateBaziChart(input) {
|
|
|
74
112
|
solarMonth = solar.getMonth();
|
|
75
113
|
solarDay = solar.getDay();
|
|
76
114
|
}
|
|
115
|
+
const civilSolar = createDateTime(solarYear, solarMonth, solarDay, input.hour, input.hour === undefined ? undefined : solarMinute, input.hour === undefined ? undefined : solarSecond);
|
|
77
116
|
// ── 2. True Solar Time Correction ───────────────────────────────────────
|
|
78
|
-
const enableTST = ((
|
|
117
|
+
const enableTST = ((_c = input.enableTrueSolarTime) !== null && _c !== void 0 ? _c : true) &&
|
|
79
118
|
input.longitude !== undefined && input.longitude !== null &&
|
|
80
119
|
input.hour !== undefined;
|
|
81
120
|
if (enableTST) {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
121
|
+
const civilTimeInput = input.timezone !== undefined
|
|
122
|
+
? {
|
|
123
|
+
year: solarYear,
|
|
124
|
+
month: solarMonth,
|
|
125
|
+
day: solarDay,
|
|
126
|
+
hour: solarHour,
|
|
127
|
+
minute: solarMinute,
|
|
128
|
+
timeZoneOffset: input.timezone,
|
|
129
|
+
dstOffset: (_d = input.dstOffset) !== null && _d !== void 0 ? _d : 0,
|
|
130
|
+
}
|
|
131
|
+
: {
|
|
132
|
+
year: solarYear,
|
|
133
|
+
month: solarMonth,
|
|
134
|
+
day: solarDay,
|
|
135
|
+
hour: solarHour,
|
|
136
|
+
minute: solarMinute,
|
|
137
|
+
timeZoneId: input.timezoneId,
|
|
138
|
+
};
|
|
139
|
+
const detail = (0, true_solar_time_1.calculateTrueSolarTime)(civilTimeInput, {
|
|
89
140
|
longitude: input.longitude,
|
|
90
141
|
});
|
|
91
142
|
const [dateStr, timeStr] = detail.trueSolarDateTime.split(' ');
|
|
@@ -101,13 +152,32 @@ function calculateBaziChart(input) {
|
|
|
101
152
|
trueSolarTime: detail.trueSolarTime.substring(0, 5),
|
|
102
153
|
trueSolarDateTime: detail.trueSolarDateTime,
|
|
103
154
|
solarDate: dateStr,
|
|
155
|
+
standardMeridian: detail.standardMeridian,
|
|
104
156
|
longitudeCorrectionMinutes: detail.longitudeCorrectionMinutes,
|
|
105
157
|
equationOfTimeMinutes: detail.equationOfTimeMinutes,
|
|
106
158
|
algorithm: detail.algorithm,
|
|
107
159
|
};
|
|
108
160
|
}
|
|
161
|
+
else if (((_e = input.dstOffset) !== null && _e !== void 0 ? _e : 0) !== 0 && input.hour !== undefined) {
|
|
162
|
+
// ── 2b. DST normalization without True Solar Time ────────────────────
|
|
163
|
+
// DST is a civil-clock correction, not a solar-time refinement: a birth
|
|
164
|
+
// recorded at 15:20 during a DST period (e.g. China 1986–1991 summer,
|
|
165
|
+
// Taiwan 1946–1961) actually occurred at 14:20 standard time. The TST
|
|
166
|
+
// path above already subtracts dstOffset inside calculateTrueSolarTime;
|
|
167
|
+
// when TST is disabled it must still be normalized out here, otherwise
|
|
168
|
+
// the pillars use the shifted clock hour — a wrong 时辰, and near
|
|
169
|
+
// midnight a wrong day pillar. Date-based math keeps day/month/year
|
|
170
|
+
// rollover safe (lunar input was already converted to solar above).
|
|
171
|
+
const shifted = new Date(Date.UTC(solarYear, solarMonth - 1, solarDay, solarHour, solarMinute));
|
|
172
|
+
shifted.setUTCMinutes(shifted.getUTCMinutes() - Math.round(((_f = input.dstOffset) !== null && _f !== void 0 ? _f : 0) * 60));
|
|
173
|
+
solarYear = shifted.getUTCFullYear();
|
|
174
|
+
solarMonth = shifted.getUTCMonth() + 1;
|
|
175
|
+
solarDay = shifted.getUTCDate();
|
|
176
|
+
solarHour = shifted.getUTCHours();
|
|
177
|
+
solarMinute = shifted.getUTCMinutes();
|
|
178
|
+
}
|
|
109
179
|
// ── 3. Generate Four Pillars ─────────────────────────────────────────────
|
|
110
|
-
const { pillars, eightChar, dayStem } = (0, pillars_1.generatePillarsFromSolar)(solarYear, solarMonth, solarDay, solarHour, solarMinute, solarSecond, (
|
|
180
|
+
const { pillars, eightChar, dayStem } = (0, pillars_1.generatePillarsFromSolar)(solarYear, solarMonth, solarDay, solarHour, solarMinute, solarSecond, (_g = input.dayBoundaryMode) !== null && _g !== void 0 ? _g : 'MIDNIGHT_00');
|
|
111
181
|
// ── 4. Day Master ────────────────────────────────────────────────────────
|
|
112
182
|
const dayMaster = (0, pillars_1.getStemInfo)(dayStem);
|
|
113
183
|
// ── 5. Da Yun Cycles ─────────────────────────────────────────────────────
|
|
@@ -117,7 +187,29 @@ function calculateBaziChart(input) {
|
|
|
117
187
|
year: pillars.year.branch,
|
|
118
188
|
month: pillars.month.branch,
|
|
119
189
|
day: pillars.day.branch,
|
|
120
|
-
hour: (
|
|
190
|
+
hour: (_j = (_h = pillars.hour) === null || _h === void 0 ? void 0 : _h.branch) !== null && _j !== void 0 ? _j : '',
|
|
121
191
|
});
|
|
122
|
-
|
|
192
|
+
const calculationSolar = createDateTime(solarYear, solarMonth, solarDay, input.hour === undefined ? undefined : solarHour, input.hour === undefined ? undefined : solarMinute, input.hour === undefined ? undefined : solarSecond);
|
|
193
|
+
const lunarCalendar = createLunarDateTime(calculationSolar);
|
|
194
|
+
return {
|
|
195
|
+
pillars,
|
|
196
|
+
dayMaster,
|
|
197
|
+
daYun,
|
|
198
|
+
interactions,
|
|
199
|
+
solarTimeInfo,
|
|
200
|
+
calendar: {
|
|
201
|
+
inputType,
|
|
202
|
+
civilSolar,
|
|
203
|
+
calculationSolar,
|
|
204
|
+
lunar: lunarCalendar.lunar,
|
|
205
|
+
zodiac: lunarCalendar.zodiac,
|
|
206
|
+
},
|
|
207
|
+
metadata: {
|
|
208
|
+
trueSolarTimeApplied: enableTST,
|
|
209
|
+
dayBoundaryMode: (_k = input.dayBoundaryMode) !== null && _k !== void 0 ? _k : 'MIDNIGHT_00',
|
|
210
|
+
longitude: (_l = input.longitude) !== null && _l !== void 0 ? _l : null,
|
|
211
|
+
timezoneBasis: (_o = (_m = input.timezone) !== null && _m !== void 0 ? _m : input.timezoneId) !== null && _o !== void 0 ? _o : null,
|
|
212
|
+
dstOffset: (_p = input.dstOffset) !== null && _p !== void 0 ? _p : 0,
|
|
213
|
+
},
|
|
214
|
+
};
|
|
123
215
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -20,6 +20,22 @@ export interface Pillar {
|
|
|
20
20
|
stem: string;
|
|
21
21
|
branch: string;
|
|
22
22
|
element: FiveElement;
|
|
23
|
+
ganZhi: string;
|
|
24
|
+
stemPolarity: Polarity;
|
|
25
|
+
stemTenGod: string;
|
|
26
|
+
branchElement: FiveElement;
|
|
27
|
+
hiddenStems: HiddenStemInfo[];
|
|
28
|
+
naYin: string;
|
|
29
|
+
xun: string;
|
|
30
|
+
voidBranches: string[];
|
|
31
|
+
growthStage: string;
|
|
32
|
+
}
|
|
33
|
+
export interface HiddenStemInfo {
|
|
34
|
+
stem: string;
|
|
35
|
+
element: FiveElement;
|
|
36
|
+
polarity: Polarity;
|
|
37
|
+
tenGod: string;
|
|
38
|
+
isMain: boolean;
|
|
23
39
|
}
|
|
24
40
|
/** The Four Pillars (四柱) */
|
|
25
41
|
export interface FourPillars {
|
|
@@ -36,6 +52,10 @@ export interface DaYunCycle {
|
|
|
36
52
|
ganZhi: string;
|
|
37
53
|
startYear: number;
|
|
38
54
|
startAge: number;
|
|
55
|
+
endYear: number;
|
|
56
|
+
endAge: number;
|
|
57
|
+
stemTenGod: string;
|
|
58
|
+
branchTenGod: string;
|
|
39
59
|
}
|
|
40
60
|
/** Da Yun metadata */
|
|
41
61
|
export interface DaYunInfo {
|
|
@@ -43,6 +63,13 @@ export interface DaYunInfo {
|
|
|
43
63
|
isForward: boolean;
|
|
44
64
|
startYear: number;
|
|
45
65
|
startAge: number;
|
|
66
|
+
startDate: string;
|
|
67
|
+
startOffset: {
|
|
68
|
+
years: number;
|
|
69
|
+
months: number;
|
|
70
|
+
days: number;
|
|
71
|
+
hours: number;
|
|
72
|
+
};
|
|
46
73
|
}
|
|
47
74
|
export type InteractionType = 'CLASH' | 'COMBINATION_2' | 'TRINE' | 'DIRECTIONAL' | 'PUNISHMENT' | 'DESTRUCTION' | 'HARM';
|
|
48
75
|
/** A detected branch interaction */
|
|
@@ -57,10 +84,36 @@ export interface SolarTimeInfo {
|
|
|
57
84
|
trueSolarTime: string;
|
|
58
85
|
trueSolarDateTime: string;
|
|
59
86
|
solarDate: string;
|
|
87
|
+
standardMeridian: number;
|
|
60
88
|
longitudeCorrectionMinutes: number;
|
|
61
89
|
equationOfTimeMinutes: number;
|
|
62
90
|
algorithm: string;
|
|
63
91
|
}
|
|
92
|
+
export interface CalendarDateTime {
|
|
93
|
+
year: number;
|
|
94
|
+
month: number;
|
|
95
|
+
day: number;
|
|
96
|
+
hour: number | null;
|
|
97
|
+
minute: number | null;
|
|
98
|
+
second: number | null;
|
|
99
|
+
}
|
|
100
|
+
export interface LunarDateTime extends CalendarDateTime {
|
|
101
|
+
isLeapMonth: boolean;
|
|
102
|
+
}
|
|
103
|
+
export interface CalendarInfo {
|
|
104
|
+
inputType: 'solar' | 'lunar';
|
|
105
|
+
civilSolar: CalendarDateTime;
|
|
106
|
+
calculationSolar: CalendarDateTime;
|
|
107
|
+
lunar: LunarDateTime;
|
|
108
|
+
zodiac: string;
|
|
109
|
+
}
|
|
110
|
+
export interface CalculationMetadata {
|
|
111
|
+
trueSolarTimeApplied: boolean;
|
|
112
|
+
dayBoundaryMode: DayBoundaryMode;
|
|
113
|
+
longitude: number | null;
|
|
114
|
+
timezoneBasis: number | string | null;
|
|
115
|
+
dstOffset: number;
|
|
116
|
+
}
|
|
64
117
|
/** Full output of calculateBaziChart() */
|
|
65
118
|
export interface BaziChart {
|
|
66
119
|
pillars: FourPillars;
|
|
@@ -68,6 +121,8 @@ export interface BaziChart {
|
|
|
68
121
|
daYun: DaYunInfo;
|
|
69
122
|
interactions: BranchInteraction[];
|
|
70
123
|
solarTimeInfo: SolarTimeInfo | null;
|
|
124
|
+
calendar: CalendarInfo;
|
|
125
|
+
metadata: CalculationMetadata;
|
|
71
126
|
}
|
|
72
127
|
export interface BaziInput {
|
|
73
128
|
year: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfate/bazi-engine",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "Accurate Four Pillars (八字) chart engine with True Solar Time correction, Da Yun cycles, and interaction detection. Extracted from the OpenFate.ai core engine.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
],
|
|
12
12
|
"scripts": {
|
|
13
13
|
"build": "tsc",
|
|
14
|
-
"test": "tsx --test tests/*.test.ts",
|
|
14
|
+
"test": "node --import tsx --test tests/*.test.ts",
|
|
15
15
|
"release": "npm version patch && git push --follow-tags",
|
|
16
16
|
"prepublishOnly": "npm run build && npm run test"
|
|
17
17
|
},
|