@01.software/sdk 0.1.0-dev.260109.7cf07c9 → 0.1.0-dev.260206.8918543

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,58 +1,42 @@
1
1
  # @01.software/sdk
2
2
 
3
- 01.software 플랫폼의 공식 TypeScript SDK입니다. Supabase 스타일의 직관적인 API와 강력한 타입 안전성을 제공하며, 브라우저와 서버 환경을 모두 지원합니다.
3
+ Official TypeScript SDK for the 01.software platform.
4
4
 
5
- ## 주요 기능
6
-
7
- - 🔒 **타입 안전성**: TypeScript 기반의 완전한 타입 추론 및 discriminated union
8
- - 🌐 **멀티 환경**: 브라우저와 서버 환경 모두 지원
9
- - 🎯 **직관적 API**: Supabase 스타일의 쿼리 빌더
10
- - ⚡ **React Query 통합**: 데이터 페칭 및 캐싱 최적화
11
- - 🔄 **Webhook 처리**: 타입 안전한 webhook 핸들링
12
- - 📦 **37개 컬렉션**: 브랜드, 상품, 주문, 콘텐츠 등 포괄적 지원
13
- - 🔁 **자동 재시도**: Exponential backoff 전략 기반 재시도 로직
14
- - 🐛 **디버그 모드**: 요청/응답/에러 선택적 로깅
15
- - ⚠️ **향상된 에러 처리**: 사용자 친화적 에러 메시지 및 제안
16
-
17
- ## 설치
5
+ ## Installation
18
6
 
19
7
  ```bash
20
8
  npm install @01.software/sdk
21
- # 또는
22
- yarn add @01.software/sdk
23
- # 또는
9
+ # or
24
10
  pnpm add @01.software/sdk
25
11
  ```
26
12
 
27
- ## 빠른 시작
13
+ ## Features
14
+
15
+ - Full TypeScript type inference
16
+ - Browser and server environment support
17
+ - React Query integration
18
+ - Automatic retry with exponential backoff
19
+ - Webhook handling
20
+
21
+ ## Getting Started
28
22
 
29
- ### 브라우저 환경
23
+ ### Browser Client
30
24
 
31
25
  ```typescript
32
26
  import { createBrowserClient } from '@01.software/sdk'
33
27
 
34
28
  const client = createBrowserClient({
35
- clientKey: process.env.NEXT_PUBLIC_SOFTWARE_CLIENT_KEY
29
+ clientKey: process.env.NEXT_PUBLIC_SOFTWARE_CLIENT_KEY,
36
30
  })
37
31
 
38
- // 쿼리 빌더로 데이터 조회
39
- const { data: products } = await client.from('products').find({
32
+ // Query data
33
+ const { data } = await client.from('products').find({
40
34
  limit: 10,
41
- where: { status: { equals: 'published' } }
35
+ where: { status: { equals: 'published' } },
42
36
  })
43
-
44
- // React Query 훅 사용
45
- function ProductList() {
46
- const { data, isLoading } = client.query.useCollection('products', {
47
- limit: 10
48
- })
49
-
50
- if (isLoading) return <div>Loading...</div>
51
- return <div>{data?.docs.map(p => p.name)}</div>
52
- }
53
37
  ```
54
38
 
55
- ### 서버 환경
39
+ ### Server Client
56
40
 
57
41
  ```typescript
58
42
  import { createServerClient } from '@01.software/sdk'
@@ -62,469 +46,205 @@ const client = createServerClient({
62
46
  secretKey: process.env.SOFTWARE_SECRET_KEY,
63
47
  })
64
48
 
65
- // 주문 생성
49
+ // Create order (server only)
66
50
  const order = await client.api.createOrder({
67
51
  paymentId: 'pay_123',
68
52
  orderNumber: generateOrderNumber(),
69
53
  email: 'user@example.com',
70
- orderProducts: [
71
- {
72
- product: 'product_id',
73
- variant: 'variant_id',
74
- quantity: 1,
75
- price: 10000,
76
- },
77
- ],
54
+ orderProducts: [...],
78
55
  totalAmount: 10000,
79
56
  })
80
57
  ```
