amlich-api 0.1.0 → 0.2.0

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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # amlich-api
2
2
 
3
- TypeScript/JavaScript SDK để gọi AmLich API.
3
+ TypeScript SDK cho AmLich API.
4
4
 
5
5
  - Base URL mặc định: `https://amlich.giaphahobe.vn`
6
6
  - API prefix mặc định: `/api/v1`
7
- - Tương thích Node.js >= 18 (dùng `fetch` built-in) và browser
7
+ - Chạy trên Node.js >= 18 (fetch built-in) và browser
8
8
 
9
9
  ## Cài đặt
10
10
 
@@ -12,61 +12,119 @@ TypeScript/JavaScript SDK để gọi AmLich API.
12
12
  npm i amlich-api
13
13
  ```
14
14
 
15
- ## Sử dụng nhanh
15
+ ## Khởi tạo client
16
16
 
17
17
  ```ts
18
18
  import { AmLichClient } from "amlich-api";
19
19
 
20
- const api = new AmLichClient();
21
-
22
- const today = await api.today(7);
23
- console.log(today);
20
+ const api = new AmLichClient({
21
+ baseUrl: "https://amlich.giaphahobe.vn",
22
+ apiPrefix: "/api/v1",
23
+ timeoutMs: 15000,
24
+ });
24
25
  ```
25
26
 
26
- ## API
27
+ Constructor options:
27
28
 
28
- ### `new AmLichClient(options?)`
29
+ - `baseUrl?: string`
30
+ - `apiPrefix?: string`
31
+ - `headers?: Record<string, string>`
32
+ - `fetchImpl?: typeof fetch`
33
+ - `timeoutMs?: number`
29
34
 
30
- Options:
35
+ ## Methods
31
36
 
32
- - `baseUrl?: string` (default: `https://amlich.giaphahobe.vn`)
33
- - `apiPrefix?: string` (default: `/api/v1`)
34
- - `headers?: Record<string, string>`
35
- - `fetchImpl?: typeof fetch` (truyền custom fetch nếu môi trường không có sẵn)
36
- - `timeoutMs?: number` (default: `15000`)
37
+ ### Calendar core
37
38
 
38
- ### `today(tz?: number): Promise<TodayResponse>`
39
+ ```ts
40
+ await api.today(7);
41
+ await api.day("2026-02-21", 7, 22);
42
+ await api.month(2026, 2, 7);
43
+ ```
39
44
 
40
- Gọi `GET /today`.
45
+ - `today(tz?: number)` -> `GET /today`
46
+ - `day(date: string, tz?: number, hour?: number)` -> `GET /day`
47
+ - `month(year: number, month: number, tz?: number)` -> `GET /month`
41
48
 
42
- ### `day(date: string, tz?: number, hour?: number): Promise<DayResponse>`
49
+ ### Tra cứu can chi / tương hợp
43
50
 
44
- Gọi `GET /day?date=YYYY-MM-DD&tz=7&hour=23`.
51
+ ```ts
52
+ await api.napam("Giáp Tý");
53
+ await api.chiRelations("Tý");
54
+ await api.compat("2026-02-21", 7, 1987);
55
+ ```
45
56
 
46
- Nếu server hiện tại chưa expose endpoint này, SDK sẽ throw `AmLichError` với `status = 404`.
57
+ - `napam(name: string)` -> `GET /napam`
58
+ - `chiRelations(chi: string)` -> `GET /chi/relations`
59
+ - `compat(date: string, tz?: number, userYear?: number)` -> `GET /compat`
47
60
 
48
- ### `month(year: number, month: number, tz?: number): Promise<MonthResponse>`
61
+ ### Tiết khí / ngày kỵ / sao / thần sát
49
62
 
50
- Gọi `GET /month?year=2026&month=2&tz=7`.
63
+ ```ts
64
+ await api.tietKhi("2026-02-21", 7);
65
+ await api.tietKhiYear(2026, 7);
66
+ await api.ngayKy("2026-02-21", 7);
67
+ await api.saoTu("2026-02-21", 7);
68
+ await api.thanSat("2026-02-21", 7);
69
+ ```
51
70
 
52
- Nếu server hiện tại chưa expose endpoint này, SDK sẽ throw `AmLichError` với `status = 404`.
71
+ - `tietKhi(date: string, tz?: number)` -> `GET /tietkhi`
72
+ - `tietKhiYear(year: number, tz?: number)` -> `GET /tietkhi/year`
73
+ - `ngayKy(date: string, tz?: number)` -> `GET /ngayky`
74
+ - `saoTu(date: string, tz?: number)` -> `GET /saotu`
75
+ - `thanSat(date: string, tz?: number)` -> `GET /thansat`
53
76
 
54
- ## Override baseUrl
77
+ ### Warnings / Pick
55
78
 
