@cp949/japanpost-react 1.0.1 → 1.0.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.ko.md +62 -22
- package/README.md +207 -191
- package/dist/index.d.ts +1 -11
- package/dist/index.es.js +106 -62
- package/dist/index.umd.cjs +1 -1
- package/dist/src/components/AddressSearchInput.d.ts.map +1 -0
- package/dist/src/components/PostalCodeInput.d.ts.map +1 -0
- package/dist/src/core/errors.d.ts.map +1 -0
- package/dist/src/core/formatters.d.ts.map +1 -0
- package/dist/src/core/normalizers.d.ts.map +1 -0
- package/dist/{core → src/core}/types.d.ts +27 -7
- package/dist/src/core/types.d.ts.map +1 -0
- package/dist/src/core/validators.d.ts.map +1 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/react/toJapanAddressError.d.ts.map +1 -0
- package/dist/src/react/useJapanAddress.d.ts.map +1 -0
- package/dist/src/react/useJapanAddressSearch.d.ts.map +1 -0
- package/dist/src/react/useJapanPostalCode.d.ts.map +1 -0
- package/dist/{react → src/react}/useLatestRequestState.d.ts +1 -0
- package/dist/src/react/useLatestRequestState.d.ts.map +1 -0
- package/package.json +4 -4
- package/dist/components/AddressSearchInput.d.ts.map +0 -1
- package/dist/components/PostalCodeInput.d.ts.map +0 -1
- package/dist/core/errors.d.ts.map +0 -1
- package/dist/core/formatters.d.ts.map +0 -1
- package/dist/core/normalizers.d.ts.map +0 -1
- package/dist/core/types.d.ts.map +0 -1
- package/dist/core/validators.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/react/toJapanAddressError.d.ts.map +0 -1
- package/dist/react/useJapanAddress.d.ts.map +0 -1
- package/dist/react/useJapanAddressSearch.d.ts.map +0 -1
- package/dist/react/useJapanPostalCode.d.ts.map +0 -1
- package/dist/react/useLatestRequestState.d.ts.map +0 -1
- /package/dist/{components → src/components}/AddressSearchInput.d.ts +0 -0
- /package/dist/{components → src/components}/PostalCodeInput.d.ts +0 -0
- /package/dist/{core → src/core}/errors.d.ts +0 -0
- /package/dist/{core → src/core}/formatters.d.ts +0 -0
- /package/dist/{core → src/core}/normalizers.d.ts +0 -0
- /package/dist/{core → src/core}/validators.d.ts +0 -0
- /package/dist/{react → src/react}/toJapanAddressError.d.ts +0 -0
- /package/dist/{react → src/react}/useJapanAddress.d.ts +0 -0
- /package/dist/{react → src/react}/useJapanAddressSearch.d.ts +0 -0
- /package/dist/{react → src/react}/useJapanPostalCode.d.ts +0 -0
package/README.ko.md
CHANGED
|
@@ -169,8 +169,10 @@ export function PostalForm() {
|
|
|
169
169
|
- `useJapanAddress`
|
|
170
170
|
- `PostalCodeInput`
|
|
171
171
|
- `AddressSearchInput`
|
|
172
|
-
- `JapanAddress`, `JapanAddressDataSource`, `
|
|
173
|
-
`
|
|
172
|
+
- `JapanAddress`, `JapanAddressDataSource`, `JapanPostalCodeSearchInput`,
|
|
173
|
+
`JapanAddressSearchInput`, `JapanPostSearchcodeRequest`,
|
|
174
|
+
`JapanPostAddresszipRequest`, `Page`를 포함한
|
|
175
|
+
공개 타입
|
|
174
176
|
- 요청 옵션 타입: `JapanAddressRequestOptions`
|
|
175
177
|
|
|
176
178
|
## 유틸리티 메모
|
|
@@ -182,56 +184,94 @@ export function PostalForm() {
|
|
|
182
184
|
|
|
183
185
|
### useJapanPostalCode
|
|
184
186
|
|
|
185
|
-
우편번호로 주소를 조회합니다.
|
|
186
|
-
prefix 검색으로 동작합니다.
|
|
187
|
+
우편번호로 주소를 조회합니다. 문자열 입력과 구조화된 요청 입력을 모두
|
|
188
|
+
받을 수 있고, `3~6자리` 입력일 때는 prefix 검색으로 동작합니다.
|
|
187
189
|
|
|
188
190
|
```tsx
|
|
189
|
-
const { loading, data, error, search, reset } = useJapanPostalCode({
|
|
191
|
+
const { loading, data, error, search, cancel, reset } = useJapanPostalCode({
|
|
190
192
|
dataSource,
|
|
191
193
|
});
|
|
192
194
|
```
|
|
193
195
|
|
|
196
|
+
```tsx
|
|
197
|
+
void search("1000001");
|
|
198
|
+
void search({
|
|
199
|
+
postalCode: "1000001",
|
|
200
|
+
pageNumber: 1,
|
|
201
|
+
rowsPerPage: 10,
|
|
202
|
+
includeParenthesesTown: true,
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
`cancel()`은 진행 중인 조회를 중단하고 현재 data/error 상태는 그대로 유지합니다.
|
|
207
|
+
`reset()`은 `cancel()`을 호출한 뒤 data/error까지 비워 훅을 idle 상태로
|
|
208
|
+
되돌립니다.
|
|
209
|
+
|
|
194
210
|
### useJapanAddressSearch
|
|
195
211
|
|
|
196
|
-
자유 형식
|
|
212
|
+
자유 형식 키워드 또는 구조화된 필드로 주소를 검색하며 `debounceMs`를 지원합니다.
|
|
197
213
|
|
|
198
214
|
```tsx
|
|
199
|
-
const { loading, data, error, search, reset } = useJapanAddressSearch({
|
|
215
|
+
const { loading, data, error, search, cancel, reset } = useJapanAddressSearch({
|
|
200
216
|
dataSource,
|
|
201
217
|
debounceMs: 300,
|
|
202
218
|
});
|
|
203
219
|
```
|
|
204
220
|
|
|
221
|
+
```tsx
|
|
222
|
+
void search("Tokyo");
|
|
223
|
+
void search({
|
|
224
|
+
addressQuery: "Tokyo",
|
|
225
|
+
pageNumber: 1,
|
|
226
|
+
rowsPerPage: 10,
|
|
227
|
+
includeCityDetails: true,
|
|
228
|
+
});
|
|
229
|
+
void search({
|
|
230
|
+
prefName: "東京都",
|
|
231
|
+
cityName: "千代田区",
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
`search("Tokyo")`는 `addressQuery` 검색의 축약형입니다. 구조화된 필드는
|
|
236
|
+
`addressQuery` 없이도 사용할 수 있어서, 도도부현/시/동 단위로만 검색하고 싶을 때
|
|
237
|
+
유용합니다.
|
|
238
|
+
|
|
205
239
|
이 훅은 빈 검색어에 대해 여전히 클라이언트 선제 검증을 수행하고, 요청을 보내기
|
|
206
240
|
전에 `invalid_query`를 반환합니다. 이 검증은 UX 보조 장치이며 서버 검증이나
|
|
207
241
|
서버 계약 처리를 대체하지 않습니다.
|
|
208
242
|
|
|
243
|
+
`cancel()`은 진행 중인 요청이나 대기 중인 debounce 타이머를 중단하고,
|
|
244
|
+
현재 data/error 상태는 그대로 유지합니다. `reset()`은 `cancel()`을 호출한 뒤
|
|
245
|
+
data와 error까지 비워 훅을 idle 상태로 되돌립니다.
|
|
246
|
+
|
|
209
247
|
### useJapanAddress
|
|
210
248
|
|
|
211
|
-
우편번호 조회와
|
|
249
|
+
우편번호 조회와 주소 질의 검색을 하나의 훅으로 합칩니다.
|
|
212
250
|
|
|
213
251
|
```tsx
|
|
214
|
-
const { loading, data, error, searchByPostalCode,
|
|
252
|
+
const { loading, data, error, searchByPostalCode, searchByAddressQuery, reset } =
|
|
215
253
|
useJapanAddress({ dataSource, debounceMs: 300 });
|
|
216
254
|
```
|
|
217
255
|
|
|
218
256
|
모든 훅은 런타임에서 `dataSource`가 필요합니다.
|
|
219
257
|
|
|
220
|
-
|
|
258
|
+
`useJapanPostalCode().search`, `useJapanAddressSearch().search`,
|
|
259
|
+
`useJapanAddress`의 편의 메서드는 모두 같은 공개 검색 입력 타입을 받습니다.
|
|
221
260
|
|
|
222
|
-
- `useJapanPostalCode().search(
|
|
223
|
-
- `useJapanAddressSearch().search(
|
|
224
|
-
- `useJapanAddress().searchByPostalCode(
|
|
225
|
-
- `useJapanAddress().
|
|
261
|
+
- `useJapanPostalCode().search(input: JapanPostalCodeSearchInput)`
|
|
262
|
+
- `useJapanAddressSearch().search(input: JapanAddressSearchInput)`
|
|
263
|
+
- `useJapanAddress().searchByPostalCode(input: JapanPostalCodeSearchInput)`
|
|
264
|
+
- `useJapanAddress().searchByAddressQuery(input: JapanAddressSearchInput)`
|
|
226
265
|
|
|
227
266
|
대신 훅 내부에서 `dataSource` 호출 전에 request object를 조립합니다.
|
|
228
267
|
|
|
229
|
-
- 우편번호 조회: `{
|
|
230
|
-
- 주소 검색: `{
|
|
268
|
+
- 우편번호 조회: `{ postalCode, pageNumber: 0, rowsPerPage: 100 }`
|
|
269
|
+
- 주소 검색: `{ addressQuery, pageNumber: 0, rowsPerPage: 100 }`
|
|
231
270
|
|
|
232
|
-
`
|
|
233
|
-
|
|
234
|
-
|
|
271
|
+
`useJapanAddressSearch`에서는 `addressQuery`를 생략한 구조화 요청도 가능하므로
|
|
272
|
+
도도부현, 시, 동 필드만으로 검색할 수 있습니다. `includeCityDetails`,
|
|
273
|
+
`includePrefectureDetails` 같은 optional flag는 기본적으로 넣지 않으며,
|
|
274
|
+
필요하면 사용자 data source 구현에서 직접 지정하면 됩니다.
|
|
235
275
|
|
|
236
276
|
## 에러 처리 메모
|
|
237
277
|
|
|
@@ -248,9 +288,9 @@ type JapanAddressRequestOptions = {
|
|
|
248
288
|
};
|
|
249
289
|
```
|
|
250
290
|
|
|
251
|
-
훅은 superseded 요청, `reset()`, unmount 정리 상황에서 이전
|
|
252
|
-
있도록 `signal`을 전달합니다. 백엔드 레이어가 abort를 지원하면
|
|
253
|
-
있습니다.
|
|
291
|
+
훅은 superseded 요청, `cancel()`, `reset()`, unmount 정리 상황에서 이전
|
|
292
|
+
요청을 취소할 수 있도록 `signal`을 전달합니다. 백엔드 레이어가 abort를 지원하면
|
|
293
|
+
그대로 활용할 수 있습니다.
|
|
254
294
|
|
|
255
295
|
권장 에러 코드 매핑:
|
|
256
296
|
|
package/README.md
CHANGED
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
<!-- This file is generated by `pnpm readme:package`. Edit docs/README.en.md and docs/README.ko.md instead. -->
|
|
6
6
|
|
|
7
|
-
React
|
|
8
|
-
lookup.
|
|
7
|
+
React hooks, headless input components, and utilities for Japan postal-code
|
|
8
|
+
and address lookup.
|
|
9
9
|
|
|
10
|
-
This package
|
|
11
|
-
|
|
12
|
-
and are documented in the root README.
|
|
10
|
+
This package does not call Japan Post directly. You provide a
|
|
11
|
+
`JapanAddressDataSource` that talks to your own backend API.
|
|
13
12
|
|
|
14
13
|
## Install
|
|
15
14
|
|
|
@@ -17,45 +16,56 @@ and are documented in the root README.
|
|
|
17
16
|
pnpm add @cp949/japanpost-react
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
-
|
|
19
|
+
- Peer dependencies: React 18 or React 19
|
|
20
|
+
- Package source in this repository:
|
|
21
|
+
`packages/japanpost-react`
|
|
22
|
+
- Demo app in this repository: `apps/demo`
|
|
23
|
+
|
|
24
|
+
## What The Package Provides
|
|
25
|
+
|
|
26
|
+
- Hooks for postal-code lookup and address search:
|
|
27
|
+
`useJapanPostalCode`, `useJapanAddressSearch`, `useJapanAddress`
|
|
28
|
+
- Headless form components:
|
|
29
|
+
`PostalCodeInput`, `AddressSearchInput`
|
|
30
|
+
- Utilities:
|
|
31
|
+
`normalizeJapanPostalCode`, `formatJapanPostalCode`,
|
|
32
|
+
`isValidJapanPostalCode`, `normalizeJapanPostAddressRecord`,
|
|
33
|
+
`createJapanAddressError`
|
|
34
|
+
- Public types for the request, response, error, and data-source contracts
|
|
21
35
|
|
|
22
36
|
## Quick Start
|
|
23
37
|
|
|
38
|
+
The package expects a `JapanAddressDataSource` with two methods:
|
|
39
|
+
|
|
40
|
+
- `lookupPostalCode(request, options?)`
|
|
41
|
+
- `searchAddress(request, options?)`
|
|
42
|
+
|
|
43
|
+
Both methods return `Promise<Page<JapanAddress>>`.
|
|
44
|
+
|
|
24
45
|
```tsx
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
JapanAddress,
|
|
30
|
-
|
|
46
|
+
import {
|
|
47
|
+
PostalCodeInput,
|
|
48
|
+
createJapanAddressError,
|
|
49
|
+
useJapanPostalCode,
|
|
50
|
+
type JapanAddress,
|
|
51
|
+
type JapanAddressDataSource,
|
|
52
|
+
type JapanAddressRequestOptions,
|
|
53
|
+
type Page,
|
|
31
54
|
} from "@cp949/japanpost-react";
|
|
32
|
-
import { createJapanAddressError } from "@cp949/japanpost-react";
|
|
33
55
|
|
|
34
|
-
// The only supported integration model is a real server-backed flow.
|
|
35
|
-
// Point the data source at your own backend API.
|
|
36
|
-
// On beta-compatible backends, blank addresszip searches and postal-code misses
|
|
37
|
-
// may return HTTP 200 with an empty page. Keep those as successful Page results.
|
|
38
|
-
// The status mapping below applies only to non-OK responses.
|
|
39
56
|
function isAbortError(error: unknown): boolean {
|
|
40
57
|
return error instanceof DOMException && error.name === "AbortError";
|
|
41
58
|
}
|
|
42
59
|
|
|
43
|
-
function
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (status === 400) {
|
|
53
|
-
return path === "/q/japanpost/searchcode"
|
|
54
|
-
? "invalid_postal_code"
|
|
55
|
-
: "invalid_query";
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return "data_source_error";
|
|
60
|
+
function isPagePayload(payload: unknown): payload is Page<JapanAddress> {
|
|
61
|
+
return (
|
|
62
|
+
typeof payload === "object" &&
|
|
63
|
+
payload !== null &&
|
|
64
|
+
Array.isArray((payload as { elements?: unknown }).elements) &&
|
|
65
|
+
typeof (payload as { totalElements?: unknown }).totalElements === "number" &&
|
|
66
|
+
typeof (payload as { pageNumber?: unknown }).pageNumber === "number" &&
|
|
67
|
+
typeof (payload as { rowsPerPage?: unknown }).rowsPerPage === "number"
|
|
68
|
+
);
|
|
59
69
|
}
|
|
60
70
|
|
|
61
71
|
async function readPage(
|
|
@@ -63,10 +73,10 @@ async function readPage(
|
|
|
63
73
|
request: unknown,
|
|
64
74
|
options?: JapanAddressRequestOptions,
|
|
65
75
|
): Promise<Page<JapanAddress>> {
|
|
66
|
-
let
|
|
76
|
+
let response: Response;
|
|
67
77
|
|
|
68
78
|
try {
|
|
69
|
-
|
|
79
|
+
response = await fetch(path, {
|
|
70
80
|
method: "POST",
|
|
71
81
|
headers: {
|
|
72
82
|
"content-type": "application/json",
|
|
@@ -78,173 +88,213 @@ async function readPage(
|
|
|
78
88
|
throw createJapanAddressError(
|
|
79
89
|
isAbortError(error) ? "timeout" : "network_error",
|
|
80
90
|
isAbortError(error) ? "Request timed out" : "Network request failed",
|
|
81
|
-
{
|
|
82
|
-
cause: error,
|
|
83
|
-
},
|
|
91
|
+
{ cause: error },
|
|
84
92
|
);
|
|
85
93
|
}
|
|
86
94
|
|
|
87
|
-
if (!
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
status:
|
|
92
|
-
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw createJapanAddressError(
|
|
97
|
+
"data_source_error",
|
|
98
|
+
`Request failed with status ${response.status}`,
|
|
99
|
+
{ status: response.status },
|
|
100
|
+
);
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
let payload: unknown;
|
|
96
104
|
|
|
97
105
|
try {
|
|
98
|
-
payload = await
|
|
106
|
+
payload = await response.json();
|
|
99
107
|
} catch (error) {
|
|
100
108
|
throw createJapanAddressError(
|
|
101
109
|
"bad_response",
|
|
102
110
|
"Response payload was not valid JSON",
|
|
103
|
-
{
|
|
104
|
-
cause: error,
|
|
105
|
-
},
|
|
111
|
+
{ cause: error },
|
|
106
112
|
);
|
|
107
113
|
}
|
|
108
114
|
|
|
109
|
-
if (
|
|
110
|
-
typeof payload !== "object" ||
|
|
111
|
-
payload === null ||
|
|
112
|
-
!Array.isArray((payload as { elements?: unknown }).elements) ||
|
|
113
|
-
typeof (payload as { totalElements?: unknown }).totalElements !== "number" ||
|
|
114
|
-
typeof (payload as { pageNumber?: unknown }).pageNumber !== "number" ||
|
|
115
|
-
typeof (payload as { rowsPerPage?: unknown }).rowsPerPage !== "number"
|
|
116
|
-
) {
|
|
115
|
+
if (!isPagePayload(payload)) {
|
|
117
116
|
throw createJapanAddressError(
|
|
118
117
|
"bad_response",
|
|
119
118
|
"Response payload must include a valid page payload",
|
|
120
119
|
);
|
|
121
120
|
}
|
|
122
121
|
|
|
123
|
-
return payload
|
|
122
|
+
return payload;
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
const dataSource: JapanAddressDataSource = {
|
|
127
|
-
|
|
128
|
-
return readPage(
|
|
126
|
+
lookupPostalCode(request, options) {
|
|
127
|
+
return readPage("/q/japanpost/searchcode", request, options);
|
|
129
128
|
},
|
|
130
|
-
|
|
131
|
-
return readPage(
|
|
129
|
+
searchAddress(request, options) {
|
|
130
|
+
return readPage("/q/japanpost/addresszip", request, options);
|
|
132
131
|
},
|
|
133
132
|
};
|
|
134
133
|
|
|
135
|
-
export function
|
|
134
|
+
export function PostalCodeLookupExample() {
|
|
136
135
|
const { loading, data, error, search } = useJapanPostalCode({ dataSource });
|
|
137
136
|
|
|
138
137
|
return (
|
|
139
138
|
<div>
|
|
140
|
-
<
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
{
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
{
|
|
149
|
-
|
|
150
|
-
|
|
139
|
+
<PostalCodeInput
|
|
140
|
+
buttonLabel="Search"
|
|
141
|
+
label="Postal code"
|
|
142
|
+
onSearch={(postalCode) => {
|
|
143
|
+
void search({ postalCode });
|
|
144
|
+
}}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
{loading ? <p>Loading...</p> : null}
|
|
148
|
+
{error ? <p>{error.message}</p> : null}
|
|
149
|
+
|
|
150
|
+
<ul>
|
|
151
|
+
{(data?.elements ?? []).map((address) => (
|
|
152
|
+
<li key={`${address.postalCode}-${address.address}`}>
|
|
153
|
+
{address.address}
|
|
154
|
+
</li>
|
|
155
|
+
))}
|
|
156
|
+
</ul>
|
|
151
157
|
</div>
|
|
152
158
|
);
|
|
153
159
|
}
|
|
154
160
|
```
|
|
155
161
|
|
|
156
|
-
The
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
162
|
+
The example paths above match this repository's reference backend. In your own
|
|
163
|
+
app, the backend routes can be different as long as your `dataSource`
|
|
164
|
+
implementation returns the same public types.
|
|
165
|
+
|
|
166
|
+
## Core Contract
|
|
167
|
+
|
|
168
|
+
`Page<T>` is the result shape shared by the hooks and the reference backend:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
type Page<T> = {
|
|
172
|
+
elements: T[];
|
|
173
|
+
totalElements: number;
|
|
174
|
+
pageNumber: number;
|
|
175
|
+
rowsPerPage: number;
|
|
176
|
+
};
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`JapanAddress` is the normalized address shape returned by the package:
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
type JapanAddress = {
|
|
183
|
+
postalCode: string;
|
|
184
|
+
prefecture: string;
|
|
185
|
+
prefectureKana?: string;
|
|
186
|
+
city: string;
|
|
187
|
+
cityKana?: string;
|
|
188
|
+
town: string;
|
|
189
|
+
townKana?: string;
|
|
190
|
+
address: string;
|
|
191
|
+
provider: "japan-post";
|
|
192
|
+
};
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The hooks keep this page payload as-is, so consumers read
|
|
196
|
+
`data?.elements`, `data?.totalElements`, `data?.pageNumber`, and
|
|
197
|
+
`data?.rowsPerPage` directly.
|
|
183
198
|
|
|
184
199
|
## Hooks
|
|
185
200
|
|
|
186
|
-
### useJapanPostalCode
|
|
201
|
+
### `useJapanPostalCode`
|
|
187
202
|
|
|
188
|
-
|
|
189
|
-
|
|
203
|
+
- Accepts `string` or `JapanPostalCodeSearchInput`
|
|
204
|
+
- Normalizes the input to digits before calling the data source
|
|
205
|
+
- Allows `3-7` digits, so prefix lookup is possible
|
|
206
|
+
- Builds `{ postalCode, pageNumber: 0, rowsPerPage: 100 }` by default
|
|
207
|
+
- Exposes `loading`, `data`, `error`, `search`, `cancel`, and `reset`
|
|
190
208
|
|
|
191
209
|
```tsx
|
|
192
|
-
const
|
|
193
|
-
|
|
210
|
+
const postalCode = useJapanPostalCode({ dataSource });
|
|
211
|
+
|
|
212
|
+
void postalCode.search("100-0001");
|
|
213
|
+
void postalCode.search({
|
|
214
|
+
postalCode: "1000001",
|
|
215
|
+
pageNumber: 1,
|
|
216
|
+
rowsPerPage: 10,
|
|
217
|
+
includeParenthesesTown: true,
|
|
194
218
|
});
|
|
195
219
|
```
|
|
196
220
|
|
|
197
|
-
### useJapanAddressSearch
|
|
221
|
+
### `useJapanAddressSearch`
|
|
198
222
|
|
|
199
|
-
|
|
223
|
+
- Accepts `string` or `JapanAddressSearchInput`
|
|
224
|
+
- Supports free-form search and structured fields in the same request type
|
|
225
|
+
- Rejects a fully blank query before calling the data source
|
|
226
|
+
- Omits `includeCityDetails` and `includePrefectureDetails` unless you set them
|
|
227
|
+
- Supports `debounceMs`
|
|
228
|
+
- Exposes `loading`, `data`, `error`, `search`, `cancel`, and `reset`
|
|
200
229
|
|
|
201
230
|
```tsx
|
|
202
|
-
const
|
|
231
|
+
const addressSearch = useJapanAddressSearch({
|
|
203
232
|
dataSource,
|
|
204
233
|
debounceMs: 300,
|
|
205
234
|
});
|
|
206
|
-
```
|
|
207
235
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
236
|
+
void addressSearch.search("千代田");
|
|
237
|
+
void addressSearch.search({
|
|
238
|
+
prefName: "東京都",
|
|
239
|
+
cityName: "千代田区",
|
|
240
|
+
pageNumber: 0,
|
|
241
|
+
rowsPerPage: 10,
|
|
242
|
+
});
|
|
243
|
+
```
|
|
212
244
|
|
|
213
|
-
### useJapanAddress
|
|
245
|
+
### `useJapanAddress`
|
|
214
246
|
|
|
215
|
-
Combines postal-code lookup and
|
|
247
|
+
- Combines postal-code lookup and address search in one hook
|
|
248
|
+
- Reuses the same `dataSource`
|
|
249
|
+
- Exposes `searchByPostalCode`, `searchByAddressQuery`, and `reset`
|
|
250
|
+
- Returns `data` and `error` for the currently active search mode only
|
|
216
251
|
|
|
217
252
|
```tsx
|
|
218
|
-
const
|
|
219
|
-
|
|
253
|
+
const address = useJapanAddress({
|
|
254
|
+
dataSource,
|
|
255
|
+
debounceMs: 300,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
void address.searchByPostalCode("1000001");
|
|
259
|
+
void address.searchByAddressQuery({
|
|
260
|
+
addressQuery: "千代田",
|
|
261
|
+
pageNumber: 0,
|
|
262
|
+
rowsPerPage: 10,
|
|
263
|
+
});
|
|
220
264
|
```
|
|
221
265
|
|
|
222
|
-
|
|
266
|
+
## Headless Components
|
|
267
|
+
|
|
268
|
+
### `PostalCodeInput`
|
|
269
|
+
|
|
270
|
+
- Renders a `<form>` with `<label>`, `<input>`, and `<button>`
|
|
271
|
+
- Supports controlled and uncontrolled usage
|
|
272
|
+
- Calls `onSearch` with a normalized digits-only postal code
|
|
273
|
+
- Defaults `inputMode="numeric"` unless overridden with `inputProps`
|
|
223
274
|
|
|
224
|
-
|
|
275
|
+
### `AddressSearchInput`
|
|
225
276
|
|
|
226
|
-
-
|
|
227
|
-
-
|
|
228
|
-
- `
|
|
229
|
-
- `useJapanAddress().searchByKeyword(query: string)`
|
|
277
|
+
- Renders the same minimal form structure
|
|
278
|
+
- Supports controlled and uncontrolled usage
|
|
279
|
+
- Calls `onSearch` with a trimmed query string
|
|
230
280
|
|
|
231
|
-
|
|
281
|
+
Both components accept:
|
|
232
282
|
|
|
233
|
-
-
|
|
234
|
-
-
|
|
283
|
+
- `inputProps` for the rendered `<input>`
|
|
284
|
+
- `buttonProps` for the rendered `<button>`
|
|
235
285
|
|
|
236
|
-
|
|
237
|
-
`includePrefectureDetails` are omitted unless your own data source
|
|
238
|
-
implementation sets them explicitly.
|
|
286
|
+
## Data Source Integration
|
|
239
287
|
|
|
240
|
-
|
|
288
|
+
The package exports types for both sides of the integration:
|
|
241
289
|
|
|
242
|
-
`JapanAddressDataSource`
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
`
|
|
290
|
+
- `JapanAddressDataSource`
|
|
291
|
+
- `JapanPostSearchcodeRequest`
|
|
292
|
+
- `JapanPostAddresszipRequest`
|
|
293
|
+
- `JapanPostalCodeSearchInput`
|
|
294
|
+
- `JapanAddressSearchInput`
|
|
295
|
+
- `JapanAddressRequestOptions`
|
|
246
296
|
|
|
247
|
-
|
|
297
|
+
The optional second argument to each data-source method is:
|
|
248
298
|
|
|
249
299
|
```ts
|
|
250
300
|
type JapanAddressRequestOptions = {
|
|
@@ -252,63 +302,29 @@ type JapanAddressRequestOptions = {
|
|
|
252
302
|
};
|
|
253
303
|
```
|
|
254
304
|
|
|
255
|
-
|
|
256
|
-
`reset()` calls, and unmount cleanup
|
|
257
|
-
|
|
258
|
-
Recommended error-code mapping:
|
|
259
|
-
|
|
260
|
-
| Situation | Recommended code |
|
|
261
|
-
| --- | --- |
|
|
262
|
-
| Invalid postal code input | `invalid_postal_code` |
|
|
263
|
-
| Blank keyword input in hook-side pre-validation | `invalid_query` |
|
|
264
|
-
| Network failure | `network_error` |
|
|
265
|
-
| Request aborted / timeout | `timeout` |
|
|
266
|
-
| No matching addresses on backends that surface misses as errors | `not_found` |
|
|
267
|
-
| Malformed success payload | `bad_response` |
|
|
268
|
-
| Other backend failures | `data_source_error` |
|
|
269
|
-
|
|
270
|
-
In this repository's reference demo flow, the sample `dataSource` classifies
|
|
271
|
-
failed requests by HTTP status code only. Current beta-compatible flows may
|
|
272
|
-
return `200` with an empty page for both blank `addresszip` requests and
|
|
273
|
-
postal-code misses, and those should stay successful page results. Other
|
|
274
|
-
`400` responses can still map to `invalid_query`, `404` remains useful for
|
|
275
|
-
backends that intentionally surface misses as errors, and `504` maps to
|
|
276
|
-
`timeout`.
|
|
277
|
-
|
|
278
|
-
## Headless Components
|
|
279
|
-
|
|
280
|
-
`PostalCodeInput` and `AddressSearchInput` provide behavior and DOM structure
|
|
281
|
-
without bundled styles, so you can plug them into your own design system.
|
|
282
|
-
|
|
283
|
-
Both components also support native prop passthrough:
|
|
284
|
-
|
|
285
|
-
- `inputProps`: forwarded to the rendered `<input />`
|
|
286
|
-
- `buttonProps`: forwarded to the rendered `<button />`
|
|
287
|
-
|
|
288
|
-
Use these for `id`, `name`, `placeholder`, `aria-*`, `autoComplete`,
|
|
289
|
-
`className`, and form integration. `PostalCodeInput` defaults to
|
|
290
|
-
`inputMode="numeric"` unless you override it through `inputProps`.
|
|
291
|
-
|
|
292
|
-
## Data Source and Server Integration
|
|
305
|
+
The hooks pass `signal` so your data source can cancel superseded requests,
|
|
306
|
+
`cancel()` calls, `reset()` calls, and unmount cleanup.
|
|
293
307
|
|
|
294
|
-
|
|
295
|
-
uses token-based authentication, so browser apps should not hold upstream
|
|
296
|
-
credentials directly. The supported integration model is a real server-backed
|
|
297
|
-
flow.
|
|
308
|
+
This repository's reference backend uses these routes:
|
|
298
309
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
integration testing. The demo's `/minimal-api` path is only a development-time
|
|
302
|
-
route to that local server. When the upstream payload includes both structured
|
|
303
|
-
address parts and a free-form `address` string, the reference server keeps the
|
|
304
|
-
display address non-duplicated instead of concatenating both blindly.
|
|
310
|
+
- `POST /q/japanpost/searchcode`
|
|
311
|
+
- `POST /q/japanpost/addresszip`
|
|
305
312
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
`timeout` error code.
|
|
313
|
+
But those route names are not part of the package API. They are just the
|
|
314
|
+
example used by `apps/demo` and `apps/minimal-api`.
|
|
309
315
|
|
|
310
|
-
##
|
|
316
|
+
## Constraints And Notes
|
|
311
317
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
318
|
+
- `dataSource` is required at runtime for all hooks.
|
|
319
|
+
- `isValidJapanPostalCode()` checks for an exact 7-digit postal code after
|
|
320
|
+
normalization. `useJapanPostalCode()` is less strict and accepts `3-7`
|
|
321
|
+
digits for prefix lookup.
|
|
322
|
+
- `formatJapanPostalCode()` inserts a hyphen only when the normalized value is
|
|
323
|
+
exactly 7 digits.
|
|
324
|
+
- `cancel()` on `useJapanPostalCode()` and `useJapanAddressSearch()` aborts the
|
|
325
|
+
in-flight request but keeps the latest settled `data` and `error`.
|
|
326
|
+
- `reset()` clears both `data` and `error`.
|
|
327
|
+
- The package does not require a backend to return `404` for misses. Returning
|
|
328
|
+
`200` with an empty page is also compatible with the hook contract.
|
|
329
|
+
- Use your own server-side API in the `dataSource` implementation. Keep Japan
|
|
330
|
+
Post credentials and token exchange on the server side.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { PostalCodeInput } from './components/PostalCodeInput';
|
|
3
|
-
export { createJapanAddressError } from './core/errors';
|
|
4
|
-
export { formatJapanPostalCode, normalizeJapanPostalCode, } from './core/formatters';
|
|
5
|
-
export { normalizeJapanPostAddressRecord } from './core/normalizers';
|
|
6
|
-
export type { AddressSearchInputProps, JapanAddress, JapanAddressDataSource, JapanAddressError, JapanAddressErrorCode, JapanPostAddresszipRequest, JapanPostSearchcodeRequest, JapanAddressRequestOptions, JapanAddressSearchResult, JapanPostalCodeLookupResult, NormalizedJapanAddressRecord, Page, PostalCodeInputProps, UseJapanAddressOptions, UseJapanAddressResult, UseJapanAddressSearchOptions, UseJapanAddressSearchResult, UseJapanPostalCodeOptions, UseJapanPostalCodeResult, } from './core/types';
|
|
7
|
-
export { isValidJapanPostalCode } from './core/validators';
|
|
8
|
-
export { useJapanAddress } from './react/useJapanAddress';
|
|
9
|
-
export { useJapanAddressSearch } from './react/useJapanAddressSearch';
|
|
10
|
-
export { useJapanPostalCode } from './react/useJapanPostalCode';
|
|
11
|
-
//# sourceMappingURL=index.d.ts.map
|
|
1
|
+
export {}
|