81
58
 
82
- ## 핵심 개념
83
-
84
- ### 1. 클라이언트
59
+ ## API
85
60
 
86
- #### BrowserClient
87
-
88
- 브라우저 환경에서 사용하는 클라이언트로, React Query 통합과 쿼리 빌더를 제공합니다.
61
+ ### Client Configuration
89
62
 
90
63
  ```typescript
91
64
  const client = createBrowserClient({
92
- clientKey: 'your-client-key',
93
- baseUrl: 'https://api.example.com', // 선택사항
94
- debug: true, // 디버그 모드 활성화
95
- retry: {
96
- maxRetries: 3, // 최대 재시도 횟수
97
- retryDelay: (attempt) => Math.min(1000 * 2 ** attempt, 10000), // Exponential backoff
98
- },
99
- errorLogger: {
100
- log: (error) => console.error('SDK Error:', error), // 커스텀 에러 로거
101
- },
65
+ clientKey: string, // Required
66
+ baseUrl?: string, // API URL (optional)
67
+ debug?: boolean | DebugOptions,
68
+ retry?: RetryOptions,
69
+ errorLogger?: ErrorLogger,
102
70
  })
103
71
  ```
104
72
 
105
- **주요 속성:**
106
-
107
- - `query`: React Query (`useCollection`, `useCollectionSingle`, `useCollectionInfinite`)
108
- - `collections`: 컬렉션 API 클라이언트
109
- - `from()`: Supabase 스타일 쿼리 빌더
73
+ | Option | Type | Description |
74
+ | ----------- | ------------------------- | ---------------------------- |
75
+ | `clientKey` | `string` | API client key |
76
+ | `secretKey` | `string` | API secret key (server only) |
77
+ | `baseUrl` | `string` | API base URL |
78
+ | `debug` | `boolean \| DebugOptions` | Enable debug logging |
79
+ | `retry` | `RetryOptions` | Retry configuration |
110
80
 
111
- #### ServerClient
81
+ ### Query Builder
112
82
 
113
- 서버 환경에서 사용하는 클라이언트로, API 클라이언트를 추가로 제공합니다.
83
+ Access collections via `client.from(collection)` method.
114
84
 
115
85
  ```typescript
116
- const client = createServerClient({
117
- clientKey: 'your-client-key',
118
- secretKey: 'your-secret-key', // 서버 전용
119
- })
120
- ```
121
-
122
- **추가 속성:**
123
-
124
- - `api`: 주문 및 트랜잭션 API (`createOrder`, `updateOrder`, `updateTransaction`)
125
-
126
- ### 2. 쿼리 빌더
127
-
128
- Supabase 스타일의 타입 안전한 쿼리 빌더를 제공합니다.
129
-
130
- ```typescript
131
- // 목록 조회
86
+ // List query
132
87
  const { data } = await client.from('products').find({
133
88
  limit: 20,
134
89
  page: 1,
135
90
  sort: '-createdAt',
136
- where: {
137
- status: { equals: 'published' },
138
- price: { greater_than: 1000 },
139
- },
91
+ where: { status: { equals: 'published' } },
140
92
  })
141
93
 
142
- // ID로 조회
143
- const { data: product } = await client.from('products').findById('product_id')
94
+ // Single item query
95
+ const { data } = await client.from('products').findById('id')
144
96
 
145
- // 생성
146
- const { data: newProduct } = await client.from('products').create({
147
- name: '새 상품',
148
- price: 10000,
149
- })
97
+ // Create (server only)
98
+ const { data } = await client.from('products').create({ name: 'Product' })
150
99
 
151
- // 수정
152
- const { data: updatedProduct } = await client
153
- .from('products')
154
- .update('product_id', {
155
- name: '수정된 상품명',
156
- })
100
+ // Update (server only)
101
+ const { data } = await client.from('products').update('id', { name: 'Updated' })
157
102
 
158
- // 삭제
159
- await client.from('products').remove('product_id')
103
+ // Delete (server only)
104
+ await client.from('products').remove('id')
160
105
  ```