56
79
  ```ts
57
- import { AmLichClient } from "amlich-api";
80
+ await api.warnings({
81
+ date: "2026-02-21",
82
+ tz: 7,
83
+ userYear: 1987,
84
+ purpose: "khaisuong",
85
+ ruleset: "vn-basic",
86
+ });
58
87
 
59
- const api = new AmLichClient({
60
- baseUrl: "https://example.com",
61
- apiPrefix: "/api/v1",
62
- headers: {
63
- "x-api-key": "your-key",
64
- },
65
- timeoutMs: 10000,
88
+ await api.pick({
89
+ from: "2026-02-20",
90
+ to: "2026-03-15",
91
+ tz: 7,
92
+ purpose: "khaisuong",
93
+ userYear: 1987,
94
+ limit: 5,
95
+ maxDays: 92,
96
+ ruleset: "vn-basic",
66
97
  });
67
98
  ```
68
99
 
69
- ## Xử lỗi `AmLichError`
100
+ - `warnings(query)` -> `GET /warnings`
101
+ - `pick(query)` -> `GET /pick`
102
+
103
+ ### Convert (POST)
104
+
105
+ ```ts
106
+ await api.solarToLunar({
107
+ year: 2026,
108
+ month: 2,
109
+ day: 21,
110
+ tz: 7,
111
+ });
112
+
113
+ await api.lunarToSolar({
114
+ year: 2026,
115
+ month: 1,
116
+ day: 14,
117
+ isLeap: false,
118
+ tz: 7,
119
+ });
120
+ ```
121
+
122
+ - `solarToLunar(payload)` -> `POST /convert/solar-to-lunar`
123
+ - `lunarToSolar(payload)` -> `POST /convert/lunar-to-solar`
124
+
125
+ ## Error handling
126
+
127
+ SDK throw `AmLichError` khi timeout/network error hoặc HTTP status không thành công.
70
128
 
71
129
  ```ts
72
130
  import { AmLichClient, AmLichError } from "amlich-api";
@@ -74,19 +132,20 @@ import { AmLichClient, AmLichError } from "amlich-api";
74
132
  const api = new AmLichClient();
75
133
 
76
134
  try {
77
- const data = await api.today(7);
78
- console.log(data);
135
+ await api.today(7);
79
136
  } catch (error) {
80
137
  if (error instanceof AmLichError) {
81
- console.error("Status:", error.status);
82
- console.error("Message:", error.message);
83
- console.error("Data:", error.data);
84
- } else {
85
- console.error("Unknown error:", error);
138
+ console.error(error.status);
139
+ console.error(error.message);
140
+ console.error(error.data);
86
141
  }
87
142
  }
88
143
  ```
89
144
 
145
+ ## Type notes
146
+
147
+ Package dùng kiểu `loose but structured`: có field chính + `[k: string]: unknown` để thích ứng khi API thay đổi schema.
148
+
90
149
  ## Development
91
150
 
92
151
  ```bash
@@ -95,47 +154,6 @@ npm run dev
95
154
  npm run build
96
155
  ```
97
156
 
98
- ## Publish npm
99
-
100
- 1. Đăng nhập npm:
101
-
102
- ```bash
103
- npm login
104
- ```
105
-
106
- 2. Kiểm tra tên package có trống không:
107
-
108
- ```bash
109
- npm view amlich-api version
110
- ```
111
-
112
- - Nếu command báo lỗi `404 Not Found`, tên `amlich-api` chưa bị chiếm.
113
- - Nếu có version trả về, tên đã bị chiếm.
114
-
115
- 3. Publish package:
116
-
117
- ```bash
118
- npm publish --access public
119
- ```
120
-
121
- ### Fallback khi name bị chiếm
122
-
123
- Đổi sang scoped package, ví dụ `@your-scope/amlich-api`:
124
-
125
- 1. Cập nhật trường `name` trong `package.json`:
126
-
127
- ```json
128
- {
129
- "name": "@your-scope/amlich-api"
130
- }
131
- ```
132
-
133
- 2. Publish lại:
134
-
135
- ```bash
136
- npm publish --access public
137
- ```
138
-
139
157
  ## License
140
158
 
141
159
  MIT
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  type UnknownFields = {
2
2
  [key: string]: unknown;
3
3
  };
4
+ type Purpose = "khaisuong" | "cuoihoi" | "dongtho" | "nhaptrach" | "xuat_hanh" | (string & {});
4
5
  type TodayResponse = {
5
6
  date: string;
6
7
  } & UnknownFields;
@@ -14,6 +15,99 @@ type MonthResponse = {
14
15
  month: number;
15
16
  days: unknown[];
16
17
  } & UnknownFields;
