@cp949/japanpost-react 1.0.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/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026 cp949
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,500 @@
1
+ <!-- This file is generated by `pnpm readme:package`. Edit docs/README.en.md and docs/README.ko.md instead. -->
2
+
3
+ # @cp949/japanpost-react
4
+
5
+ [English](#english) | [한국어](#한국어)
6
+
7
+ ---
8
+
9
+ ## English
10
+
11
+ React + TypeScript hooks and headless inputs for Japan postal-code and address
12
+ lookup.
13
+
14
+ This package guide covers the published library. Repository-level demo scripts
15
+ such as `pnpm demo:full` currently target Linux/WSL-style shell environments
16
+ and are documented in the root README.
17
+
18
+ ## Install
19
+
20
+ ```bash
21
+ pnpm add @cp949/japanpost-react
22
+ ```
23
+
24
+ - Supported React versions: React 18 and React 19
25
+
26
+ ## Quick Start
27
+
28
+ ```tsx
29
+ import { useJapanPostalCode } from "@cp949/japanpost-react";
30
+ import type { JapanAddressDataSource } from "@cp949/japanpost-react";
31
+ import { createJapanAddressError } from "@cp949/japanpost-react";
32
+
33
+ // The only supported integration model is a real server-backed flow.
34
+ // Point the data source at your own backend API.
35
+ const dataSource: JapanAddressDataSource = {
36
+ async lookupPostalCode(postalCode) {
37
+ const res = await fetch(`/searchcode/${postalCode}`);
38
+ if (!res.ok) {
39
+ const message = `Postal code lookup failed with status ${res.status}`;
40
+
41
+ if (res.status === 400) {
42
+ throw createJapanAddressError("invalid_postal_code", message, {
43
+ status: res.status,
44
+ });
45
+ }
46
+
47
+ if (res.status === 404) {
48
+ throw createJapanAddressError("not_found", message, {
49
+ status: res.status,
50
+ });
51
+ }
52
+
53
+ if (res.status === 504) {
54
+ throw createJapanAddressError("timeout", message, {
55
+ status: res.status,
56
+ });
57
+ }
58
+
59
+ throw createJapanAddressError("data_source_error", message, {
60
+ status: res.status,
61
+ });
62
+ }
63
+ const payload = await res.json();
64
+ if (!Array.isArray(payload.addresses)) {
65
+ throw createJapanAddressError(
66
+ "bad_response",
67
+ "Postal code lookup returned an invalid payload",
68
+ );
69
+ }
70
+ return payload.addresses;
71
+ },
72
+ async searchAddress(query) {
73
+ const res = await fetch(`/addresszip?q=${encodeURIComponent(query)}`);
74
+ if (!res.ok) {
75
+ const message = `Address search failed with status ${res.status}`;
76
+
77
+ if (res.status === 400) {
78
+ throw createJapanAddressError("invalid_query", message, {
79
+ status: res.status,
80
+ });
81
+ }
82
+
83
+ if (res.status === 404) {
84
+ throw createJapanAddressError("not_found", message, {
85
+ status: res.status,
86
+ });
87
+ }
88
+
89
+ if (res.status === 504) {
90
+ throw createJapanAddressError("timeout", message, {
91
+ status: res.status,
92
+ });
93
+ }
94
+
95
+ throw createJapanAddressError("data_source_error", message, {
96
+ status: res.status,
97
+ });
98
+ }
99
+ const payload = await res.json();
100
+ if (!Array.isArray(payload.addresses)) {
101
+ throw createJapanAddressError(
102
+ "bad_response",
103
+ "Address search returned an invalid payload",
104
+ );
105
+ }
106
+ return payload.addresses;
107
+ },
108
+ };
109
+
110
+ export function PostalForm() {
111
+ const { loading, data, error, search } = useJapanPostalCode({ dataSource });
112
+
113
+ return (
114
+ <div>
115
+ <button onClick={() => void search("100-0001")}>Search</button>
116
+ {loading && <p>Loading...</p>}
117
+ {error && (
118
+ <p>
119
+ {error.code}: {error.message}
120
+ </p>
121
+ )}
122
+ {data?.addresses.map((addr) => (
123
+ <p key={addr.postalCode + addr.address}>{addr.address}</p>
124
+ ))}
125
+ </div>
126
+ );
127
+ }
128
+ ```
129
+
130
+ ## Exports
131
+
132
+ - `normalizeJapanPostalCode`
133
+ - `formatJapanPostalCode`
134
+ - `normalizeJapanPostAddressRecord`
135
+ - `isValidJapanPostalCode`
136
+ - `createJapanAddressError`
137
+ - `useJapanPostalCode`
138
+ - `useJapanAddressSearch`
139
+ - `useJapanAddress`
140
+ - `PostalCodeInput`
141
+ - `AddressSearchInput`
142
+ - Public types including `JapanAddress` and `JapanAddressDataSource`
143
+ - Request options type: `JapanAddressRequestOptions`
144
+
145
+ ## Utility Notes
146
+
147
+ `formatJapanPostalCode()` inserts a hyphen only when the normalized value is
148
+ exactly 7 digits. For any other length, it returns the normalized digits
149
+ without inserting a hyphen.
150
+
151
+ ## Hooks
152
+
153
+ ### useJapanPostalCode
154
+
155
+ Looks up addresses by postal code.
156
+
157
+ ```tsx
158
+ const { loading, data, error, search, reset } = useJapanPostalCode({
159
+ dataSource,
160
+ });
161
+ ```
162
+
163
+ ### useJapanAddressSearch
164
+
165
+ Searches addresses by free-form keyword and supports debouncing.
166
+
167
+ ```tsx
168
+ const { loading, data, error, search, reset } = useJapanAddressSearch({
169
+ dataSource,
170
+ debounceMs: 300,
171
+ });
172
+ ```
173
+
174
+ ### useJapanAddress
175
+
176
+ Combines postal-code lookup and keyword search into one hook.
177
+
178
+ ```tsx
179
+ const { loading, data, error, searchByPostalCode, searchByKeyword, reset } =
180
+ useJapanAddress({ dataSource, debounceMs: 300 });
181
+ ```
182
+
183
+ All hooks require `dataSource` at runtime.
184
+
185
+ ## Error Handling Notes
186
+
187
+ `JapanAddressDataSource` should return `JapanAddress[]` directly from both
188
+ methods. The hooks wrap those arrays into lookup/search result objects.
189
+
190
+ Both methods may also receive an optional second argument:
191
+
192
+ ```ts
193
+ type JapanAddressRequestOptions = {
194
+ signal?: AbortSignal;
195
+ };
196
+ ```
197
+
198
+ Hooks pass `signal` so your data source can cancel superseded requests,
199
+ `reset()` calls, and unmount cleanup when your backend layer supports aborts.
200
+
201
+ Recommended error-code mapping:
202
+
203
+ | Situation | Recommended code |
204
+ | --- | --- |
205
+ | Invalid postal code input | `invalid_postal_code` |
206
+ | Blank keyword input | `invalid_query` |
207
+ | Network failure | `network_error` |
208
+ | Request aborted / timeout | `timeout` |
209
+ | No matching addresses | `not_found` |
210
+ | Malformed success payload | `bad_response` |
211
+ | Other backend failures | `data_source_error` |
212
+
213
+ In this repository's reference demo flow, the sample `dataSource` maps `400
214
+ /searchcode/...` to `invalid_postal_code`, `400 /addresszip?...` to
215
+ `invalid_query`, `404` to `not_found`, and `504` to `timeout`.
216
+
217
+ ## Headless Components
218
+
219
+ `PostalCodeInput` and `AddressSearchInput` provide behavior and DOM structure
220
+ without bundled styles, so you can plug them into your own design system.
221
+
222
+ Both components also support native prop passthrough:
223
+
224
+ - `inputProps`: forwarded to the rendered `<input />`
225
+ - `buttonProps`: forwarded to the rendered `<button />`
226
+
227
+ Use these for `id`, `name`, `placeholder`, `aria-*`, `autoComplete`,
228
+ `className`, and form integration. `PostalCodeInput` defaults to
229
+ `inputMode="numeric"` unless you override it through `inputProps`.
230
+
231
+ ## Data Source and Server Integration
232
+
233
+ Use this package with your own backend server. The official Japan Post flow
234
+ uses token-based authentication, so browser apps should not hold upstream
235
+ credentials directly. The supported integration model is a real server-backed
236
+ flow.
237
+
238
+ This repository includes `apps/minimal-api` as the reference local server. It
239
+ wraps Japan Post API ver 2.0 and is intended for local development and
240
+ integration testing. The demo's `/minimal-api` path is only a development-time
241
+ route to that local server. When the upstream payload includes both structured
242
+ address parts and a free-form `address` string, the reference server keeps the
243
+ display address non-duplicated instead of concatenating both blindly.
244
+
245
+ Timeout messages can differ depending on whether the token exchange timed out or
246
+ the upstream lookup request timed out. Both cases still map cleanly to the
247
+ `timeout` error code.
248
+
249
+ ## SSR
250
+
251
+ Use your server-side API from the `dataSource` implementation, and keep token
252
+ exchange plus upstream signing on the server. React hooks and UI components
253
+ should stay in client components.
254
+
255
+ ---
256
+
257
+ ## 한국어
258
+
259
+ React + TypeScript 기반의 일본 우편번호/주소 검색 훅과 headless 입력
260
+ 컴포넌트 라이브러리입니다.
261
+
262
+ 이 문서는 배포 패키지 사용 가이드입니다. `pnpm demo:full` 같은 저장소 수준 보조
263
+ 스크립트는 현재 Linux/WSL 계열 셸 환경을 전제로 하며, 자세한 내용은 루트 README에서 다룹니다.
264
+
265
+ ## 설치
266
+
267
+ ```bash
268
+ pnpm add @cp949/japanpost-react
269
+ ```
270
+
271
+ - 지원 React 버전: React 18, React 19
272
+
273
+ ## 빠른 시작
274
+
275
+ ```tsx
276
+ import { useJapanPostalCode } from "@cp949/japanpost-react";
277
+ import type { JapanAddressDataSource } from "@cp949/japanpost-react";
278
+ import { createJapanAddressError } from "@cp949/japanpost-react";
279
+
280
+ // 현재 지원 방식은 실제 서버 연동뿐입니다.
281
+ // 앱의 백엔드 API 경로에 맞게 dataSource를 연결하세요.
282
+ const dataSource: JapanAddressDataSource = {
283
+ async lookupPostalCode(postalCode) {
284
+ const res = await fetch(`/searchcode/${postalCode}`);
285
+ if (!res.ok) {
286
+ const message = `Postal code lookup failed with status ${res.status}`;
287
+
288
+ if (res.status === 400) {
289
+ throw createJapanAddressError("invalid_postal_code", message, {
290
+ status: res.status,
291
+ });
292
+ }
293
+
294
+ if (res.status === 404) {
295
+ throw createJapanAddressError("not_found", message, {
296
+ status: res.status,
297
+ });
298
+ }
299
+
300
+ if (res.status === 504) {
301
+ throw createJapanAddressError("timeout", message, {
302
+ status: res.status,
303
+ });
304
+ }
305
+
306
+ throw createJapanAddressError("data_source_error", message, {
307
+ status: res.status,
308
+ });
309
+ }
310
+ const payload = await res.json();
311
+ if (!Array.isArray(payload.addresses)) {
312
+ throw createJapanAddressError(
313
+ "bad_response",
314
+ "Postal code lookup returned an invalid payload",
315
+ );
316
+ }
317
+ return payload.addresses;
318
+ },
319
+ async searchAddress(query) {
320
+ const res = await fetch(`/addresszip?q=${encodeURIComponent(query)}`);
321
+ if (!res.ok) {
322
+ const message = `Address search failed with status ${res.status}`;
323
+
324
+ if (res.status === 400) {
325
+ throw createJapanAddressError("invalid_query", message, {
326
+ status: res.status,
327
+ });
328
+ }
329
+
330
+ if (res.status === 404) {
331
+ throw createJapanAddressError("not_found", message, {
332
+ status: res.status,
333
+ });
334
+ }
335
+
336
+ if (res.status === 504) {
337
+ throw createJapanAddressError("timeout", message, {
338
+ status: res.status,
339
+ });
340
+ }
341
+
342
+ throw createJapanAddressError("data_source_error", message, {
343
+ status: res.status,
344
+ });
345
+ }
346
+ const payload = await res.json();
347
+ if (!Array.isArray(payload.addresses)) {
348
+ throw createJapanAddressError(
349
+ "bad_response",
350
+ "Address search returned an invalid payload",
351
+ );
352
+ }
353
+ return payload.addresses;
354
+ },
355
+ };
356
+
357
+ export function PostalForm() {
358
+ const { loading, data, error, search } = useJapanPostalCode({ dataSource });
359
+
360
+ return (
361
+ <div>
362
+ <button onClick={() => void search("100-0001")}>조회</button>
363
+ {loading && <p>조회 중...</p>}
364
+ {error && (
365
+ <p>
366
+ {error.code}: {error.message}
367
+ </p>
368
+ )}
369
+ {data?.addresses.map((addr) => (
370
+ <p key={addr.postalCode + addr.address}>{addr.address}</p>
371
+ ))}
372
+ </div>
373
+ );
374
+ }
375
+ ```
376
+
377
+ ## Exports
378
+
379
+ - `normalizeJapanPostalCode`
380
+ - `formatJapanPostalCode`
381
+ - `normalizeJapanPostAddressRecord`
382
+ - `isValidJapanPostalCode`
383
+ - `createJapanAddressError`
384
+ - `useJapanPostalCode`
385
+ - `useJapanAddressSearch`
386
+ - `useJapanAddress`
387
+ - `PostalCodeInput`
388
+ - `AddressSearchInput`
389
+ - `JapanAddress`, `JapanAddressDataSource`를 포함한 공개 타입
390
+ - 요청 옵션 타입: `JapanAddressRequestOptions`
391
+
392
+ ## 유틸리티 메모
393
+
394
+ `formatJapanPostalCode()`는 정규화된 값이 정확히 7자리일 때만 하이픈을 넣습니다.
395
+ 그 외 길이에서는 하이픈을 추가하지 않고 숫자만 남긴 값을 그대로 반환합니다.
396
+
397
+ ## Hooks
398
+
399
+ ### useJapanPostalCode
400
+
401
+ 우편번호로 주소를 조회합니다.
402
+
403
+ ```tsx
404
+ const { loading, data, error, search, reset } = useJapanPostalCode({
405
+ dataSource,
406
+ });
407
+ ```
408
+
409
+ ### useJapanAddressSearch
410
+
411
+ 자유 형식 키워드로 주소를 검색하며 `debounceMs`를 지원합니다.
412
+
413
+ ```tsx
414
+ const { loading, data, error, search, reset } = useJapanAddressSearch({
415
+ dataSource,
416
+ debounceMs: 300,
417
+ });
418
+ ```
419
+
420
+ ### useJapanAddress
421
+
422
+ 우편번호 조회와 키워드 검색을 하나의 훅으로 합칩니다.
423
+
424
+ ```tsx
425
+ const { loading, data, error, searchByPostalCode, searchByKeyword, reset } =
426
+ useJapanAddress({ dataSource, debounceMs: 300 });
427
+ ```
428
+
429
+ 모든 훅은 런타임에서 `dataSource`가 필요합니다.
430
+
431
+ ## 에러 처리 메모
432
+
433
+ `JapanAddressDataSource`의 두 메서드는 모두 `JapanAddress[]`를 직접
434
+ 반환해야 합니다. 각 훅은 그 배열을 받아 `{ postalCode, addresses }`,
435
+ `{ query, addresses }` 형태의 결과 객체를 조합합니다.
436
+
437
+ 두 메서드는 선택적인 두 번째 인자도 받을 수 있습니다.
438
+
439
+ ```ts
440
+ type JapanAddressRequestOptions = {
441
+ signal?: AbortSignal;
442
+ };
443
+ ```
444
+
445
+ 훅은 superseded 요청, `reset()`, unmount 정리 상황에서 이전 요청을 취소할 수
446
+ 있도록 `signal`을 전달합니다. 백엔드 레이어가 abort를 지원하면 그대로 활용할 수
447
+ 있습니다.
448
+
449
+ 권장 에러 코드 매핑:
450
+
451
+ | 상황 | 권장 코드 |
452
+ | --- | --- |
453
+ | 잘못된 우편번호 입력 | `invalid_postal_code` |
454
+ | 빈 주소 검색어 | `invalid_query` |
455
+ | 네트워크 실패 | `network_error` |
456
+ | 요청 중단 / 타임아웃 | `timeout` |
457
+ | 검색 결과 없음 | `not_found` |
458
+ | 성공 응답 shape 이상 | `bad_response` |
459
+ | 그 외 백엔드 오류 | `data_source_error` |
460
+
461
+ 이 저장소의 참고 demo 흐름에서는 예시 `dataSource`가 `400 /searchcode/...`를
462
+ `invalid_postal_code`, `400 /addresszip?...`를 `invalid_query`, `404`를
463
+ `not_found`, `504`를 `timeout`으로 매핑합니다.
464
+
465
+ ## Headless 컴포넌트
466
+
467
+ `PostalCodeInput`, `AddressSearchInput`은 스타일 없이 동작과 DOM 구조만
468
+ 제공하므로, 앱의 디자인 시스템에 맞게 직접 꾸밀 수 있습니다.
469
+
470
+ 두 컴포넌트는 네이티브 props 전달도 지원합니다.
471
+
472
+ - `inputProps`: 실제 `<input />`에 전달
473
+ - `buttonProps`: 실제 `<button />`에 전달
474
+
475
+ 따라서 `id`, `name`, `placeholder`, `aria-*`, `autoComplete`, `className`,
476
+ 폼 연동용 속성을 직접 넘길 수 있습니다. `PostalCodeInput`은 별도 override가
477
+ 없으면 기본적으로 `inputMode="numeric"`를 사용합니다.
478
+
479
+ ## Data Source와 서버 연동
480
+
481
+ 이 패키지는 자체 백엔드 서버와 함께 사용하는 것을 권장합니다. Japan Post
482
+ 공식 연동은 토큰 기반 인증을 사용하므로, 브라우저에서 업스트림 자격증명을
483
+ 직접 보관하면 안 됩니다. 현재 지원 방식은 실제 서버 연동뿐입니다.
484
+
485
+ 이 저장소의 `apps/minimal-api`는 로컬 기준 참고 서버 구현입니다. Japan Post
486
+ API ver 2.0을 감싸며, 로컬 개발과 통합 확인 용도로 쓰는 구성을 목표로
487
+ 합니다. demo의 `/minimal-api` 경로는 개발 편의를 위한 로컬 경로 연결입니다.
488
+ 업스트림 payload에 구조화된 주소 필드와 원본 전체 주소 문자열인 `address`가 함께
489
+ 있더라도, 참고 서버는 둘을 그대로 이어붙이지 않고 중복 없는 표시 주소를
490
+ 우선 사용합니다.
491
+
492
+ timeout 메시지는 토큰 발급 단계와 실제 조회 단계 중 어느 쪽에서 timeout이
493
+ 발생했는지에 따라 달라질 수 있지만, 두 경우 모두 `timeout` 코드로 다루면
494
+ 됩니다.
495
+
496
+ ## SSR
497
+
498
+ `dataSource` 구현에서는 서버 측 API를 사용하고, 토큰 교환과 업스트림 서명은
499
+ 서버에서만 처리하세요. React 훅과 UI 컴포넌트는 클라이언트 컴포넌트에서
500
+ 사용하는 것이 안전합니다.
@@ -0,0 +1,7 @@
1
+ import { AddressSearchInputProps } from '../core/types';
2
+ /**
3
+ * 스타일 의존성이 없는 최소한의 주소 키워드 검색 입력 컴포넌트.
4
+ * value를 전달하면 제어 모드, 전달하지 않으면 비제어 모드로 동작한다.
5
+ */
6
+ export declare function AddressSearchInput({ defaultValue, value, disabled, label, buttonLabel, inputProps, buttonProps, onChange, onSearch, }: AddressSearchInputProps): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=AddressSearchInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AddressSearchInput.d.ts","sourceRoot":"","sources":["../../src/components/AddressSearchInput.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,YAAiB,EACjB,KAAK,EACL,QAAQ,EACR,KAAyB,EACzB,WAAsB,EACtB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,QAAQ,GACT,EAAE,uBAAuB,2CA4CzB"}
@@ -0,0 +1,7 @@
1
+ import { PostalCodeInputProps } from '../core/types';
2
+ /**
3
+ * 스타일 의존성이 없는 최소한의 우편번호 입력 컴포넌트.
4
+ * value를 전달하면 제어 모드, 전달하지 않으면 비제어 모드로 동작한다.
5
+ */
6
+ export declare function PostalCodeInput({ defaultValue, value, disabled, label, buttonLabel, inputProps, buttonProps, onChange, onSearch, }: PostalCodeInputProps): import("react/jsx-runtime").JSX.Element;
7
+ //# sourceMappingURL=PostalCodeInput.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostalCodeInput.d.ts","sourceRoot":"","sources":["../../src/components/PostalCodeInput.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,EAC9B,YAAiB,EACjB,KAAK,EACL,QAAQ,EACR,KAAqB,EACrB,WAAsB,EACtB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,QAAQ,GACT,EAAE,oBAAoB,2CA6CtB"}
@@ -0,0 +1,10 @@
1
+ import { JapanAddressError, JapanAddressErrorCode } from './types';
2
+ /**
3
+ * 라이브러리 전용 에러 객체를 생성한다.
4
+ * name과 code를 일관되게 설정해 catch 블록에서 타입 좁히기가 쉽도록 한다.
5
+ */
6
+ export declare function createJapanAddressError(code: JapanAddressErrorCode, message: string, options?: {
7
+ cause?: unknown;
8
+ status?: number;
9
+ }): JapanAddressError;
10
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/core/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAExE;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,qBAAqB,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7C,iBAAiB,CAQnB"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 우편번호에서 숫자가 아닌 문자(하이픈 등)를 제거해 순수 숫자 문자열로 만든다.
3
+ * 예: "123-4567" → "1234567"
4
+ */
5
+ export declare function normalizeJapanPostalCode(value: string): string;
6
+ /**
7
+ * 우편번호를 "NNN-NNNN" 형식으로 포맷한다.
8
+ * 정확히 7자리가 아닐 때는 하이픈 없이 숫자만 반환한다.
9
+ * 예: "1234567" → "123-4567"
10
+ */
11
+ export declare function formatJapanPostalCode(value: string): string;
12
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../../src/core/formatters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAW3D"}
@@ -0,0 +1,7 @@
1
+ import { JapanAddress, NormalizedJapanAddressRecord } from './types';
2
+ /**
3
+ * 정규화된 내부 주소 레코드를 라이브러리 공개 JapanAddress 형태로 변환한다.
4
+ * address는 도도부현·시구정촌·동·상세주소를 순서대로 이어붙인 문자열이다.
5
+ */
6
+ export declare function normalizeJapanPostAddressRecord(record: NormalizedJapanAddressRecord): JapanAddress;
7
+ //# sourceMappingURL=normalizers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizers.d.ts","sourceRoot":"","sources":["../../src/core/normalizers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAU1E;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,4BAA4B,GACnC,YAAY,CAoBd"}