161
106
 
162
- ### 3. React Query
163
-
164
- 데이터 페칭과 캐싱을 위한 React Query 통합을 제공합니다.
107
+ ### React Query Hooks
165
108
 
166
109
  ```typescript
167
- // 컬렉션 목록 조회
168
- const { data, isLoading, error } = client.query.useCollection('products', {
169
- limit: 10,
170
- where: { status: { equals: 'published' } },
110
+ // List query
111
+ const { data, isLoading } = client.query.useQuery({
112
+ collection: 'products',
113
+ options: { limit: 10 },
114
+ })
115
+
116
+ // Suspense mode
117
+ const { data } = client.query.useSuspenseQuery({
118
+ collection: 'products',
119
+ options: { limit: 10 },
171
120
  })
172
121
 
173
- // 단일 항목 조회
174
- const { data: product } = client.query.useCollectionSingle('products', {
175
- where: { slug: { equals: 'my-product' } },
122
+ // Query by ID
123
+ const { data } = client.query.useQueryById({
124
+ collection: 'products',
125
+ id: 'product_id',
176
126
  })
177
127
 
178
- // 무한 스크롤
179
- const { data, fetchNextPage, hasNextPage } = client.query.useCollectionInfinite(
180
- 'products',
181
- {
182
- limit: 20,
183
- },
184
- )
128
+ // Infinite scroll
129
+ const { data, fetchNextPage, hasNextPage } = client.query.useInfiniteQuery({
130
+ collection: 'products',
131
+ options: { limit: 20 },
132
+ })
185
133
  ```
186
134
 
187
- ### 4. Webhook 처리
135
+ ### Server API
188
136
 
189
- Next.js API Route에서 타입 안전한 webhook 처리를 제공합니다.
137
+ Available only in ServerClient.
190
138
 
191
139
  ```typescript
192
- // app/api/webhook/route.ts
193
- import { handleWebhook } from '@01.software/sdk'
140
+ // Create order
141
+ await client.api.createOrder(data)
194
142
 
195
- export async function POST(request: Request) {
196
- return handleWebhook(request, async (event) => {
197
- console.log('Collection:', event.collection)
198
- console.log('Operation:', event.operation) // 'create' | 'update' | 'delete'
199
- console.log('Data:', event.data)
200
-
201
- if (event.collection === 'orders') {
202
- // 주문 관련 처리
203
- if (event.operation === 'update') {
204
- console.log('Order updated:', event.data.id)
205
- }
206
- }
207
- })
208
- }
143
+ // Update order
144
+ await client.api.updateOrder(id, data)
145
+
146
+ // Update transaction
147
+ await client.api.updateTransaction(id, data)
209
148
  ```
210
149
 
211
- 타입 안전한 컬렉션별 핸들러:
150
+ ### Webhook
212
151
 
213
152
  ```typescript
214
- import { createTypedWebhookHandler } from '@01.software/sdk'
215
-
216
- const handleOrderWebhook = createTypedWebhookHandler(
217
- 'orders',
218
- async (event) => {
219
- // event.data의 타입이 Order로 자동 추론됨
220
- console.log('Order:', event.data.orderNumber)
221
- },
222
- )
153
+ import { handleWebhook, createTypedWebhookHandler } from '@01.software/sdk'
223
154
 
155
+ // Basic handler
224
156
  export async function POST(request: Request) {
225
- return handleWebhook(request, handleOrderWebhook)
157
+ return handleWebhook(request, async (event) => {
158
+ console.log(event.collection, event.operation, event.data)
159
+ })
226
160
  }
227
- ```
228
-
229
- ## API 레퍼런스
230
-
231
- ### 컬렉션 목록
232
-
233
- SDK는 37개의 컬렉션을 지원합니다:
234
-
235
- **브랜드 관련**
236
-
237
- - `brands`, `brand-logos`, `brand-og-images`, `brand-settings`, `brand-secret-keys`
238
-
239
- **상품 관련**
240
161
 
