@sazito/client-sdk 1.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 ADDED
@@ -0,0 +1,459 @@
1
+ # Sazito Client SDK
2
+
3
+ Official JavaScript/TypeScript SDK for Sazito storefronts.
4
+
5
+ This SDK is built for application developers who want a typed, framework-agnostic client with:
6
+ - automatic request/response key transformation
7
+ - unified response objects (`{ data, error }`)
8
+ - configurable retry/timeout/cache behavior
9
+ - guest checkout credential handling
10
+ - modular API access for products, checkout, user, CMS, analytics, and more
11
+
12
+ ## Package
13
+
14
+ - Name: `@sazito/client-sdk`
15
+ - Version: `1.0.1`
16
+ - License: `MIT`
17
+
18
+ ## Requirements
19
+
20
+ - Node.js 18+ (recommended) or any runtime with `fetch`
21
+ - Browser environments with `fetch`
22
+
23
+ The SDK sends requests to `http://api.sazito.com:8080` and includes your store domain in the `x-forwarded-host` header.
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm install @sazito/client-sdk
29
+ ```
30
+
31
+ ```bash
32
+ yarn add @sazito/client-sdk
33
+ ```
34
+
35
+ ```bash
36
+ pnpm add @sazito/client-sdk
37
+ ```
38
+
39
+ ## Quick Start
40
+
41
+ ```ts
42
+ import { createSazitoClient } from '@sazito/client-sdk';
43
+
44
+ const sazito = createSazitoClient({
45
+ domain: 'mystore.sazito.com'
46
+ });
47
+
48
+ const res = await sazito.products.list({
49
+ page: 1,
50
+ pageSize: 20,
51
+ sort: 'newest'
52
+ });
53
+
54
+ if (res.error) {
55
+ console.error(res.error.message, res.error.status);
56
+ } else {
57
+ console.log(res.data.items);
58
+ }
59
+ ```
60
+
61
+ ## Core Response Model
62
+
63
+ All SDK methods return a `SazitoResponse<T>`:
64
+
65
+ ```ts
66
+ type SazitoResponse<T> = {
67
+ data?: T;
68
+ error?: {
69
+ status?: number;
70
+ message: string;
71
+ type: 'network' | 'api' | 'validation';
72
+ details?: any;
73
+ };
74
+ };
75
+ ```
76
+
77
+ Typical usage:
78
+
79
+ ```ts
80
+ const response = await sazito.categories.get(110);
81
+
82
+ if (response.error) {
83
+ // API/network/validation issue
84
+ return;
85
+ }
86
+
87
+ // success path
88
+ console.log(response.data);
89
+ ```
90
+
91
+ ## Configuration
92
+
93
+ ```ts
94
+ import { createSazitoClient } from '@sazito/client-sdk';
95
+
96
+ const sazito = createSazitoClient({
97
+ domain: 'mystore.sazito.com',
98
+ timeout: 30000,
99
+ debug: false,
100
+ retry: {
101
+ enabled: true,
102
+ retries: 3,
103
+ retryDelay: 1000
104
+ },
105
+ cache: {
106
+ products: { enabled: true, ttl: 600000 },
107
+ categories: { enabled: true, ttl: 600000 },
108
+ cart: { enabled: false },
109
+ orders: { enabled: false },
110
+ search: { enabled: true, ttl: 300000 },
111
+ cms: { enabled: true, ttl: 600000 },
112
+ tags: { enabled: true, ttl: 600000 },
113
+ entityRoutes: { enabled: true, ttl: 600000 }
114
+ }
115
+ });
116
+ ```
117
+
118
+ ### Config Fields
119
+
120
+ | Field | Type | Required | Notes |
121
+ |---|---|---|---|
122
+ | `domain` | `string` | Yes | Store domain without protocol |
123
+ | `timeout` | `number` | No | Global request timeout in ms (default `30000`) |
124
+ | `retry` | object | No | Retry policy for 5xx responses |
125
+ | `cache` | object | No | Per-module cache strategy |
126
+ | `customFetchApi` | `typeof fetch` | No | Override fetch implementation |
127
+ | `debug` | `boolean` | No | Enables SDK debug logging |
128
+
129
+ ## Per-Request Overrides
130
+
131
+ Any API call can receive `RequestOptions`:
132
+
133
+ ```ts
134
+ const response = await sazito.products.get('/product/sample-slug', {
135
+ cache: false,
136
+ timeout: 5000,
137
+ retries: 1,
138
+ headers: {
139
+ 'X-Request-ID': 'req-123'
140
+ }
141
+ });
142
+ ```
143
+
144
+ Supported request options:
145
+ - `retries`
146
+ - `timeout`
147
+ - `cache`
148
+ - `headers`
149
+ - `signal`
150
+
151
+ ## Authentication
152
+
153
+ ```ts
154
+ sazito.setAuthToken('<jwt>');
155
+
156
+ const isLoggedIn = sazito.isAuthenticated();
157
+ const token = sazito.getAuthToken();
158
+
159
+ sazito.clearAuth();
160
+ ```
161
+
162
+ Auth token behavior:
163
+ - SDK injects `Authorization` header automatically when a token exists.
164
+ - Token is sent as raw JWT (not `Bearer <token>`).
165
+ - Token is persisted under `user_id_token` in `localStorage` (with cookie fallback).
166
+
167
+ ## Cache and Credential Utilities
168
+
169
+ ```ts
170
+ sazito.clearCache();
171
+ sazito.clearCredentials();
172
+ sazito.clearAll();
173
+
174
+ sazito.cart.clearCart();
175
+ sazito.invoices.clearInvoice();
176
+ sazito.shipping.clearAddress();
177
+ sazito.payments.clearPayment();
178
+ ```
179
+
180
+ ## API Surface
181
+
182
+ ### Client Modules
183
+
184
+ The client instance exposes:
185
+ - `products`
186
+ - `categories`
187
+ - `cart`
188
+ - `orders`
189
+ - `invoices`
190
+ - `shipping`
191
+ - `payments`
192
+ - `users`
193
+ - `search`
194
+ - `feedbacks`
195
+ - `wallet`
196
+ - `cms`
197
+ - `images`
198
+ - `visits`
199
+ - `booking`
200
+ - `entityRoutes`
201
+ - `menu`
202
+ - `general`
203
+
204
+ ### Methods by Module
205
+
206
+ | Module | Methods |
207
+ |---|---|
208
+ | `products` | `get`, `list`, `search` |
209
+ | `categories` | `get`, `list` |
210
+ | `cart` | `get`, `create`, `addItem`, `updateItem`, `removeItem`, `clearCart` |
211
+ | `orders` | `list`, `get` |
212
+ | `invoices` | `get`, `create`, `refresh`, `addShippingAddress`, `addDiscountCode`, `assignShippingMethod`, `addDetails`, `getApplicableShippingMethods`, `clearInvoice` |
213
+ | `shipping` | `createAddress`, `updateAddress`, `getAddress`, `getMethods`, `clearAddress` |
214
+ | `payments` | `getMethods`, `create`, `initialize`, `processStep`, `clearPayment` |
215
+ | `users` | `login`, `requestMobileOTP`, `verifyMobileOTP`, `requestEmailLogin`, `register`, `getCurrentUser`, `updateProfile`, `requestMobilePhoneUpdate`, `verifyMobilePhoneUpdate`, `forgotPassword`, `revivePassword`, `mergeUser` |
216
+ | `search` | `search` |
217
+ | `feedbacks` | `list`, `create`, `get` |
218
+ | `wallet` | `getBalance`, `applyCredit`, `removeCredit`, `listTransactions` |
219
+ | `cms` | `getPage`, `listPages`, `getBlogPost`, `listBlogPosts`, `listAll` |
220
+ | `images` | `upload`, `delete` |
221
+ | `visits` | `track`, `trackProduct`, `trackCategory` |
222
+ | `booking` | `listEvents`, `getEvent`, `createBooking`, `listBookings`, `cancelBooking` |
223
+ | `entityRoutes` | `resolve` |
224
+ | `menu` | `getHeaderMenu` |
225
+ | `general` | `getInfo`, `getFeatures`, `getCheckoutConfig`, `getWalletConfig`, `getTajrobeConfig` |
226
+
227
+ ## Usage Examples
228
+
229
+ ### Products and Search
230
+
231
+ ```ts
232
+ const product = await sazito.products.get('/product/some-product-slug');
233
+
234
+ const list = await sazito.products.list({
235
+ categories: [73, 94],
236
+ priceMin: 100000,
237
+ priceMax: 900000,
238
+ availableOnly: true,
239
+ discountedOnly: true,
240
+ sort: '!price',
241
+ page: 1,
242
+ pageSize: 12
243
+ });
244
+
245
+ const search = await sazito.search.query('shoes', {
246
+ categoryId: 73,
247
+ minPrice: 100000,
248
+ maxPrice: 900000,
249
+ page: 1,
250
+ pageSize: 10
251
+ });
252
+ ```
253
+
254
+ ### Guest Checkout Flow
255
+
256
+ ```ts
257
+ // 1) Add product to cart (creates guest cart automatically if needed)
258
+ await sazito.cart.addItem(12345, 2);
259
+
260
+ // 2) Create invoice from cart
261
+ const invoiceRes = await sazito.invoices.create();
262
+ if (invoiceRes.error) throw new Error(invoiceRes.error.message);
263
+
264
+ // 3) Add shipping address
265
+ const addrRes = await sazito.shipping.createAddress({
266
+ firstName: 'John',
267
+ lastName: 'Doe',
268
+ mobilePhone: '09123456789',
269
+ regionId: 1,
270
+ cityId: 10,
271
+ address: 'No. 10, Example St',
272
+ postalCode: '1234567890'
273
+ });
274
+ if (addrRes.error) throw new Error(addrRes.error.message);
275
+
276
+ // 4) Attach shipping address to invoice
277
+ await sazito.invoices.addShippingAddress(addrRes.data.id, addrRes.data.identifier);
278
+
279
+ // 5) Fetch methods and assign shipping
280
+ const methodsRes = await sazito.invoices.getApplicableShippingMethods();
281
+ if (methodsRes.data?.length) {
282
+ const firstRate = methodsRes.data[0]?.rates?.[0];
283
+ const currentInvoice = await sazito.invoices.get();
284
+
285
+ if (firstRate && currentInvoice.data) {
286
+ await sazito.invoices.assignShippingMethod([
287
+ {
288
+ rateId: firstRate.id,
289
+ invoiceItemIds: currentInvoice.data.items.map(i => i.id)
290
+ }
291
+ ]);
292
+ }
293
+ }
294
+
295
+ // 6) Payment
296
+ const paymentMethods = await sazito.payments.getMethods();
297
+ if (paymentMethods.data?.length) {
298
+ await sazito.payments.create(paymentMethods.data[0].id);
299
+ const action = await sazito.payments.initialize();
300
+ console.log(action.data);
301
+ }
302
+ ```
303
+
304
+ ### Users/Auth
305
+
306
+ ```ts
307
+ const login = await sazito.users.login({
308
+ email: 'dev@example.com',
309
+ password: 'strong-password'
310
+ });
311
+
312
+ if (login.data?.jwt) {
313
+ sazito.setAuthToken(login.data.jwt);
314
+ }
315
+
316
+ const me = await sazito.users.getCurrentUser();
317
+ ```
318
+
319
+ ### CMS and Entity Routes
320
+
321
+ ```ts
322
+ const route = await sazito.entityRoutes.resolve('/product/some-product-slug');
323
+ const page = await sazito.cms.getPage('/about-us');
324
+ const blog = await sazito.cms.getBlogPost('/blog/how-to-buy');
325
+ ```
326
+
327
+ ### Menu and General Config
328
+
329
+ ```ts
330
+ const menu = await sazito.menu.getHeaderMenu();
331
+ const info = await sazito.general.getInfo();
332
+ const features = await sazito.general.getFeatures();
333
+ ```
334
+
335
+ ### Analytics Visits
336
+
337
+ ```ts
338
+ await sazito.visits.track();
339
+ ```
340
+
341
+ ## Data Transformation Behavior
342
+
343
+ The SDK transforms request and response keys to improve developer ergonomics.
344
+
345
+ Common examples:
346
+ - `no_of_items` -> `quantity`
347
+ - `single_item_price` -> `unitPrice`
348
+ - `product_variant_id` -> `variantId`
349
+ - `first_name` -> `firstName`
350
+ - `postal_code` -> `postalCode`
351
+
352
+ Notes:
353
+ - Request payloads are transformed before being sent.
354
+ - Response payloads are transformed before being returned.
355
+ - Some APIs accept both SDK-friendly and raw fields for backward compatibility.
356
+
357
+ ## Visual API Playground
358
+
359
+ Run the local visual playground:
360
+
361
+ ```bash
362
+ yarn visual:apis
363
+ ```
364
+
365
+ Then open:
366
+ - `http://127.0.0.1:4173`
367
+
368
+ Files:
369
+ - `/Users/rezamahmoudi/sazito-sdk/scripts/visual-docs-server.js`
370
+ - `/Users/rezamahmoudi/sazito-sdk/scripts/visual-api-playground/public/index.html`
371
+ - `/Users/rezamahmoudi/sazito-sdk/scripts/visual-api-playground/public/app.js`
372
+ - `/Users/rezamahmoudi/sazito-sdk/scripts/visual-api-playground/public/styles.css`
373
+
374
+ ## Development
375
+
376
+ Project scripts:
377
+
378
+ ```bash
379
+ yarn build # Build dist outputs
380
+ yarn dev # Rollup watch mode
381
+ yarn typecheck # TypeScript check (no emit)
382
+ yarn lint # ESLint on src/
383
+ yarn validate # typecheck + lint
384
+ ```
385
+
386
+ ## Fumadocs Documentation Site
387
+
388
+ SDK docs are implemented as a separate Fumadocs app in `/Users/rezamahmoudi/sazito-sdk/docs`.
389
+
390
+ Run docs locally from the repository root:
391
+
392
+ ```bash
393
+ yarn docs:install
394
+ yarn docs:dev
395
+ ```
396
+
397
+ Build/start docs:
398
+
399
+ ```bash
400
+ yarn docs:build
401
+ yarn docs:start
402
+ ```
403
+
404
+ This docs app is tracked in GitHub, but it is not included in the published npm package.
405
+ Publishing is controlled by the root `files` list in `/Users/rezamahmoudi/sazito-sdk/package.json`, which only ships:
406
+ - `dist/`
407
+ - `README.md`
408
+ - `LICENSE`
409
+
410
+ Output formats:
411
+ - `dist/index.js` (CJS)
412
+ - `dist/index.esm.js` (ESM)
413
+ - `dist/index.umd.js` (UMD)
414
+ - `dist/index.d.ts` (types)
415
+
416
+ ## Repository Structure
417
+
418
+ ```txt
419
+ src/
420
+ api/ API modules
421
+ core/ client, config, HTTP layer, cache
422
+ constants/ endpoint constants
423
+ types/ exported SDK types
424
+ utils/ token storage, credentials, transformers
425
+ scripts/
426
+ visual-docs-server.js
427
+ visual-api-playground/public/
428
+ docs/
429
+ app/ Next.js routes and layouts
430
+ content/docs/ MDX documentation pages
431
+ lib/ Fumadocs source/layout config
432
+ ```
433
+
434
+ ## Troubleshooting
435
+
436
+ ### `error.type === 'validation'`
437
+ Usually means prerequisite state is missing (for example no cart/invoice credentials in guest flow). Initialize earlier steps first.
438
+
439
+ ### `error.type === 'network'`
440
+ Check connectivity, runtime `fetch` support, and request timeout.
441
+
442
+ ### Authentication issues
443
+ Make sure token is set with `setAuthToken` and that your backend accepts raw JWT in `Authorization`.
444
+
445
+ ### CMS helpers may throw
446
+ `cms.getPage` / `cms.getBlogPost` validate entity type and can throw when URL resolves to another entity type. Wrap these calls in `try/catch`.
447
+
448
+ ## Minimal TypeScript Example
449
+
450
+ ```ts
451
+ import { createSazitoClient, SazitoResponse, Product } from '@sazito/client-sdk';
452
+
453
+ const client = createSazitoClient({ domain: 'mystore.sazito.com' });
454
+
455
+ async function getProduct(path: string): Promise<Product | null> {
456
+ const res: SazitoResponse<Product> = await client.products.get(path);
457
+ return res.data ?? null;
458
+ }
459
+ ```