18
+ type NapAmResponse = {
19
+ name?: string;
20
+ value?: string;
21
+ } & UnknownFields;
22
+ type ChiRelationsResponse = {
23
+ chi?: string;
24
+ tamHop?: string[];
25
+ lucXungWith?: string;
26
+ } & UnknownFields;
27
+ type CompatResponse = {
28
+ user?: unknown;
29
+ day?: unknown;
30
+ relations?: unknown;
31
+ verdict?: "good" | "neutral" | "bad" | string;
32
+ } & UnknownFields;
33
+ type TietKhiResponse = {
34
+ index?: number;
35
+ name?: string;
36
+ startAt?: string;
37
+ endAt?: string;
38
+ next?: unknown;
39
+ secondsToNext?: number;
40
+ } & UnknownFields;
41
+ type TietKhiYearResponse = {
42
+ year?: number;
43
+ terms?: unknown[];
44
+ } & UnknownFields;
45
+ type NgayKyResponse = {
46
+ date?: string;
47
+ items?: unknown[];
48
+ } & UnknownFields;
49
+ type SaoTuResponse = {
50
+ jd?: number;
51
+ sao28Tu?: unknown;
52
+ } & UnknownFields;
53
+ type ThanSatResponse = {
54
+ date?: string;
55
+ items?: unknown[];
56
+ } & UnknownFields;
57
+ type WarningItem = {
58
+ type?: string;
59
+ code?: string;
60
+ name?: string;
61
+ level?: "good" | "warn" | "avoid" | "bad" | string;
62
+ reason?: string;
63
+ } & UnknownFields;
64
+ type WarningsResponse = {
65
+ input?: unknown;
66
+ level?: "good" | "warn" | "bad" | string;
67
+ summary?: string[];
68
+ items?: WarningItem[];
69
+ } & UnknownFields;
70
+ type PickResultItem = {
71
+ date?: string;
72
+ weekdayName?: string;
73
+ score?: number;
74
+ verdict?: "good" | "ok" | "avoid" | string;
75
+ reasons?: string[];
76
+ highlights?: unknown;
77
+ } & UnknownFields;
78
+ type PickResponse = {
79
+ input?: unknown;
80
+ results?: PickResultItem[];
81
+ meta?: unknown;
82
+ } & UnknownFields;
83
+ type ConvertRequest = {
84
+ year: number;
85
+ month: number;
86
+ day: number;
87
+ tz?: number;
88
+ isLeap?: boolean;
89
+ } & UnknownFields;
90
+ type ConvertResponse = {
91
+ input?: unknown;
92
+ output?: unknown;
93
+ } & UnknownFields;
94
+ type WarningsQuery = {
95
+ date: string;
96
+ tz?: number;
97
+ userYear?: number;
98
+ purpose?: Purpose;
99
+ ruleset?: string;
100
+ };
101
+ type PickQuery = {
102
+ from: string;
103
+ to: string;
104
+ purpose: Purpose;
105
+ tz?: number;
106
+ userYear?: number;
107
+ limit?: number;
108
+ maxDays?: number;
109
+ ruleset?: string;
110
+ };
17
111
  type AmLichClientOptions = {
18
112
  baseUrl?: string;
19
113
  apiPrefix?: string;
@@ -40,9 +134,23 @@ declare class AmLichClient {
40
134
  today(tz?: number): Promise<TodayResponse>;
41
135
  day(date: string, tz?: number, hour?: number): Promise<DayResponse>;
42
136
  month(year: number, month: number, tz?: number): Promise<MonthResponse>;
137
+ napam(name: string): Promise<NapAmResponse>;
138
+ chiRelations(chi: string): Promise<ChiRelationsResponse>;
139
+ compat(date: string, tz?: number, userYear?: number): Promise<CompatResponse>;
140
+ tietKhi(date: string, tz?: number): Promise<TietKhiResponse>;
141
+ tietKhiYear(year: number, tz?: number): Promise<TietKhiYearResponse>;
142
+ ngayKy(date: string, tz?: number): Promise<NgayKyResponse>;
143
+ saoTu(date: string, tz?: number): Promise<SaoTuResponse>;
144
+ thanSat(date: string, tz?: number): Promise<ThanSatResponse>;
145
+ warnings(query: WarningsQuery): Promise<WarningsResponse>;
146
+ pick(query: PickQuery): Promise<PickResponse>;
147
+ solarToLunar(payload: ConvertRequest): Promise<ConvertResponse>;
148
+ lunarToSolar(payload: ConvertRequest): Promise<ConvertResponse>;
149
+ private get;
150
+ private post;
43
151
  private buildUrl;
44
152
  private parseResponseBody;
45
153
  private request;
46
154
  }
47
155
 
48
- export { AmLichClient, type AmLichClientOptions, AmLichError, type DayResponse, type MonthResponse, type TodayResponse };
156
+ export { AmLichClient, type AmLichClientOptions, AmLichError, type ChiRelationsResponse, type CompatResponse, type ConvertRequest, type ConvertResponse, type DayResponse, type MonthResponse, type NapAmResponse, type NgayKyResponse, type PickQuery, type PickResponse, type SaoTuResponse, type ThanSatResponse, type TietKhiResponse, type TietKhiYearResponse, type TodayResponse, type WarningsQuery, type WarningsResponse };
package/dist/index.js CHANGED
@@ -38,24 +38,95 @@ var AmLichClient = class {
38
38
  this.timeoutMs = timeoutMs;
39
39
  }
40
40
  today(tz) {
41
- return this.request("/today", {
41
+ return this.get("/today", {
42
42
  tz
43
43
  });
44
44
  }
45
45
  day(date, tz, hour) {
46
- return this.request("/day", {
46
+ return this.get("/day", {
47
47
  date,
48
48
  tz,
49
49
  hour
50
50
  });
51
51
  }
52
52
  month(year, month, tz) {
53
- return this.request("/month", {
53
+ return this.get("/month", {
54
54
  year,
55
55
  month,
56
56
  tz
57
57
  });
58
58
  }
59
+ napam(name) {
60
+ return this.get("/napam", { name });
61
+ }
62
+ chiRelations(chi) {
63
+ return this.get("/chi/relations", { chi });
64
+ }
65
+ compat(date, tz, userYear) {
66
+ return this.get("/compat", {
67
+ date,
68
+ tz,
69
+ userYear
70
+ });
71
+ }
72
+ tietKhi(date, tz) {
73
+ return this.get("/tietkhi", {
74
+ date,
75
+ tz
76
+ });
77
+ }
78
+ tietKhiYear(year, tz) {
79
+ return this.get("/tietkhi/year", {
80
+ year,
81
+ tz
82
+ });
83
+ }
84
+ ngayKy(date, tz) {
85
+ return this.get("/ngayky", {
86
+ date,
87
+ tz
88
+ });
89
+ }
90
+ saoTu(date, tz) {
91
+ return this.get("/saotu", {
92
+ date,
93
+ tz
94
+ });
95
+ }
96
+ thanSat(date, tz) {
97
+ return this.get("/thansat", {
98
+ date,
99
+ tz
100
+ });
101
+ }
102
+ warnings(query) {
103
+ return this.get("/warnings", query);
104
+ }
105
+ pick(query) {
106
+ return this.get("/pick", query);
107
+ }
108
+ solarToLunar(payload) {
109
+ return this.post(
110
+ "/convert/solar-to-lunar",
111
+ payload
112
+ );
113
+ }
114
+ lunarToSolar(payload) {
115
+ return this.post(
116
+ "/convert/lunar-to-solar",
117
+ payload
118
+ );
119
+ }
120
+ get(path, query) {
121
+ return this.request("GET", path, {
122
+ query
123
+ });
124
+ }
125
+ post(path, body) {
126
+ return this.request("POST", path, {
127
+ body
128
+ });
129
+ }
59
130
  buildUrl(path, query) {
60
131
  const normalizedPrefix = this.apiPrefix.startsWith("/") ? this.apiPrefix : `/${this.apiPrefix}`;
61
132
  const normalizedPath = path.startsWith("/") ? path : `/${path}`;
@@ -77,21 +148,30 @@ var AmLichClient = class {
77
148
  return text;
78
149
  }
79
150
  }
80
- async request(path, query) {
81
- const url = this.buildUrl(path, query);
151
+ async request(method, path, options) {
152
+ const url = this.buildUrl(path, options?.query);
82
153
  const controller = new AbortController();
83
154
  const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
155
+ const headers = {
156
+ ...this.headers
157
+ };
158
+ let bodyText;
159
+ if (method === "POST") {
160
+ headers["content-type"] = headers["content-type"] ?? "application/json";
161
+ bodyText = JSON.stringify(options?.body ?? {});
162
+ }
84
163
  let response;
85
164
  try {
86
165
  response = await this.fetchImpl(url, {
87
- method: "GET",
88
- headers: this.headers,
89
- signal: controller.signal
166
+ method,
167
+ headers,
168
+ signal: controller.signal,
169
+ body: bodyText
90
170
  });
91
171
  } catch (error) {
92
172
  if (error instanceof DOMException && error.name === "AbortError") {
93
173
  throw new AmLichError(
94
- `Request timed out after ${this.timeoutMs}ms while calling ${url.pathname}`
174
+ `Request timed out after ${this.timeoutMs}ms while calling ${method} ${url.pathname}`
95
175
  );
96
176
  }
97
177
  throw new AmLichError(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amlich-api",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "TypeScript client SDK for AmLich API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -41,4 +41,4 @@
41
41
  "tsup": "^8.4.0",
42
42
  "typescript": "^5.8.2"
43
43
  }
44
- }
44
+ }