@sazuapp/client 0.1.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 +155 -0
- package/dist/client.d.ts +55 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +69 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +35 -0
- package/dist/http.d.ts +31 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +157 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/types.d.ts +110 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/package.json +53 -0
package/README.md
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# @sazuapp/client
|
|
2
|
+
|
|
3
|
+
[SAZU API](https://www.sazu.app/manse-api) 의 공식 **TypeScript SDK**.
|
|
4
|
+
한 줄로 사주·만세력·합형충파해·격국·용신 등 **14개 명리 모듈** 을 분석합니다.
|
|
5
|
+
|
|
6
|
+
> sazu.app 에서 제공하는 manse-api 이용자들을 위한 공식 TypeScript SDK 입니다.
|
|
7
|
+
|
|
8
|
+
<br/><br/><br/>
|
|
9
|
+
|
|
10
|
+
## 왜 SAZU SDK 인가
|
|
11
|
+
|
|
12
|
+
**사주·만세력·운세 관련 서비스를 만들고 있다면, 직접 명리 엔진을 구현하지 마세요.**
|
|
13
|
+
|
|
14
|
+
- 🚀 **3줄 연동** — `new SazuClient({ apiKey })` → `sazu.calculate({...})` → 끝.
|
|
15
|
+
- 🎯 **14개 명리 모듈** — 사주팔자·합형충파해·격국·용신·12운성·신살·허자·대운 한 번에.
|
|
16
|
+
- 📅 **200년 범위 만세력** — 1900~2100 음/양력 변환, 절기 기반 월주 판별, 진태양시 보정.
|
|
17
|
+
- ⚡ **50ms 이하 응답** — 챗봇·앱·웹사이트에 즉시 통합. 사용자 경험 손실 0.
|
|
18
|
+
- 🛡 **자동 재시도** — 네트워크·5xx 에러에 exponential backoff. 429 에 Retry-After 자동 준수.
|
|
19
|
+
- 🔒 **서버 사이드 전용 가드** — 브라우저 환경 감지 시 자동 경고 (API 키 유출 방지).
|
|
20
|
+
- 📦 **의존성 0** — native fetch 만 사용. 번들 크기 최소.
|
|
21
|
+
- ✨ **완벽한 TypeScript 타입** — IDE 자동완성 + AI 코드 생성 친화.
|
|
22
|
+
|
|
23
|
+
<br/><br/><br/>
|
|
24
|
+
|
|
25
|
+
## 설치
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @sazuapp/client
|
|
29
|
+
# 또는
|
|
30
|
+
pnpm add @sazuapp/client
|
|
31
|
+
yarn add @sazuapp/client
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
<br/><br/><br/>
|
|
35
|
+
|
|
36
|
+
## 시작하기
|
|
37
|
+
|
|
38
|
+
1. [https://www.sazu.app/manse-api](https://www.sazu.app/manse-api) — 서비스 소개·가격 확인
|
|
39
|
+
2. [Pro 구독](https://www.sazu.app/manse-api/pricing) (선택, Free 도 사용 가능)
|
|
40
|
+
3. [대시보드 → 키 발급](https://www.sazu.app/manse-api/dashboard/keys) — `SAZU_API_KEY` 환경변수에 설정
|
|
41
|
+
|
|
42
|
+
<br/><br/><br/>
|
|
43
|
+
|
|
44
|
+
## 기본 사용
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { SazuClient } from '@sazuapp/client'
|
|
48
|
+
|
|
49
|
+
const sazu = new SazuClient({ apiKey: process.env.SAZU_API_KEY! })
|
|
50
|
+
|
|
51
|
+
// 현재 키의 tier·한도 확인
|
|
52
|
+
const me = await sazu.me()
|
|
53
|
+
console.log(me.tier) // 'free' | 'pro' | 'event'
|
|
54
|
+
|
|
55
|
+
// 사주 분석
|
|
56
|
+
const result = await sazu.calculate({
|
|
57
|
+
birthYear: 1979,
|
|
58
|
+
birthMonth: 2,
|
|
59
|
+
birthDay: 8,
|
|
60
|
+
birthHour: 18,
|
|
61
|
+
isFemale: false,
|
|
62
|
+
isLunar: false,
|
|
63
|
+
birthCity: '서울',
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
console.log(result.fourPillars) // 연·월·일·시주
|
|
67
|
+
console.log(result.elements) // 오행 분포
|
|
68
|
+
console.log(result.relationships) // 합형충파해 (Pro)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
<br/><br/><br/>
|
|
72
|
+
|
|
73
|
+
## 음/양력 변환
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const solar = await sazu.calendar.convert({
|
|
77
|
+
year: 1990,
|
|
78
|
+
month: 4,
|
|
79
|
+
day: 8,
|
|
80
|
+
direction: 'toSolar', // 'toSolar' | 'toLunar'
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
console.log(solar) // { year, month, day, isLeapMonth }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
<br/><br/><br/>
|
|
87
|
+
|
|
88
|
+
## 에러 처리
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { SazuClient, SazuApiError } from '@sazuapp/client'
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const result = await sazu.calculate({ ... })
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err instanceof SazuApiError) {
|
|
97
|
+
console.error(err.code, err.message, err.responseId)
|
|
98
|
+
if (err.isAuthError) { /* 키 재발급 안내 */ }
|
|
99
|
+
if (err.isRateLimited) { /* err.retryAfterSec 후 재시도 */ }
|
|
100
|
+
if (err.isTransient) { /* 일시 장애, SDK 가 이미 재시도 */ }
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
<br/><br/><br/>
|
|
106
|
+
|
|
107
|
+
## Next.js (App Router) 통합
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
// app/api/saju/route.ts
|
|
111
|
+
import { NextResponse } from 'next/server'
|
|
112
|
+
import { SazuClient } from '@sazuapp/client'
|
|
113
|
+
|
|
114
|
+
const sazu = new SazuClient({ apiKey: process.env.SAZU_API_KEY! })
|
|
115
|
+
|
|
116
|
+
export async function POST(req: Request) {
|
|
117
|
+
const body = await req.json()
|
|
118
|
+
const result = await sazu.calculate(body)
|
|
119
|
+
return NextResponse.json({ ok: true, data: result })
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
<br/><br/><br/>
|
|
124
|
+
|
|
125
|
+
## ⚠ 서버 사이드 전용
|
|
126
|
+
|
|
127
|
+
본 SDK 는 **서버 사이드에서만 사용**하세요. 브라우저 환경에서 import 하면 자동 경고가 출력되며, **API 키가 클라이언트 번들에 포함될 위험**이 있습니다.
|
|
128
|
+
|
|
129
|
+
- ✅ Next.js: `app/api/*` 또는 Server Actions
|
|
130
|
+
- ✅ Vite/SvelteKit: `+server.ts` / endpoint
|
|
131
|
+
- ✅ Express·Fastify·Hono·Nest 백엔드
|
|
132
|
+
- ❌ React 컴포넌트 안에서 직접 import 금지
|
|
133
|
+
- ❌ `'use client'` 컴포넌트에서 사용 금지
|
|
134
|
+
|
|
135
|
+
<br/><br/><br/>
|
|
136
|
+
|
|
137
|
+
## 옵션
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
const sazu = new SazuClient({
|
|
141
|
+
apiKey: process.env.SAZU_API_KEY!,
|
|
142
|
+
baseUrl: 'https://api.sazu.app', // 기본값
|
|
143
|
+
maxRetries: 2, // 기본 2 (5xx·네트워크 에러)
|
|
144
|
+
timeoutMs: 30000, // 기본 30s
|
|
145
|
+
headers: { // 추가 헤더 (선택)
|
|
146
|
+
'X-Trace-Id': '...',
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
<br/><br/><br/>
|
|
152
|
+
|
|
153
|
+
## 문의
|
|
154
|
+
|
|
155
|
+
contact@sazu.app
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SazuClient — SAZU API 의 TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* 사용 예 (서버 사이드):
|
|
5
|
+
*
|
|
6
|
+
* import { SazuClient } from '@sazuapp/client'
|
|
7
|
+
*
|
|
8
|
+
* const sazu = new SazuClient({ apiKey: process.env.SAZU_API_KEY! })
|
|
9
|
+
*
|
|
10
|
+
* const me = await sazu.me()
|
|
11
|
+
* console.log(me.tier) // 'free' | 'pro' | 'event'
|
|
12
|
+
*
|
|
13
|
+
* const result = await sazu.calculate({
|
|
14
|
+
* birthYear: 1979, birthMonth: 2, birthDay: 8, birthHour: 18,
|
|
15
|
+
* isFemale: false, isLunar: false, birthCity: '서울',
|
|
16
|
+
* })
|
|
17
|
+
* console.log(result.fourPillars)
|
|
18
|
+
*
|
|
19
|
+
* const calendar = await sazu.calendar.convert({
|
|
20
|
+
* year: 1990, month: 4, day: 8, direction: 'toSolar',
|
|
21
|
+
* })
|
|
22
|
+
*/
|
|
23
|
+
import { HttpClient, type ClientOptions } from './http.js';
|
|
24
|
+
import type { CalculateInput, CalculateResult, CalendarConvertInput, CalendarConvertResult, MeData } from './types.js';
|
|
25
|
+
export declare class SazuClient {
|
|
26
|
+
private http;
|
|
27
|
+
/** 음·양력 변환 도구. */
|
|
28
|
+
readonly calendar: CalendarApi;
|
|
29
|
+
constructor(opts: ClientOptions);
|
|
30
|
+
/**
|
|
31
|
+
* 현재 API 키의 tier·한도 조회.
|
|
32
|
+
*
|
|
33
|
+
* 용도: Pro 자격 사전 검증, 한도 표시 UI.
|
|
34
|
+
*/
|
|
35
|
+
me(): Promise<MeData>;
|
|
36
|
+
/**
|
|
37
|
+
* 사주 계산 + 14개 모듈 분석.
|
|
38
|
+
*
|
|
39
|
+
* Free 플랜: 기본 5개 모듈 (fourPillars, decadeFortune, elements, summary, sinStrength)
|
|
40
|
+
* Pro 플랜: 14개 모듈 전체 (격국·용신·12운성·신살·허자·합형충파해 등)
|
|
41
|
+
*/
|
|
42
|
+
calculate(input: CalculateInput): Promise<CalculateResult>;
|
|
43
|
+
}
|
|
44
|
+
declare class CalendarApi {
|
|
45
|
+
private http;
|
|
46
|
+
constructor(http: HttpClient);
|
|
47
|
+
/**
|
|
48
|
+
* 양력 ↔ 음력 변환.
|
|
49
|
+
*
|
|
50
|
+
* 200년 범위 만세력 기반, 윤달 처리 포함.
|
|
51
|
+
*/
|
|
52
|
+
convert(input: CalendarConvertInput): Promise<CalendarConvertResult>;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAA;AAC1D,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,MAAM,EACP,MAAM,YAAY,CAAA;AAEnB,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAY;IAExB,kBAAkB;IAClB,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAA;gBAElB,IAAI,EAAE,aAAa;IAK/B;;;;OAIG;IACG,EAAE,IAAI,OAAO,CAAC,MAAM,CAAC;IAI3B;;;;;OAKG;IACG,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAMjE;AAED,cAAM,WAAW;IACH,OAAO,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAEpC;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAM3E"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SazuClient — SAZU API 의 TypeScript SDK.
|
|
3
|
+
*
|
|
4
|
+
* 사용 예 (서버 사이드):
|
|
5
|
+
*
|
|
6
|
+
* import { SazuClient } from '@sazuapp/client'
|
|
7
|
+
*
|
|
8
|
+
* const sazu = new SazuClient({ apiKey: process.env.SAZU_API_KEY! })
|
|
9
|
+
*
|
|
10
|
+
* const me = await sazu.me()
|
|
11
|
+
* console.log(me.tier) // 'free' | 'pro' | 'event'
|
|
12
|
+
*
|
|
13
|
+
* const result = await sazu.calculate({
|
|
14
|
+
* birthYear: 1979, birthMonth: 2, birthDay: 8, birthHour: 18,
|
|
15
|
+
* isFemale: false, isLunar: false, birthCity: '서울',
|
|
16
|
+
* })
|
|
17
|
+
* console.log(result.fourPillars)
|
|
18
|
+
*
|
|
19
|
+
* const calendar = await sazu.calendar.convert({
|
|
20
|
+
* year: 1990, month: 4, day: 8, direction: 'toSolar',
|
|
21
|
+
* })
|
|
22
|
+
*/
|
|
23
|
+
import { HttpClient } from './http.js';
|
|
24
|
+
export class SazuClient {
|
|
25
|
+
http;
|
|
26
|
+
/** 음·양력 변환 도구. */
|
|
27
|
+
calendar;
|
|
28
|
+
constructor(opts) {
|
|
29
|
+
this.http = new HttpClient(opts);
|
|
30
|
+
this.calendar = new CalendarApi(this.http);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 현재 API 키의 tier·한도 조회.
|
|
34
|
+
*
|
|
35
|
+
* 용도: Pro 자격 사전 검증, 한도 표시 UI.
|
|
36
|
+
*/
|
|
37
|
+
async me() {
|
|
38
|
+
return this.http.request('/v1/me');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 사주 계산 + 14개 모듈 분석.
|
|
42
|
+
*
|
|
43
|
+
* Free 플랜: 기본 5개 모듈 (fourPillars, decadeFortune, elements, summary, sinStrength)
|
|
44
|
+
* Pro 플랜: 14개 모듈 전체 (격국·용신·12운성·신살·허자·합형충파해 등)
|
|
45
|
+
*/
|
|
46
|
+
async calculate(input) {
|
|
47
|
+
return this.http.request('/v1/sazu/calculate', {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
body: JSON.stringify(input),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
class CalendarApi {
|
|
54
|
+
http;
|
|
55
|
+
constructor(http) {
|
|
56
|
+
this.http = http;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 양력 ↔ 음력 변환.
|
|
60
|
+
*
|
|
61
|
+
* 200년 범위 만세력 기반, 윤달 처리 포함.
|
|
62
|
+
*/
|
|
63
|
+
async convert(input) {
|
|
64
|
+
return this.http.request('/v1/calendar/convert', {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
body: JSON.stringify(input),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API 에러 클래스.
|
|
3
|
+
*
|
|
4
|
+
* 모든 API 호출이 실패하면 SazuApiError 를 throw 한다.
|
|
5
|
+
* - status: HTTP 상태 코드 (네트워크 실패 시 0)
|
|
6
|
+
* - code: 서버 측 에러 코드 (`INVALID_API_KEY`, `KEY_EXPIRED` 등)
|
|
7
|
+
* - message: 사람이 읽을 수 있는 메시지
|
|
8
|
+
* - responseId: 응답 fingerprint (forensics·고객 지원 시 사용)
|
|
9
|
+
*/
|
|
10
|
+
export declare class SazuApiError extends Error {
|
|
11
|
+
readonly status: number;
|
|
12
|
+
readonly code: string | undefined;
|
|
13
|
+
readonly responseId: string | undefined;
|
|
14
|
+
readonly retryAfterSec: number | undefined;
|
|
15
|
+
constructor(opts: {
|
|
16
|
+
status: number;
|
|
17
|
+
code?: string;
|
|
18
|
+
message: string;
|
|
19
|
+
responseId?: string;
|
|
20
|
+
retryAfterSec?: number;
|
|
21
|
+
});
|
|
22
|
+
/** 인증 실패 (401, 키 누락·만료·폐기) */
|
|
23
|
+
get isAuthError(): boolean;
|
|
24
|
+
/** Rate limit 초과 (429) — retryAfterSec 후 재시도 가능 */
|
|
25
|
+
get isRateLimited(): boolean;
|
|
26
|
+
/** 일시적 장애 (5xx) — 재시도 가능 */
|
|
27
|
+
get isTransient(): boolean;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;gBAE9B,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,aAAa,CAAC,EAAE,MAAM,CAAA;KACvB;IASD,8BAA8B;IAC9B,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED,mDAAmD;IACnD,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,4BAA4B;IAC5B,IAAI,WAAW,IAAI,OAAO,CAEzB;CACF"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API 에러 클래스.
|
|
3
|
+
*
|
|
4
|
+
* 모든 API 호출이 실패하면 SazuApiError 를 throw 한다.
|
|
5
|
+
* - status: HTTP 상태 코드 (네트워크 실패 시 0)
|
|
6
|
+
* - code: 서버 측 에러 코드 (`INVALID_API_KEY`, `KEY_EXPIRED` 등)
|
|
7
|
+
* - message: 사람이 읽을 수 있는 메시지
|
|
8
|
+
* - responseId: 응답 fingerprint (forensics·고객 지원 시 사용)
|
|
9
|
+
*/
|
|
10
|
+
export class SazuApiError extends Error {
|
|
11
|
+
status;
|
|
12
|
+
code;
|
|
13
|
+
responseId;
|
|
14
|
+
retryAfterSec;
|
|
15
|
+
constructor(opts) {
|
|
16
|
+
super(opts.message);
|
|
17
|
+
this.name = 'SazuApiError';
|
|
18
|
+
this.status = opts.status;
|
|
19
|
+
this.code = opts.code;
|
|
20
|
+
this.responseId = opts.responseId;
|
|
21
|
+
this.retryAfterSec = opts.retryAfterSec;
|
|
22
|
+
}
|
|
23
|
+
/** 인증 실패 (401, 키 누락·만료·폐기) */
|
|
24
|
+
get isAuthError() {
|
|
25
|
+
return this.status === 401;
|
|
26
|
+
}
|
|
27
|
+
/** Rate limit 초과 (429) — retryAfterSec 후 재시도 가능 */
|
|
28
|
+
get isRateLimited() {
|
|
29
|
+
return this.status === 429;
|
|
30
|
+
}
|
|
31
|
+
/** 일시적 장애 (5xx) — 재시도 가능 */
|
|
32
|
+
get isTransient() {
|
|
33
|
+
return this.status === 0 || this.status >= 500;
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP fetch wrapper — 서버 사이드 전용.
|
|
3
|
+
*
|
|
4
|
+
* 보안 정책:
|
|
5
|
+
* - 브라우저 환경 감지 시 경고 (API 키 노출 방지)
|
|
6
|
+
* - 모든 요청에 X-Client-Type: sdk 헤더 부착 (서버 측 통계 분리)
|
|
7
|
+
* - 응답 _meta.responseId 자동 추출 → SazuApiError 에 포함
|
|
8
|
+
* - 5xx / 0 (네트워크) 에러는 exponential backoff 로 재시도 (옵션)
|
|
9
|
+
*/
|
|
10
|
+
export interface ClientOptions {
|
|
11
|
+
/** SAZU API 키 (필수). 환경변수 SAZU_API_KEY 권장. */
|
|
12
|
+
apiKey: string;
|
|
13
|
+
/** API 베이스 URL. 기본 https://api.sazu.app */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** 추가 HTTP 헤더 (재정의 금지: Authorization·x-api-key·X-Client-Type) */
|
|
16
|
+
headers?: Record<string, string>;
|
|
17
|
+
/** 재시도 횟수 (5xx·네트워크 에러). 기본 2. */
|
|
18
|
+
maxRetries?: number;
|
|
19
|
+
/** fetch 타임아웃 ms. 기본 30000. */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export declare class HttpClient {
|
|
23
|
+
private apiKey;
|
|
24
|
+
private baseUrl;
|
|
25
|
+
private headers;
|
|
26
|
+
private maxRetries;
|
|
27
|
+
private timeoutMs;
|
|
28
|
+
constructor(opts: ClientOptions);
|
|
29
|
+
request<T>(path: string, init?: RequestInit): Promise<T>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,aAAa;IAC5B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAA;IACd,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,kCAAkC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+BAA+B;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AA8BD,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,SAAS,CAAQ;gBAEb,IAAI,EAAE,aAAa;IAgBzB,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;CA8GnE"}
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP fetch wrapper — 서버 사이드 전용.
|
|
3
|
+
*
|
|
4
|
+
* 보안 정책:
|
|
5
|
+
* - 브라우저 환경 감지 시 경고 (API 키 노출 방지)
|
|
6
|
+
* - 모든 요청에 X-Client-Type: sdk 헤더 부착 (서버 측 통계 분리)
|
|
7
|
+
* - 응답 _meta.responseId 자동 추출 → SazuApiError 에 포함
|
|
8
|
+
* - 5xx / 0 (네트워크) 에러는 exponential backoff 로 재시도 (옵션)
|
|
9
|
+
*/
|
|
10
|
+
import { SazuApiError } from './errors.js';
|
|
11
|
+
const DEFAULT_BASE_URL = 'https://api.sazu.app';
|
|
12
|
+
const DEFAULT_MAX_RETRIES = 2;
|
|
13
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
14
|
+
/**
|
|
15
|
+
* 클라이언트 브라우저 환경 감지 — API 키 유출 위험 경고.
|
|
16
|
+
*
|
|
17
|
+
* 1회만 경고 출력 (반복 호출 시 spam 방지).
|
|
18
|
+
*/
|
|
19
|
+
let warnedBrowser = false;
|
|
20
|
+
function warnIfBrowser() {
|
|
21
|
+
if (warnedBrowser)
|
|
22
|
+
return;
|
|
23
|
+
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
|
24
|
+
warnedBrowser = true;
|
|
25
|
+
// eslint-disable-next-line no-console
|
|
26
|
+
console.warn('[@sazuapp/client] ⚠ 브라우저 환경 감지. 본 SDK 는 서버 사이드 전용입니다.\n' +
|
|
27
|
+
' API 키가 클라이언트 번들에 포함되면 외부에 노출됩니다.\n' +
|
|
28
|
+
' Next.js: app/api/* 또는 server actions 안에서만 사용하세요.\n' +
|
|
29
|
+
' Vite/SvelteKit: +server.ts 또는 endpoint 에서만 사용하세요.');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function sleep(ms) {
|
|
33
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
34
|
+
}
|
|
35
|
+
export class HttpClient {
|
|
36
|
+
apiKey;
|
|
37
|
+
baseUrl;
|
|
38
|
+
headers;
|
|
39
|
+
maxRetries;
|
|
40
|
+
timeoutMs;
|
|
41
|
+
constructor(opts) {
|
|
42
|
+
if (!opts.apiKey || typeof opts.apiKey !== 'string') {
|
|
43
|
+
throw new SazuApiError({
|
|
44
|
+
status: 0,
|
|
45
|
+
code: 'NO_API_KEY',
|
|
46
|
+
message: 'apiKey 가 필요합니다. https://www.sazu.app/manse-api/dashboard/keys 에서 발급받으세요.',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
this.apiKey = opts.apiKey;
|
|
50
|
+
this.baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
51
|
+
this.headers = opts.headers ?? {};
|
|
52
|
+
this.maxRetries = opts.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
53
|
+
this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
54
|
+
}
|
|
55
|
+
async request(path, init = {}) {
|
|
56
|
+
warnIfBrowser();
|
|
57
|
+
const url = `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
|
58
|
+
const headers = {
|
|
59
|
+
'Content-Type': 'application/json',
|
|
60
|
+
...this.headers,
|
|
61
|
+
// 아래 3개는 사용자가 override 불가
|
|
62
|
+
'x-api-key': this.apiKey,
|
|
63
|
+
'X-Client-Type': 'sdk',
|
|
64
|
+
'User-Agent': `@sazuapp/client/${PACKAGE_VERSION}`,
|
|
65
|
+
};
|
|
66
|
+
let lastError = null;
|
|
67
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const timeoutHandle = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
70
|
+
try {
|
|
71
|
+
const res = await fetch(url, {
|
|
72
|
+
...init,
|
|
73
|
+
headers,
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
});
|
|
76
|
+
clearTimeout(timeoutHandle);
|
|
77
|
+
const text = await res.text();
|
|
78
|
+
let body = null;
|
|
79
|
+
try {
|
|
80
|
+
body = text ? JSON.parse(text) : null;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* 비-JSON 응답 */
|
|
84
|
+
}
|
|
85
|
+
const responseId = body?._meta?.responseId ??
|
|
86
|
+
res.headers.get('x-response-id') ??
|
|
87
|
+
undefined;
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const errObj = body?.error;
|
|
90
|
+
const retryAfterRaw = res.headers.get('retry-after');
|
|
91
|
+
const retryAfterSec = retryAfterRaw ? Number(retryAfterRaw) : undefined;
|
|
92
|
+
const err = new SazuApiError({
|
|
93
|
+
status: res.status,
|
|
94
|
+
code: errObj?.code,
|
|
95
|
+
message: errObj?.message ?? `HTTP ${res.status}: ${text.slice(0, 200) || res.statusText}`,
|
|
96
|
+
responseId,
|
|
97
|
+
retryAfterSec: Number.isFinite(retryAfterSec) ? retryAfterSec : undefined,
|
|
98
|
+
});
|
|
99
|
+
// 재시도 가능한 케이스: 5xx, 429
|
|
100
|
+
if (err.isTransient && attempt < this.maxRetries) {
|
|
101
|
+
const backoffMs = Math.min(2 ** attempt * 500, 5000);
|
|
102
|
+
lastError = err;
|
|
103
|
+
await sleep(backoffMs);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (err.isRateLimited && err.retryAfterSec && attempt < this.maxRetries) {
|
|
107
|
+
lastError = err;
|
|
108
|
+
await sleep(Math.min(err.retryAfterSec * 1000, 30_000));
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
const success = body;
|
|
114
|
+
if (!success || success.success !== true) {
|
|
115
|
+
throw new SazuApiError({
|
|
116
|
+
status: res.status,
|
|
117
|
+
code: 'UNEXPECTED_RESPONSE',
|
|
118
|
+
message: '예상치 못한 응답 형식입니다.',
|
|
119
|
+
responseId,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return success.data;
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
clearTimeout(timeoutHandle);
|
|
126
|
+
if (err instanceof SazuApiError) {
|
|
127
|
+
if (err.isTransient && attempt < this.maxRetries) {
|
|
128
|
+
lastError = err;
|
|
129
|
+
const backoffMs = Math.min(2 ** attempt * 500, 5000);
|
|
130
|
+
await sleep(backoffMs);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
throw err;
|
|
134
|
+
}
|
|
135
|
+
// 네트워크·timeout 등
|
|
136
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
137
|
+
const netErr = new SazuApiError({
|
|
138
|
+
status: 0,
|
|
139
|
+
code: 'NETWORK_ERROR',
|
|
140
|
+
message: `네트워크 호출 실패: ${message}`,
|
|
141
|
+
});
|
|
142
|
+
if (attempt < this.maxRetries) {
|
|
143
|
+
lastError = netErr;
|
|
144
|
+
const backoffMs = Math.min(2 ** attempt * 500, 5000);
|
|
145
|
+
await sleep(backoffMs);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw netErr;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
// 도달 불가 (loop 안에서 throw 함). 안전망.
|
|
152
|
+
throw lastError ?? new SazuApiError({ status: 0, code: 'UNKNOWN', message: '호출 실패.' });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// build 시점에 tsc 가 dist/index.js 의 import 가 필요. 패키지 버전은 빌드 산출물에서 가져옴.
|
|
156
|
+
// 단순화 위해 const 로 fix — 패키지 메이저 변경 시 동기 갱신.
|
|
157
|
+
const PACKAGE_VERSION = '0.1.0';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sazuapp/client — SAZU API TypeScript SDK
|
|
3
|
+
*
|
|
4
|
+
* 공식 문서: https://www.sazu.app/manse-api/docs
|
|
5
|
+
* 키 발급: https://www.sazu.app/manse-api/dashboard/keys
|
|
6
|
+
*
|
|
7
|
+
* 본 패키지는 SAZU REST API 의 얇은 wrapper 입니다.
|
|
8
|
+
* 명리 계산 알고리즘·비즈니스 로직은 포함하지 않으며, 모든 계산은 서버에서 수행됩니다.
|
|
9
|
+
*/
|
|
10
|
+
export { SazuClient } from './client.js';
|
|
11
|
+
export { SazuApiError } from './errors.js';
|
|
12
|
+
export type { ClientOptions } from './http.js';
|
|
13
|
+
export type { PlanTier, Locale, DetailLevel, ResponseMeta, SuccessResponse, ErrorResponse, MeData, CalculateInput, CalculateResult, CalendarDirection, CalendarConvertInput, CalendarConvertResult, } from './types.js';
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AAC9C,YAAY,EAEV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,YAAY,EACZ,eAAe,EACf,aAAa,EAEb,MAAM,EAEN,cAAc,EACd,eAAe,EAEf,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sazuapp/client — SAZU API TypeScript SDK
|
|
3
|
+
*
|
|
4
|
+
* 공식 문서: https://www.sazu.app/manse-api/docs
|
|
5
|
+
* 키 발급: https://www.sazu.app/manse-api/dashboard/keys
|
|
6
|
+
*
|
|
7
|
+
* 본 패키지는 SAZU REST API 의 얇은 wrapper 입니다.
|
|
8
|
+
* 명리 계산 알고리즘·비즈니스 로직은 포함하지 않으며, 모든 계산은 서버에서 수행됩니다.
|
|
9
|
+
*/
|
|
10
|
+
export { SazuClient } from './client.js';
|
|
11
|
+
export { SazuApiError } from './errors.js';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API 입력·출력 타입.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript 인터페이스만 정의 (런타임 zod 검증 안 함 — 의존성 0 유지).
|
|
5
|
+
* 응답 schema 는 공개 docs (https://www.sazu.app/manse-api/docs) 와 동기.
|
|
6
|
+
*
|
|
7
|
+
* 비공개 내부 구조는 노출하지 않는다 — 본 SDK 는 REST API 의 얇은 wrapper 일 뿐,
|
|
8
|
+
* sazu-logic 코드·계산 알고리즘은 절대 포함하지 않는다.
|
|
9
|
+
*/
|
|
10
|
+
export interface ResponseMeta {
|
|
11
|
+
/** forensics 추적용 응답 ID. 형식: `${ts36}-${keyHash8}-${rand8}` */
|
|
12
|
+
responseId: string;
|
|
13
|
+
/** 서버 응답 timestamp (ms) */
|
|
14
|
+
t: number;
|
|
15
|
+
}
|
|
16
|
+
export interface SuccessResponse<T> {
|
|
17
|
+
success: true;
|
|
18
|
+
data: T;
|
|
19
|
+
_meta?: ResponseMeta;
|
|
20
|
+
}
|
|
21
|
+
export interface ErrorResponse {
|
|
22
|
+
success: false;
|
|
23
|
+
error: {
|
|
24
|
+
code: string;
|
|
25
|
+
message: string;
|
|
26
|
+
[key: string]: unknown;
|
|
27
|
+
};
|
|
28
|
+
_meta?: ResponseMeta;
|
|
29
|
+
}
|
|
30
|
+
export type PlanTier = 'free' | 'pro' | 'event';
|
|
31
|
+
export interface MeData {
|
|
32
|
+
tier: PlanTier;
|
|
33
|
+
keyPrefix: string;
|
|
34
|
+
rateLimitPerMinute?: number;
|
|
35
|
+
monthlyQuota?: number;
|
|
36
|
+
}
|
|
37
|
+
export type Locale = 'ko' | 'han';
|
|
38
|
+
export type DetailLevel = 'minimal' | 'standard' | 'full';
|
|
39
|
+
export interface CalculateInput {
|
|
40
|
+
/** 출생년도 (1900~2100) */
|
|
41
|
+
birthYear: number;
|
|
42
|
+
/** 출생월 (1~12) */
|
|
43
|
+
birthMonth: number;
|
|
44
|
+
/** 출생일 (1~31) */
|
|
45
|
+
birthDay: number;
|
|
46
|
+
/** 출생시 (0~23). 시간 미상이면 생략 또는 null. */
|
|
47
|
+
birthHour?: number | null;
|
|
48
|
+
/** 출생분 (0~59). 기본 0. */
|
|
49
|
+
birthMinute?: number;
|
|
50
|
+
/** 여성 여부 */
|
|
51
|
+
isFemale: boolean;
|
|
52
|
+
/** 음력 여부. 기본 false. */
|
|
53
|
+
isLunar?: boolean;
|
|
54
|
+
/** 출생 도시 (진태양시 보정용). 기본 '서울'. */
|
|
55
|
+
birthCity?: string;
|
|
56
|
+
/** 한글 또는 한자 출력. 기본 'ko'. */
|
|
57
|
+
locale?: Locale;
|
|
58
|
+
/** 포함할 모듈 ID 목록. 생략 시 플랜별 기본 모듈. */
|
|
59
|
+
modules?: string[];
|
|
60
|
+
/** 대운 개수 (13~20) */
|
|
61
|
+
decadeCount?: number;
|
|
62
|
+
/** 응답 상세 수준. 기본 'standard'. */
|
|
63
|
+
detail?: DetailLevel;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 사주 분석 결과.
|
|
67
|
+
*
|
|
68
|
+
* 모듈별 구조가 다양하므로 타입은 `Record<string, unknown>` 로 노출.
|
|
69
|
+
* 정확한 모듈별 schema 는 docs 페이지의 예시 참조.
|
|
70
|
+
* (https://www.sazu.app/manse-api/docs)
|
|
71
|
+
*/
|
|
72
|
+
export interface CalculateResult {
|
|
73
|
+
/** 사주 원국 (연·월·일·시주) */
|
|
74
|
+
fourPillars?: Record<string, unknown>;
|
|
75
|
+
/** 10년 단위 대운 흐름 */
|
|
76
|
+
decadeFortune?: Record<string, unknown>;
|
|
77
|
+
/** 오행 분포 */
|
|
78
|
+
elements?: Record<string, unknown>;
|
|
79
|
+
/** 분석 요약 */
|
|
80
|
+
summary?: Record<string, unknown>;
|
|
81
|
+
/** 신강/신약 */
|
|
82
|
+
sinStrength?: Record<string, unknown>;
|
|
83
|
+
/** Pro 모듈 — 신살 */
|
|
84
|
+
sinsal?: Record<string, unknown>;
|
|
85
|
+
/** Pro 모듈 — 합형충파해 (원국 + 허자 이중 분석) */
|
|
86
|
+
relationships?: Record<string, unknown>;
|
|
87
|
+
/** Pro 모듈 — 허자 분석 */
|
|
88
|
+
ghostElements?: Record<string, unknown>;
|
|
89
|
+
/** Pro 모듈 — 격국 */
|
|
90
|
+
gyeokguk?: Record<string, unknown>;
|
|
91
|
+
/** Pro 모듈 — 용신 */
|
|
92
|
+
yongsin?: Record<string, unknown>;
|
|
93
|
+
/** 기타 모듈 (확장 가능) */
|
|
94
|
+
[moduleId: string]: unknown;
|
|
95
|
+
}
|
|
96
|
+
export type CalendarDirection = 'toSolar' | 'toLunar';
|
|
97
|
+
export interface CalendarConvertInput {
|
|
98
|
+
year: number;
|
|
99
|
+
month: number;
|
|
100
|
+
day: number;
|
|
101
|
+
direction: CalendarDirection;
|
|
102
|
+
}
|
|
103
|
+
export interface CalendarConvertResult {
|
|
104
|
+
year: number;
|
|
105
|
+
month: number;
|
|
106
|
+
day: number;
|
|
107
|
+
isLeapMonth?: boolean;
|
|
108
|
+
[key: string]: unknown;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SAZU API 입력·출력 타입.
|
|
3
|
+
*
|
|
4
|
+
* TypeScript 인터페이스만 정의 (런타임 zod 검증 안 함 — 의존성 0 유지).
|
|
5
|
+
* 응답 schema 는 공개 docs (https://www.sazu.app/manse-api/docs) 와 동기.
|
|
6
|
+
*
|
|
7
|
+
* 비공개 내부 구조는 노출하지 않는다 — 본 SDK 는 REST API 의 얇은 wrapper 일 뿐,
|
|
8
|
+
* sazu-logic 코드·계산 알고리즘은 절대 포함하지 않는다.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sazuapp/client",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SAZU API 공식 TypeScript SDK — 사주·만세력·합형충파해·격국·용신 등 14개 명리 분석을 한 줄로.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc -p tsconfig.json",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
22
|
+
"dev": "tsc -p tsconfig.json --watch"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"saju",
|
|
29
|
+
"sazu",
|
|
30
|
+
"fortune",
|
|
31
|
+
"manse",
|
|
32
|
+
"korean-fortune",
|
|
33
|
+
"korean-astrology",
|
|
34
|
+
"myeongni",
|
|
35
|
+
"sdk",
|
|
36
|
+
"typescript"
|
|
37
|
+
],
|
|
38
|
+
"author": "Inavan",
|
|
39
|
+
"license": "MIT",
|
|
40
|
+
"homepage": "https://www.sazu.app/manse-api",
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git+https://github.com/ainvention/sazu-app-manse-api.git",
|
|
44
|
+
"directory": "client"
|
|
45
|
+
},
|
|
46
|
+
"bugs": {
|
|
47
|
+
"url": "https://github.com/ainvention/sazu-app-manse-api/issues"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"typescript": "^5.5.0",
|
|
51
|
+
"@types/node": "^20.11.0"
|
|
52
|
+
}
|
|
53
|
+
}
|