241
- - `products`, `product-variants`, `product-options`, `product-categories`, `product-tags`, `product-images`
242
-
243
- **주문 관련**
244
-
245
- - `orders`, `order-products`, `returns`, `return-products`, `transactions`
246
-
247
- **콘텐츠 관련**
248
-
249
- - `posts`, `post-categories`, `post-tags`, `post-images`
250
- - `documents`, `document-images`
251
- - `entities`, `entity-categories`, `entity-tags`, `entity-images`
252
-
253
- **미디어 관련**
254
-
255
- - `playlists`, `playlist-images`, `musics`
256
- - `galleries`, `gallery-images`
257
-
258
- **기타**
162
+ // Type-safe handler
163
+ const handler = createTypedWebhookHandler('orders', async (event) => {
164
+ // event.data is typed as Order
165
+ console.log(event.data.orderNumber)
166
+ })
167
+ ```
259
168
 
260
- - `links`, `link-images`, `nodes`, `forms`
169
+ ## Supported Collections
261
170
 
262
- ### 유틸리티 함수
171
+ | Category | Collections |
172
+ | -------- | ---------------------------------------------------------------------------------------------------------------------------------- |
173
+ | Tenant | `tenants`, `tenant-metadata`, `tenant-logos`, `tenant-og-images` |
174
+ | Products | `products`, `product-variants`, `product-options`, `product-categories`, `product-tags`, `product-images`, `brands`, `brand-logos` |
175
+ | Orders | `orders`, `order-products`, `returns`, `return-products`, `transactions` |
176
+ | Content | `posts`, `post-categories`, `post-tags`, `post-images`, `documents`, `document-categories`, `document-images` |
177
+ | Media | `playlists`, `playlist-images`, `musics`, `galleries`, `gallery-images`, `media` |
263
178
 
264
- #### generateOrderNumber()
179
+ ## Utilities
265
180
 
266
- 날짜 기반 주문 번호를 생성합니다.
181
+ ### generateOrderNumber
267
182
 
268
183
  ```typescript
269
184
  import { generateOrderNumber } from '@01.software/sdk'
270
185
 
271
186
  const orderNumber = generateOrderNumber()
272
- // 예: "260107123456" (YYMMDDRRRRRR 형식)
187
+ // "260121123456" (YYMMDDRRRRRR format)
273
188
  ```
274
189
 
275
- #### formatOrderName()
276
-
277
- 상품 옵션 배열을 주문명으로 포맷팅합니다.
190
+ ### formatOrderName
278
191
 
279
192
  ```typescript
280
193
  import { formatOrderName } from '@01.software/sdk'
281
194
 
282
- const orderName = formatOrderName([
283
- { product: { name: '상품 A' }, variant: { name: '옵션 1' } },
284
- ])
285
- // "상품 A"
195
+ formatOrderName([{ product: { name: 'Product A' } }])
196
+ // "Product A"
286
197
 
287
- const orderName2 = formatOrderName([
288
- { product: { name: '상품 A' }, variant: { name: '옵션 1' } },
289
- { product: { name: '상품 B' }, variant: { name: '옵션 2' } },
198
+ formatOrderName([
199
+ { product: { name: 'Product A' } },
200
+ { product: { name: 'Product B' } },
290
201
  ])
291
- // "상품 A 1"
202
+ // "Product A and 1 more"
292
203
  ```
293
204
 
294
- ### React 컴포넌트
295
-
296
- #### RichTextContent
205
+ ### RichTextContent
297
206
 
298
- Payload CMS Lexical 리치 텍스트를 렌더링합니다.
207
+ React component for rendering Payload CMS Lexical rich text.
299
208
 
300
- ```typescript
209
+ ```tsx
301
210
  import { RichTextContent } from '@01.software/sdk'
302
211
 
303
- function Article({ content }) {
304
- return (
305
- <RichTextContent
306
- data={content}
307
- className="prose"
308
- internalDocToHref={(args) => `/posts/${args.doc.slug}`}
309
- blocks={{
310
- Iframe: ({ url }) => <iframe src={url} />,
311
- Player: ({ videoId }) => <VideoPlayer id={videoId} />
312
- }}
313
- />
314
- )
315
- }
316
- ```
317
-
318
- ## 고급 사용법
319
-
320
- ### 디버그 모드
321
-
322
- 개발 중 요청/응답을 로깅하여 디버깅을 쉽게 할 수 있습니다.
323
-
324
- ```typescript
325
- // 전체 디버그 모드
326
- const client = createBrowserClient({
327
- clientKey: 'your-key',
328
- debug: true, // 모든 요청/응답/에러 로깅
329
- })
330
-
331
- // 선택적 디버그 모드
332
- const client = createBrowserClient({
333
- clientKey: 'your-key',
334
- debug: {
335
- logRequests: true, // 요청만 로깅
336
- logResponses: false, // 응답 로깅 안 함
337
- logErrors: true, // 에러만 로깅
338
- },
339
- })
340
- ```
341
-
342
- ### 재시도 로직
343
-
344
- 네트워크 오류나 서버 에러 시 자동으로 재시도합니다.
345
-
346
- ```typescript
347
- const client = createBrowserClient({
348
- clientKey: 'your-key',
349
- retry: {
350
- maxRetries: 3, // 최대 재시도 횟수 (기본값: 3)
351
- retryableStatuses: [408, 429, 500, 502, 503, 504], // 재시도할 HTTP 상태 코드
352
- retryDelay: (attempt) => {
353
- // Exponential backoff: 1초 → 2초 → 4초 → 8초 (최대 10초)
354
- return Math.min(1000 * 2 ** attempt, 10000)
355
- },
356
- },
357
- })
358
- ```
359
-
360
- **기본 동작:**
361
-
362
- - 네트워크 에러는 항상 재시도
363
- - 408, 429, 500, 502, 503, 504 상태 코드는 재시도
364
- - 401, 404 등 클라이언트 에러는 재시도하지 않음
365
- - Exponential backoff 전략 사용
366
-
367
- ### 쿼리 옵션
368
-
369
- ```typescript
370
- // 정렬
371
- const { data } = await client.from('products').find({
372
- sort: '-createdAt', // 내림차순
373
- })
374
-
375
- // 페이지네이션
376
- const { data, pagination } = await client.from('products').find({
377
- page: 2,
378
- limit: 20,
379
- })
380
- console.log(pagination.hasNextPage)
381
-
382
- // 복잡한 필터
383
- const { data } = await client.from('products').find({
384
- where: {
385
- and: [
386
- { status: { equals: 'published' } },
387
- { price: { greater_than: 1000, less_than: 50000 } },
388
- {
389
- or: [
390
- { category: { equals: 'electronics' } },
391
- { tags: { contains: 'featured' } },
392
- ],
393
- },
394
- ],
395
- },
396
- })
397
- ```
398
-
399
- ### React Query 옵션
400
-
401
- ```typescript
402
- const { data } = client.query.useCollection(
403
- 'products',
404
- {
405
- limit: 10,
406
- },
407
- {
408
- staleTime: 5 * 60 * 1000, // 5분
409
- cacheTime: 10 * 60 * 1000, // 10분
410
- refetchOnWindowFocus: false,
411
- keepPreviousData: true,
412
- },
413
- )
212
+ ;<RichTextContent
213
+ data={content}
214
+ className="prose"
215
+ internalDocToHref={(args) => `/posts/${args.doc.slug}`}
216
+ blocks={{
217
+ Iframe: ({ url }) => <iframe src={url} />,
218
+ Player: ({ videoId }) => <VideoPlayer id={videoId} />,
219
+ }}
220
+ />
414
221
  ```
415
222
 
416
- ### 에러 처리
417
-
418
- SDK는 사용자 친화적인 에러 메시지와 제안을 제공합니다.
223
+ ## Error Handling
419
224
 
420
225
  ```typescript
421
226
  import {
422
- SDKError,
423
- ApiError,
424
- NetworkError,
425
- ValidationError,
426
- TimeoutError,
427
227
  isNetworkError,
228
+ isSuccessResponse,
229
+ isErrorResponse,
428
230
  } from '@01.software/sdk'
429
231
 
430
- try {
431
- const { data } = await client.from('products').find()
432
- } catch (error) {
433
- if (isNetworkError(error)) {
434
- // 사용자 친화적 메시지
435
- console.error(error.getUserMessage())
436
- // "네트워크 연결을 확인해주세요."
437
-
438
- // 개발자용 메시지
439
- console.error(error.message)
440
-
441
- // 해결 제안
442
- console.log(error.suggestion)
443
- // "인터넷 연결을 확인하거나 잠시 후 다시 시도해주세요."
444
- }
445
- }
446
- ```
447
-
448
- #### 타입 안전한 응답 처리
449
-
450
- ```typescript
451
- import { isSuccessResponse, isErrorResponse } from '@01.software/sdk'
452
-
453
232
  const response = await client.from('products').find()
454
233
 
455
234
  if (isSuccessResponse(response)) {
456
- // TypeScript가 response.data의 타입을 Product[]로 추론
457
235
  console.log(response.data)
458
- console.log(response.pagination)
459
236
  } else if (isErrorResponse(response)) {
460
- // TypeScript가 response.error의 타입을 추론
461
- console.error(response.error.code)
462
237
  console.error(response.error.message)
463
238
  }
464
239
  ```
465
240
 
466
- ## 보안
241
+ Error classes: `SDKError`, `ApiError`, `NetworkError`, `ValidationError`, `TimeoutError`
467
242
 
468
- ### 환경 변수 설정
243
+ ## Environment Variables
469
244
 
470
245
  ```bash
471
- # .env.local
472
246
  NEXT_PUBLIC_SOFTWARE_CLIENT_KEY=your_client_key
473
- SOFTWARE_SECRET_KEY=your_secret_key # 서버 전용
247
+ SOFTWARE_SECRET_KEY=your_secret_key # Server only
474
248
  ```
475
249
 
476
- ### 주의사항
477
-
478
- - `clientKey`는 브라우저에 노출되어도 안전합니다.
479
- - `secretKey`는 **절대** 브라우저에 노출되어서는 안 됩니다.
480
- - `secretKey`는 서버 환경(API Route, Server Component 등)에서만 사용하세요.
481
-
482
- ## 타입 시스템
483
-
484
- 모든 컬렉션은 자동으로 타입이 추론됩니다:
485
-
486
- ```typescript
487
- // 타입이 자동으로 추론됨
488
- const { data: products } = await client.from('products').find()
489
- // products의 타입: Product[]
490
-
491
- const { data: product } = await client.from('products').findById('id')
492
- // product의 타입: Product
493
-
494
- // 생성 시에도 타입 체크
495
- await client.from('products').create({
496
- name: '상품명', // ✅ OK
497
- price: 10000, // ✅ OK
498
- invalid: 'field', // ❌ 타입 에러
499
- })
500
- ```
501
-
502
- ## 개발
503
-
504
- SDK는 포괄적인 테스트를 포함하고 있으며, 타입 안전성과 사용자 경험을 최우선으로 설계되었습니다.
505
-
506
- ### 테스트
507
-
508
- ```bash
509
- # 모든 테스트 실행
510
- pnpm test
511
-
512
- # watch 모드로 실행
513
- pnpm test:watch
514
- ```
515
-
516
- ### 주요 특징
517
-
518
- - ✅ **완전한 타입 안전성**: TypeScript 기반 타입 추론
519
- - ✅ **사용자 친화적 에러 처리**: 명확한 에러 메시지와 해결 제안
520
- - ✅ **자동 재시도**: 네트워크 오류 시 exponential backoff 전략
521
- - ✅ **디버그 모드**: 개발 중 요청/응답/에러 로깅
522
- - ✅ **React Query 통합**: 효율적인 데이터 페칭 및 캐싱
523
-
524
- ## 라이센스
525
-
526
- MIT
527
-
528
- ## 지원
529
-
530
- 문제가 발생하거나 궁금한 점이 있다면 이슈를 등록해주세요.
250
+ > `secretKey` should only be used in server environments. Never expose it to the browser.