@diffsome/sdk 3.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/dist/index.mjs ADDED
@@ -0,0 +1,1463 @@
1
+ // src/http.ts
2
+ var DEFAULT_META = {
3
+ current_page: 1,
4
+ last_page: 1,
5
+ per_page: 15,
6
+ total: 0,
7
+ from: null,
8
+ to: null
9
+ };
10
+ function normalizeListResponse(response) {
11
+ if (response == null) {
12
+ return { data: [], meta: { ...DEFAULT_META } };
13
+ }
14
+ if (Array.isArray(response)) {
15
+ return {
16
+ data: response,
17
+ meta: { ...DEFAULT_META, total: response.length, from: response.length > 0 ? 1 : null, to: response.length > 0 ? response.length : null }
18
+ };
19
+ }
20
+ if (typeof response === "object") {
21
+ const data = Array.isArray(response.data) ? response.data : [];
22
+ const meta = {
23
+ current_page: response.meta?.current_page ?? response.current_page ?? 1,
24
+ last_page: response.meta?.last_page ?? response.last_page ?? 1,
25
+ per_page: response.meta?.per_page ?? response.per_page ?? 15,
26
+ total: response.meta?.total ?? response.total ?? data.length,
27
+ from: response.meta?.from ?? response.from ?? (data.length > 0 ? 1 : null),
28
+ to: response.meta?.to ?? response.to ?? (data.length > 0 ? data.length : null)
29
+ };
30
+ return { data, meta };
31
+ }
32
+ return { data: [], meta: { ...DEFAULT_META } };
33
+ }
34
+ var DiffsomeError = class extends Error {
35
+ constructor(message, status, errors) {
36
+ super(message);
37
+ this.name = "DiffsomeError";
38
+ this.status = status;
39
+ this.errors = errors;
40
+ }
41
+ };
42
+ var HttpClient = class {
43
+ constructor(config) {
44
+ this.token = null;
45
+ this.apiKey = null;
46
+ this.cartSessionId = null;
47
+ // Token persistence options
48
+ this.persistToken = false;
49
+ this.storageType = "localStorage";
50
+ this.tenantId = config.tenantId;
51
+ this.baseUrl = (config.baseUrl || "https://diffsome.com").replace(/\/$/, "");
52
+ this.timeout = config.timeout || 3e4;
53
+ this.apiKey = config.apiKey || null;
54
+ this.persistToken = config.persistToken ?? false;
55
+ this.storageType = config.storageType ?? "localStorage";
56
+ this.storageKey = config.storageKey ?? `diffsome_auth_token_${this.tenantId}`;
57
+ this.onAuthStateChange = config.onAuthStateChange;
58
+ if (typeof window !== "undefined" && window.localStorage) {
59
+ this.cartSessionId = localStorage.getItem(`diffsome_cart_session_${this.tenantId}`);
60
+ }
61
+ if (this.persistToken && typeof window !== "undefined") {
62
+ const storage = this.getStorage();
63
+ if (storage) {
64
+ const savedToken = storage.getItem(this.storageKey);
65
+ if (savedToken) {
66
+ this.token = savedToken;
67
+ this.onAuthStateChange?.(savedToken, void 0);
68
+ }
69
+ }
70
+ }
71
+ if (config.token) {
72
+ this.token = config.token;
73
+ }
74
+ }
75
+ /**
76
+ * Get the storage object based on config
77
+ */
78
+ getStorage() {
79
+ if (typeof window === "undefined") return null;
80
+ return this.storageType === "sessionStorage" ? window.sessionStorage : window.localStorage;
81
+ }
82
+ /**
83
+ * Set authentication token
84
+ * If persistToken is enabled, automatically saves/removes from storage
85
+ * @param token - The auth token or null to clear
86
+ * @param user - The logged-in user (Member) for the callback
87
+ */
88
+ setToken(token, user) {
89
+ this.token = token;
90
+ if (this.persistToken) {
91
+ const storage = this.getStorage();
92
+ if (storage) {
93
+ if (token) {
94
+ storage.setItem(this.storageKey, token);
95
+ } else {
96
+ storage.removeItem(this.storageKey);
97
+ }
98
+ }
99
+ }
100
+ this.onAuthStateChange?.(token, user ?? null);
101
+ }
102
+ /**
103
+ * Get current token
104
+ */
105
+ getToken() {
106
+ return this.token;
107
+ }
108
+ /**
109
+ * Check if authenticated
110
+ */
111
+ isAuthenticated() {
112
+ return this.token !== null || this.apiKey !== null;
113
+ }
114
+ /**
115
+ * Set API key for server-to-server authentication
116
+ */
117
+ setApiKey(apiKey) {
118
+ this.apiKey = apiKey;
119
+ }
120
+ /**
121
+ * Get current API key
122
+ */
123
+ getApiKey() {
124
+ return this.apiKey;
125
+ }
126
+ /**
127
+ * Set cart session ID (for guest cart persistence)
128
+ */
129
+ setCartSessionId(sessionId) {
130
+ this.cartSessionId = sessionId;
131
+ if (typeof window !== "undefined" && window.localStorage) {
132
+ if (sessionId) {
133
+ localStorage.setItem(`diffsome_cart_session_${this.tenantId}`, sessionId);
134
+ } else {
135
+ localStorage.removeItem(`diffsome_cart_session_${this.tenantId}`);
136
+ }
137
+ }
138
+ }
139
+ /**
140
+ * Get cart session ID
141
+ */
142
+ getCartSessionId() {
143
+ return this.cartSessionId;
144
+ }
145
+ /**
146
+ * Build full URL with query params
147
+ */
148
+ buildUrl(endpoint, params) {
149
+ const url = new URL(`${this.baseUrl}/api/${this.tenantId}${endpoint}`);
150
+ if (params) {
151
+ Object.entries(params).forEach(([key, value]) => {
152
+ if (value !== void 0 && value !== null) {
153
+ url.searchParams.append(key, String(value));
154
+ }
155
+ });
156
+ }
157
+ return url.toString();
158
+ }
159
+ /**
160
+ * Build request headers
161
+ * Both API key and bearer token can be sent together
162
+ */
163
+ buildHeaders(customHeaders) {
164
+ const headers = {
165
+ "Content-Type": "application/json",
166
+ "Accept": "application/json",
167
+ ...customHeaders
168
+ };
169
+ if (this.apiKey) {
170
+ headers["X-API-Key"] = this.apiKey;
171
+ }
172
+ if (this.token) {
173
+ headers["Authorization"] = `Bearer ${this.token}`;
174
+ }
175
+ if (this.cartSessionId) {
176
+ headers["X-Cart-Session"] = this.cartSessionId;
177
+ }
178
+ return headers;
179
+ }
180
+ /**
181
+ * Make HTTP request
182
+ */
183
+ async request(endpoint, options = {}) {
184
+ const { method = "GET", body, params, headers } = options;
185
+ const url = this.buildUrl(endpoint, params);
186
+ const requestHeaders = this.buildHeaders(headers);
187
+ const controller = new AbortController();
188
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
189
+ try {
190
+ const response = await fetch(url, {
191
+ method,
192
+ headers: requestHeaders,
193
+ body: body ? JSON.stringify(body) : void 0,
194
+ signal: controller.signal
195
+ });
196
+ clearTimeout(timeoutId);
197
+ const data = await response.json();
198
+ if (!response.ok) {
199
+ throw new DiffsomeError(
200
+ data.message || "Request failed",
201
+ response.status,
202
+ data.errors
203
+ );
204
+ }
205
+ return data.data !== void 0 ? data.data : data;
206
+ } catch (error) {
207
+ clearTimeout(timeoutId);
208
+ if (error instanceof DiffsomeError) {
209
+ throw error;
210
+ }
211
+ if (error instanceof Error) {
212
+ if (error.name === "AbortError") {
213
+ throw new DiffsomeError("Request timeout", 408);
214
+ }
215
+ throw new DiffsomeError(error.message, 0);
216
+ }
217
+ throw new DiffsomeError("Unknown error", 0);
218
+ }
219
+ }
220
+ /**
221
+ * GET request
222
+ */
223
+ get(endpoint, params) {
224
+ return this.request(endpoint, { method: "GET", params });
225
+ }
226
+ /**
227
+ * GET request for list endpoints - ALWAYS returns normalized ListResponse
228
+ * Guarantees: data is always an array, meta is always present
229
+ */
230
+ async getList(endpoint, params) {
231
+ const response = await this.request(endpoint, { method: "GET", params });
232
+ return normalizeListResponse(response);
233
+ }
234
+ /**
235
+ * POST request
236
+ */
237
+ post(endpoint, body) {
238
+ return this.request(endpoint, { method: "POST", body });
239
+ }
240
+ /**
241
+ * PUT request
242
+ */
243
+ put(endpoint, body) {
244
+ return this.request(endpoint, { method: "PUT", body });
245
+ }
246
+ /**
247
+ * PATCH request
248
+ */
249
+ patch(endpoint, body) {
250
+ return this.request(endpoint, { method: "PATCH", body });
251
+ }
252
+ /**
253
+ * DELETE request
254
+ */
255
+ delete(endpoint, params) {
256
+ return this.request(endpoint, { method: "DELETE", params });
257
+ }
258
+ /**
259
+ * Upload file
260
+ */
261
+ async upload(endpoint, file, fieldName = "file") {
262
+ const url = this.buildUrl(endpoint);
263
+ const formData = new FormData();
264
+ formData.append(fieldName, file);
265
+ const headers = {
266
+ "Accept": "application/json"
267
+ };
268
+ if (this.apiKey) {
269
+ headers["X-API-Key"] = this.apiKey;
270
+ }
271
+ if (this.token) {
272
+ headers["Authorization"] = `Bearer ${this.token}`;
273
+ }
274
+ const controller = new AbortController();
275
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
276
+ try {
277
+ const response = await fetch(url, {
278
+ method: "POST",
279
+ headers,
280
+ body: formData,
281
+ signal: controller.signal
282
+ });
283
+ clearTimeout(timeoutId);
284
+ const data = await response.json();
285
+ if (!response.ok) {
286
+ throw new DiffsomeError(
287
+ data.message || "Upload failed",
288
+ response.status,
289
+ data.errors
290
+ );
291
+ }
292
+ return data.data !== void 0 ? data.data : data;
293
+ } catch (error) {
294
+ clearTimeout(timeoutId);
295
+ if (error instanceof DiffsomeError) {
296
+ throw error;
297
+ }
298
+ if (error instanceof Error) {
299
+ if (error.name === "AbortError") {
300
+ throw new DiffsomeError("Upload timeout", 408);
301
+ }
302
+ throw new DiffsomeError(error.message, 0);
303
+ }
304
+ throw new DiffsomeError("Unknown error", 0);
305
+ }
306
+ }
307
+ };
308
+
309
+ // src/resources/auth.ts
310
+ var AuthResource = class {
311
+ constructor(http) {
312
+ this.http = http;
313
+ }
314
+ /**
315
+ * Login with email and password
316
+ * Token is automatically saved if persistToken is enabled
317
+ */
318
+ async login(credentials) {
319
+ const response = await this.http.post("/auth/login", credentials);
320
+ if (response.token) {
321
+ this.http.setToken(response.token, response.user);
322
+ }
323
+ return response;
324
+ }
325
+ /**
326
+ * Register new member
327
+ * Token is automatically saved if persistToken is enabled
328
+ */
329
+ async register(data) {
330
+ const response = await this.http.post("/auth/register", data);
331
+ if (response.token) {
332
+ this.http.setToken(response.token, response.user);
333
+ }
334
+ return response;
335
+ }
336
+ /**
337
+ * Logout current user
338
+ */
339
+ async logout() {
340
+ try {
341
+ await this.http.post("/auth/logout");
342
+ } finally {
343
+ this.http.setToken(null);
344
+ }
345
+ }
346
+ /**
347
+ * Get current user profile
348
+ */
349
+ async me() {
350
+ return this.http.get("/profile");
351
+ }
352
+ /**
353
+ * Update profile
354
+ */
355
+ async updateProfile(data) {
356
+ return this.http.put("/profile", data);
357
+ }
358
+ /**
359
+ * Send password reset email
360
+ */
361
+ async forgotPassword(data) {
362
+ return this.http.post("/auth/forgot-password", data);
363
+ }
364
+ /**
365
+ * Reset password with token
366
+ */
367
+ async resetPassword(data) {
368
+ return this.http.post("/auth/reset-password", data);
369
+ }
370
+ /**
371
+ * Get available social login providers
372
+ */
373
+ async getSocialProviders() {
374
+ return this.http.get("/auth/social");
375
+ }
376
+ /**
377
+ * Get social login redirect URL
378
+ */
379
+ async getSocialAuthUrl(provider) {
380
+ return this.http.get(`/auth/social/${provider}`);
381
+ }
382
+ /**
383
+ * Handle social login callback
384
+ * Token is automatically saved if persistToken is enabled
385
+ */
386
+ async socialCallback(provider, code) {
387
+ const response = await this.http.post(`/auth/social/${provider}/callback`, { code });
388
+ if (response.token) {
389
+ this.http.setToken(response.token, response.user);
390
+ }
391
+ return response;
392
+ }
393
+ /**
394
+ * Set token manually (e.g., from localStorage)
395
+ */
396
+ setToken(token) {
397
+ this.http.setToken(token);
398
+ }
399
+ /**
400
+ * Get current token
401
+ */
402
+ getToken() {
403
+ return this.http.getToken();
404
+ }
405
+ /**
406
+ * Check if user is authenticated
407
+ */
408
+ isAuthenticated() {
409
+ return this.http.isAuthenticated();
410
+ }
411
+ };
412
+
413
+ // src/resources/boards.ts
414
+ var BoardsResource = class {
415
+ constructor(http) {
416
+ this.http = http;
417
+ }
418
+ // ============================================
419
+ // Boards (Public)
420
+ // ============================================
421
+ /**
422
+ * List all boards
423
+ * @returns ListResponse with data array (always defined) and pagination meta
424
+ */
425
+ async list(params) {
426
+ return this.http.getList("/boards", params);
427
+ }
428
+ /**
429
+ * Get board by ID or slug
430
+ */
431
+ async get(idOrSlug) {
432
+ return this.http.get(`/boards/${idOrSlug}`);
433
+ }
434
+ // ============================================
435
+ // Posts
436
+ // ============================================
437
+ /**
438
+ * List posts in a board
439
+ * @returns ListResponse with data array and pagination meta
440
+ */
441
+ async listPosts(boardIdOrSlug, params) {
442
+ return this.http.getList(`/boards/${boardIdOrSlug}/posts`, params);
443
+ }
444
+ /**
445
+ * Get post by ID
446
+ */
447
+ async getPost(postId) {
448
+ return this.http.get(`/posts/${postId}`);
449
+ }
450
+ // ============================================
451
+ // Posts (Protected - requires auth)
452
+ // ============================================
453
+ /**
454
+ * Create new post
455
+ */
456
+ async createPost(data) {
457
+ return this.http.post("/posts", data);
458
+ }
459
+ /**
460
+ * Update post
461
+ */
462
+ async updatePost(postId, data) {
463
+ return this.http.put(`/posts/${postId}`, data);
464
+ }
465
+ /**
466
+ * Delete post
467
+ */
468
+ async deletePost(postId) {
469
+ return this.http.delete(`/posts/${postId}`);
470
+ }
471
+ // ============================================
472
+ // Comments
473
+ // ============================================
474
+ /**
475
+ * List comments for a post
476
+ * @returns Array of comments (always an array, never null/undefined)
477
+ */
478
+ async listComments(postId) {
479
+ const response = await this.http.getList(`/posts/${postId}/comments`);
480
+ return response.data;
481
+ }
482
+ /**
483
+ * Create comment on a post
484
+ */
485
+ async createComment(postId, data) {
486
+ return this.http.post(`/posts/${postId}/comments`, data);
487
+ }
488
+ /**
489
+ * Update comment
490
+ */
491
+ async updateComment(commentId, data) {
492
+ return this.http.put(`/comments/${commentId}`, data);
493
+ }
494
+ /**
495
+ * Delete comment
496
+ */
497
+ async deleteComment(commentId) {
498
+ return this.http.delete(`/comments/${commentId}`);
499
+ }
500
+ };
501
+
502
+ // src/resources/blog.ts
503
+ var BlogResource = class {
504
+ constructor(http) {
505
+ this.http = http;
506
+ }
507
+ /**
508
+ * List blog posts
509
+ * @returns ListResponse with data array (always defined) and pagination meta
510
+ */
511
+ async list(params) {
512
+ return this.http.getList("/blog", params);
513
+ }
514
+ /**
515
+ * Get blog post by slug
516
+ */
517
+ async get(slug) {
518
+ return this.http.get(`/blog/${slug}`);
519
+ }
520
+ /**
521
+ * Get blog post by ID
522
+ */
523
+ async getById(id) {
524
+ return this.http.get(`/blog/id/${id}`);
525
+ }
526
+ /**
527
+ * Get featured blog posts
528
+ * @returns Array of featured posts (always an array, never null/undefined)
529
+ */
530
+ async featured(limit = 5) {
531
+ const response = await this.http.getList("/blog", {
532
+ per_page: limit,
533
+ featured: true
534
+ });
535
+ return response.data;
536
+ }
537
+ /**
538
+ * Get blog posts by category
539
+ * @returns ListResponse with data array and pagination meta
540
+ */
541
+ async byCategory(category, params) {
542
+ return this.http.getList("/blog", {
543
+ ...params,
544
+ category
545
+ });
546
+ }
547
+ /**
548
+ * Get blog posts by tag
549
+ * @returns ListResponse with data array and pagination meta
550
+ */
551
+ async byTag(tag, params) {
552
+ return this.http.getList("/blog", {
553
+ ...params,
554
+ tag
555
+ });
556
+ }
557
+ /**
558
+ * Search blog posts
559
+ * @returns ListResponse with data array and pagination meta
560
+ */
561
+ async search(query, params) {
562
+ return this.http.getList("/blog", {
563
+ ...params,
564
+ search: query
565
+ });
566
+ }
567
+ /**
568
+ * Get blog categories
569
+ * @returns Array of category names (always an array)
570
+ */
571
+ async categories() {
572
+ const response = await this.http.get("/blog/categories");
573
+ return Array.isArray(response) ? response : response?.data ?? [];
574
+ }
575
+ /**
576
+ * Get blog tags
577
+ * @returns Array of tag names (always an array)
578
+ */
579
+ async tags() {
580
+ const response = await this.http.get("/blog/tags");
581
+ return Array.isArray(response) ? response : response?.data ?? [];
582
+ }
583
+ };
584
+
585
+ // src/resources/comments.ts
586
+ var CommentsResource = class {
587
+ constructor(http) {
588
+ this.http = http;
589
+ }
590
+ /**
591
+ * Get comments for a board post
592
+ */
593
+ async boardPost(postId, params) {
594
+ return this.http.get(`/posts/${postId}/comments`, params);
595
+ }
596
+ /**
597
+ * Create a comment on a board post
598
+ */
599
+ async createBoardPost(postId, data) {
600
+ return this.http.post(`/posts/${postId}/comments`, data);
601
+ }
602
+ /**
603
+ * Get comments for a blog post
604
+ */
605
+ async blogPost(slug, params) {
606
+ return this.http.get(`/blog/${slug}/comments`, params);
607
+ }
608
+ /**
609
+ * Create a comment on a blog post
610
+ */
611
+ async createBlogPost(slug, data) {
612
+ return this.http.post(`/blog/${slug}/comments`, data);
613
+ }
614
+ /**
615
+ * Get standalone comments (guestbook, feedback, etc.)
616
+ */
617
+ async standalone(pageSlug, params) {
618
+ return this.http.get(`/comments/${pageSlug}`, params);
619
+ }
620
+ /**
621
+ * Create a standalone comment
622
+ */
623
+ async createStandalone(pageSlug, data) {
624
+ return this.http.post(`/comments/${pageSlug}`, data);
625
+ }
626
+ /**
627
+ * Update a comment
628
+ */
629
+ async update(commentId, data) {
630
+ return this.http.put(`/comments/${commentId}`, data);
631
+ }
632
+ /**
633
+ * Delete a comment
634
+ */
635
+ async delete(commentId, data) {
636
+ return this.http.delete(`/comments/${commentId}`, data);
637
+ }
638
+ /**
639
+ * Like a comment
640
+ */
641
+ async like(commentId) {
642
+ return this.http.post(`/comments/${commentId}/like`);
643
+ }
644
+ };
645
+
646
+ // src/resources/forms.ts
647
+ var FormsResource = class {
648
+ constructor(http) {
649
+ this.http = http;
650
+ }
651
+ /**
652
+ * List all forms
653
+ * @returns ListResponse with data array and pagination meta
654
+ */
655
+ async list(params) {
656
+ return this.http.getList("/forms", params);
657
+ }
658
+ /**
659
+ * Get form by ID or slug
660
+ */
661
+ async get(idOrSlug) {
662
+ return this.http.get(`/forms/${idOrSlug}`);
663
+ }
664
+ /**
665
+ * Submit form data
666
+ */
667
+ async submit(formIdOrSlug, data) {
668
+ return this.http.post(`/forms/${formIdOrSlug}/submit`, data);
669
+ }
670
+ // ============================================
671
+ // Protected endpoints (requires auth)
672
+ // ============================================
673
+ /**
674
+ * Get my form submissions
675
+ * @returns ListResponse with data array and pagination meta
676
+ */
677
+ async mySubmissions(params) {
678
+ return this.http.getList("/form-submissions", params);
679
+ }
680
+ /**
681
+ * Get specific submission
682
+ */
683
+ async getSubmission(submissionId) {
684
+ return this.http.get(`/form-submissions/${submissionId}`);
685
+ }
686
+ };
687
+
688
+ // src/resources/shop.ts
689
+ var ShopResource = class {
690
+ constructor(http) {
691
+ this.http = http;
692
+ }
693
+ // ============================================
694
+ // Products (Public)
695
+ // ============================================
696
+ /**
697
+ * List products
698
+ * @returns ListResponse with data array and pagination meta
699
+ */
700
+ async listProducts(params) {
701
+ return this.http.getList("/products", params);
702
+ }
703
+ /**
704
+ * Get product by ID or slug
705
+ */
706
+ async getProduct(idOrSlug) {
707
+ return this.http.get(`/products/${idOrSlug}`);
708
+ }
709
+ /**
710
+ * Get featured products
711
+ * @returns Array of featured products (always an array)
712
+ */
713
+ async featuredProducts(limit = 8) {
714
+ const response = await this.http.getList("/products", {
715
+ per_page: limit,
716
+ is_featured: true
717
+ });
718
+ return response.data;
719
+ }
720
+ /**
721
+ * Search products
722
+ * @returns ListResponse with data array and pagination meta
723
+ */
724
+ async searchProducts(query, params) {
725
+ return this.http.getList("/products", {
726
+ ...params,
727
+ search: query
728
+ });
729
+ }
730
+ // ============================================
731
+ // Categories (Public)
732
+ // ============================================
733
+ /**
734
+ * List product categories
735
+ * @returns ListResponse with data array and pagination meta
736
+ */
737
+ async listCategories() {
738
+ return this.http.getList("/categories");
739
+ }
740
+ /**
741
+ * Get category by ID or slug
742
+ */
743
+ async getCategory(idOrSlug) {
744
+ return this.http.get(`/categories/${idOrSlug}`);
745
+ }
746
+ /**
747
+ * Get products in category
748
+ * @returns ListResponse with data array and pagination meta
749
+ */
750
+ async categoryProducts(categoryIdOrSlug, params) {
751
+ return this.http.getList(`/categories/${categoryIdOrSlug}/products`, params);
752
+ }
753
+ // ============================================
754
+ // Cart
755
+ // ============================================
756
+ /**
757
+ * Save cart session ID from response (for guest cart persistence)
758
+ */
759
+ saveCartSession(cart) {
760
+ if (cart.session_id) {
761
+ this.http.setCartSessionId(cart.session_id);
762
+ }
763
+ return cart;
764
+ }
765
+ /**
766
+ * Get current cart
767
+ */
768
+ async getCart() {
769
+ const cart = await this.http.get("/cart");
770
+ return this.saveCartSession(cart);
771
+ }
772
+ /**
773
+ * Add item to cart
774
+ */
775
+ async addToCart(data) {
776
+ const cart = await this.http.post("/cart/items", data);
777
+ return this.saveCartSession(cart);
778
+ }
779
+ /**
780
+ * Update cart item quantity
781
+ */
782
+ async updateCartItem(itemId, data) {
783
+ const cart = await this.http.put(`/cart/items/${itemId}`, data);
784
+ return this.saveCartSession(cart);
785
+ }
786
+ /**
787
+ * Remove item from cart
788
+ */
789
+ async removeFromCart(itemId) {
790
+ const cart = await this.http.delete(`/cart/items/${itemId}`);
791
+ return this.saveCartSession(cart);
792
+ }
793
+ /**
794
+ * Clear entire cart
795
+ */
796
+ async clearCart() {
797
+ await this.http.delete("/cart");
798
+ this.http.setCartSessionId(null);
799
+ }
800
+ // ============================================
801
+ // Orders (Protected)
802
+ // ============================================
803
+ /**
804
+ * List my orders
805
+ * @returns ListResponse with data array and pagination meta
806
+ */
807
+ async listOrders(params) {
808
+ return this.http.getList("/orders", params);
809
+ }
810
+ /**
811
+ * Get order by ID or order number
812
+ */
813
+ async getOrder(idOrNumber) {
814
+ return this.http.get(`/orders/${idOrNumber}`);
815
+ }
816
+ /**
817
+ * Create order from cart
818
+ */
819
+ async createOrder(data) {
820
+ return this.http.post("/orders", data);
821
+ }
822
+ /**
823
+ * Cancel order
824
+ */
825
+ async cancelOrder(orderId) {
826
+ return this.http.post(`/orders/${orderId}/cancel`);
827
+ }
828
+ // ============================================
829
+ // Payments
830
+ // ============================================
831
+ /**
832
+ * Get payment for order
833
+ */
834
+ async getPayment(orderId) {
835
+ return this.http.get(`/orders/${orderId}/payment`);
836
+ }
837
+ /**
838
+ * Get payment status (available payment methods)
839
+ */
840
+ async getPaymentStatus() {
841
+ return this.http.get("/payments/status");
842
+ }
843
+ // ============================================
844
+ // Toss Payments
845
+ // ============================================
846
+ /**
847
+ * Prepare Toss payment (get client key and payment info)
848
+ */
849
+ async tossPaymentReady(data) {
850
+ return this.http.post("/payments/toss/ready", data);
851
+ }
852
+ /**
853
+ * Confirm Toss payment (after redirect)
854
+ */
855
+ async tossPaymentConfirm(data) {
856
+ return this.http.post("/payments/toss/confirm", data);
857
+ }
858
+ /**
859
+ * Cancel Toss payment
860
+ */
861
+ async tossPaymentCancel(orderNumber, cancelReason, cancelAmount) {
862
+ await this.http.post("/payments/toss/cancel", {
863
+ order_number: orderNumber,
864
+ cancel_reason: cancelReason,
865
+ cancel_amount: cancelAmount
866
+ });
867
+ }
868
+ // ============================================
869
+ // Stripe Payments
870
+ // ============================================
871
+ /**
872
+ * Create Stripe Checkout Session
873
+ */
874
+ async stripeCheckout(data) {
875
+ return this.http.post("/payments/stripe/checkout", data);
876
+ }
877
+ /**
878
+ * Verify Stripe payment (after redirect)
879
+ */
880
+ async stripeVerify(data) {
881
+ return this.http.post("/payments/stripe/verify", data);
882
+ }
883
+ /**
884
+ * Refund Stripe payment
885
+ */
886
+ async stripeRefund(orderNumber, reason, amount) {
887
+ await this.http.post("/payments/stripe/refund", {
888
+ order_number: orderNumber,
889
+ reason,
890
+ amount
891
+ });
892
+ }
893
+ // ============================================
894
+ // Legacy Payment Methods (deprecated)
895
+ // ============================================
896
+ /**
897
+ * @deprecated Use tossPaymentReady instead
898
+ */
899
+ async preparePayment(data) {
900
+ return this.http.post(`/payments/ready`, data);
901
+ }
902
+ /**
903
+ * @deprecated Use tossPaymentConfirm instead
904
+ */
905
+ async confirmPayment(data) {
906
+ return this.http.post("/payments/confirm", data);
907
+ }
908
+ /**
909
+ * @deprecated Use tossPaymentCancel instead
910
+ */
911
+ async cancelPayment(paymentId, data) {
912
+ return this.http.post(`/payments/${paymentId}/cancel`, data);
913
+ }
914
+ // ============================================
915
+ // Coupons
916
+ // ============================================
917
+ /**
918
+ * Validate coupon code
919
+ */
920
+ async validateCoupon(code, orderAmount) {
921
+ return this.http.post("/coupons/validate", {
922
+ code,
923
+ order_amount: orderAmount
924
+ });
925
+ }
926
+ /**
927
+ * Get available coupons for current user
928
+ * @returns Array of coupons (always an array)
929
+ */
930
+ async myCoupons() {
931
+ const response = await this.http.getList("/coupons");
932
+ return response.data;
933
+ }
934
+ // ============================================
935
+ // Product Reviews
936
+ // ============================================
937
+ /**
938
+ * Get reviews for a product
939
+ * @param productSlug - Product slug
940
+ * @param params - Optional list params (rating, sort, per_page)
941
+ * @returns Reviews with stats
942
+ */
943
+ async getProductReviews(productSlug, params) {
944
+ return this.http.get(`/products/${productSlug}/reviews`, params);
945
+ }
946
+ /**
947
+ * Check if current user can review a product
948
+ * Requires: logged in + purchased the product + not already reviewed
949
+ */
950
+ async canReviewProduct(productSlug) {
951
+ const response = await this.http.get(
952
+ `/products/${productSlug}/reviews/can-review`
953
+ );
954
+ return response;
955
+ }
956
+ /**
957
+ * Create a product review (requires purchase)
958
+ */
959
+ async createReview(productSlug, data) {
960
+ const response = await this.http.post(
961
+ `/products/${productSlug}/reviews`,
962
+ data
963
+ );
964
+ return response.data;
965
+ }
966
+ /**
967
+ * Update your own review
968
+ */
969
+ async updateReview(reviewId, data) {
970
+ const response = await this.http.put(
971
+ `/reviews/${reviewId}`,
972
+ data
973
+ );
974
+ return response.data;
975
+ }
976
+ /**
977
+ * Delete your own review
978
+ */
979
+ async deleteReview(reviewId) {
980
+ await this.http.delete(`/reviews/${reviewId}`);
981
+ }
982
+ /**
983
+ * Mark a review as helpful
984
+ */
985
+ async markReviewHelpful(reviewId) {
986
+ return this.http.post(`/reviews/${reviewId}/helpful`);
987
+ }
988
+ /**
989
+ * Get my reviews
990
+ * @returns Array of reviews written by the current user
991
+ */
992
+ async myReviews(params) {
993
+ return this.http.getList("/my/reviews", params);
994
+ }
995
+ };
996
+
997
+ // src/resources/media.ts
998
+ var MediaResource = class {
999
+ constructor(http) {
1000
+ this.http = http;
1001
+ }
1002
+ /**
1003
+ * List my media files
1004
+ */
1005
+ async list(params) {
1006
+ return this.http.get("/media", params);
1007
+ }
1008
+ /**
1009
+ * Get media by ID
1010
+ */
1011
+ async get(mediaId) {
1012
+ return this.http.get(`/media/${mediaId}`);
1013
+ }
1014
+ /**
1015
+ * Upload file
1016
+ */
1017
+ async upload(file) {
1018
+ return this.http.upload("/media", file, "file");
1019
+ }
1020
+ /**
1021
+ * Upload multiple files
1022
+ */
1023
+ async uploadMultiple(files) {
1024
+ const results = [];
1025
+ for (const file of files) {
1026
+ const media = await this.upload(file);
1027
+ results.push(media);
1028
+ }
1029
+ return results;
1030
+ }
1031
+ /**
1032
+ * Delete media
1033
+ */
1034
+ async delete(mediaId) {
1035
+ return this.http.delete(`/media/${mediaId}`);
1036
+ }
1037
+ };
1038
+
1039
+ // src/resources/entities.ts
1040
+ var EntitiesResource = class {
1041
+ constructor(http) {
1042
+ this.http = http;
1043
+ }
1044
+ // ============================================
1045
+ // Entity Definitions CRUD
1046
+ // ============================================
1047
+ /**
1048
+ * List all custom entities
1049
+ * @returns Array of entities
1050
+ *
1051
+ * @example
1052
+ * ```typescript
1053
+ * const entities = await client.entities.list();
1054
+ * // [{ id: 1, name: 'Customer', slug: 'customer', records_count: 150, ... }]
1055
+ * ```
1056
+ */
1057
+ async list() {
1058
+ const response = await this.http.getList("/entities");
1059
+ return response.data;
1060
+ }
1061
+ /**
1062
+ * Create a new entity definition
1063
+ *
1064
+ * @example
1065
+ * ```typescript
1066
+ * const entity = await client.entities.create({
1067
+ * name: '고객',
1068
+ * slug: 'customers', // optional, auto-generated from name
1069
+ * description: '고객 관리',
1070
+ * schema: {
1071
+ * fields: [
1072
+ * { name: 'company', label: '회사명', type: 'text', required: true },
1073
+ * { name: 'email', label: '이메일', type: 'email', required: true },
1074
+ * { name: 'status', label: '상태', type: 'select', options: [
1075
+ * { value: 'active', label: '활성' },
1076
+ * { value: 'inactive', label: '비활성' }
1077
+ * ]}
1078
+ * ]
1079
+ * },
1080
+ * icon: 'users'
1081
+ * });
1082
+ * ```
1083
+ */
1084
+ async create(data) {
1085
+ return this.http.post("/entities", data);
1086
+ }
1087
+ /**
1088
+ * Get entity definition by slug (includes schema)
1089
+ *
1090
+ * @example
1091
+ * ```typescript
1092
+ * const entity = await client.entities.get('customers');
1093
+ * console.log(entity.schema.fields);
1094
+ * ```
1095
+ */
1096
+ async get(slug) {
1097
+ return this.http.get(`/entities/${slug}`);
1098
+ }
1099
+ /**
1100
+ * Update an entity definition
1101
+ *
1102
+ * @example
1103
+ * ```typescript
1104
+ * const updated = await client.entities.update('customers', {
1105
+ * name: '고객사',
1106
+ * description: '고객사 관리'
1107
+ * });
1108
+ * ```
1109
+ */
1110
+ async update(slug, data) {
1111
+ return this.http.put(`/entities/${slug}`, data);
1112
+ }
1113
+ /**
1114
+ * Delete an entity definition
1115
+ * If entity has records, use force=true to delete anyway
1116
+ *
1117
+ * @example
1118
+ * ```typescript
1119
+ * // Will fail if entity has records
1120
+ * await client.entities.delete('customers');
1121
+ *
1122
+ * // Force delete with all records
1123
+ * await client.entities.delete('customers', true);
1124
+ * ```
1125
+ */
1126
+ async delete(slug, force = false) {
1127
+ const params = force ? { force: "true" } : void 0;
1128
+ return this.http.delete(`/entities/${slug}`, params);
1129
+ }
1130
+ /**
1131
+ * Get entity schema (convenience method)
1132
+ * @deprecated Use get(slug) instead - it includes schema
1133
+ */
1134
+ async getSchema(slug) {
1135
+ const entity = await this.get(slug);
1136
+ return entity.schema;
1137
+ }
1138
+ // ============================================
1139
+ // Records CRUD
1140
+ // ============================================
1141
+ /**
1142
+ * List records for an entity
1143
+ * @returns ListResponse with data array and pagination meta
1144
+ *
1145
+ * @example
1146
+ * ```typescript
1147
+ * // Basic listing
1148
+ * const customers = await client.entities.listRecords('customers');
1149
+ *
1150
+ * // With pagination and search
1151
+ * const customers = await client.entities.listRecords('customers', {
1152
+ * page: 1,
1153
+ * per_page: 20,
1154
+ * search: 'ACME',
1155
+ * sort: 'company',
1156
+ * dir: 'asc'
1157
+ * });
1158
+ *
1159
+ * // With filtering
1160
+ * const vipCustomers = await client.entities.listRecords('customers', {
1161
+ * filters: JSON.stringify({ tier: 'vip' })
1162
+ * });
1163
+ * ```
1164
+ */
1165
+ async listRecords(slug, params) {
1166
+ return this.http.getList(`/entities/${slug}/records`, params);
1167
+ }
1168
+ /**
1169
+ * Get a single record by ID
1170
+ *
1171
+ * @example
1172
+ * ```typescript
1173
+ * const customer = await client.entities.getRecord('customers', 1);
1174
+ * console.log(customer.data.company); // 'ABC Corp'
1175
+ * ```
1176
+ */
1177
+ async getRecord(slug, id) {
1178
+ return this.http.get(`/entities/${slug}/records/${id}`);
1179
+ }
1180
+ /**
1181
+ * Create a new record
1182
+ * Request body fields are defined by entity schema
1183
+ *
1184
+ * @example
1185
+ * ```typescript
1186
+ * const newCustomer = await client.entities.createRecord('customers', {
1187
+ * company: 'ABC Corp',
1188
+ * email: 'contact@abc.com',
1189
+ * tier: 'standard',
1190
+ * });
1191
+ * ```
1192
+ */
1193
+ async createRecord(slug, data) {
1194
+ return this.http.post(`/entities/${slug}/records`, data);
1195
+ }
1196
+ /**
1197
+ * Update a record
1198
+ * Only provided fields will be updated, existing data is preserved
1199
+ *
1200
+ * @example
1201
+ * ```typescript
1202
+ * const updated = await client.entities.updateRecord('customers', 1, {
1203
+ * tier: 'vip',
1204
+ * email: 'new@abc.com'
1205
+ * });
1206
+ * ```
1207
+ */
1208
+ async updateRecord(slug, id, data) {
1209
+ return this.http.put(`/entities/${slug}/records/${id}`, data);
1210
+ }
1211
+ /**
1212
+ * Delete a record
1213
+ *
1214
+ * @example
1215
+ * ```typescript
1216
+ * await client.entities.deleteRecord('customers', 1);
1217
+ * ```
1218
+ */
1219
+ async deleteRecord(slug, id) {
1220
+ return this.http.delete(`/entities/${slug}/records/${id}`);
1221
+ }
1222
+ // ============================================
1223
+ // Helper Methods
1224
+ // ============================================
1225
+ /**
1226
+ * Get a value from a record's data
1227
+ *
1228
+ * @example
1229
+ * ```typescript
1230
+ * const record = await client.entities.getRecord('customers', 1);
1231
+ * const company = client.entities.getValue(record, 'company');
1232
+ * ```
1233
+ */
1234
+ getValue(record, field) {
1235
+ return record.data?.[field];
1236
+ }
1237
+ /**
1238
+ * Create a typed accessor for an entity
1239
+ *
1240
+ * @example
1241
+ * ```typescript
1242
+ * interface Customer {
1243
+ * company: string;
1244
+ * email: string;
1245
+ * tier: 'standard' | 'vip';
1246
+ * }
1247
+ *
1248
+ * const customers = client.entities.typed<Customer>('customers');
1249
+ * const list = await customers.list(); // Typed records
1250
+ * const record = await customers.get(1);
1251
+ * console.log(record.data.company); // TypeScript knows this is string
1252
+ * ```
1253
+ */
1254
+ typed(slug) {
1255
+ return {
1256
+ list: async (params) => {
1257
+ const response = await this.listRecords(slug, params);
1258
+ return {
1259
+ ...response,
1260
+ data: response.data
1261
+ };
1262
+ },
1263
+ get: async (id) => {
1264
+ const record = await this.getRecord(slug, id);
1265
+ return record;
1266
+ },
1267
+ create: async (data) => {
1268
+ const record = await this.createRecord(slug, data);
1269
+ return record;
1270
+ },
1271
+ update: async (id, data) => {
1272
+ const record = await this.updateRecord(slug, id, data);
1273
+ return record;
1274
+ },
1275
+ delete: (id) => this.deleteRecord(slug, id)
1276
+ };
1277
+ }
1278
+ };
1279
+
1280
+ // src/resources/reservation.ts
1281
+ var ReservationResource = class {
1282
+ constructor(http) {
1283
+ this.http = http;
1284
+ }
1285
+ // ============================================
1286
+ // Public Endpoints
1287
+ // ============================================
1288
+ /**
1289
+ * Get reservation settings
1290
+ * @returns Reservation settings for the tenant
1291
+ */
1292
+ async getSettings() {
1293
+ return this.http.get("/reservation/settings");
1294
+ }
1295
+ /**
1296
+ * List available services
1297
+ * @returns Array of services (always an array)
1298
+ */
1299
+ async listServices() {
1300
+ const response = await this.http.getList("/reservation/services");
1301
+ return response.data;
1302
+ }
1303
+ /**
1304
+ * List available staff members
1305
+ * @param serviceId - Optional: filter staff by service
1306
+ * @returns Array of staff members (always an array)
1307
+ */
1308
+ async listStaff(serviceId) {
1309
+ const params = serviceId ? { service_id: serviceId } : void 0;
1310
+ const response = await this.http.getList("/reservation/staffs", params);
1311
+ return response.data;
1312
+ }
1313
+ /**
1314
+ * Get available dates for booking
1315
+ * @returns Array of available date strings (YYYY-MM-DD)
1316
+ */
1317
+ async getAvailableDates(params) {
1318
+ const response = await this.http.get("/reservation/available-dates", params);
1319
+ return Array.isArray(response) ? response : response?.data ?? [];
1320
+ }
1321
+ /**
1322
+ * Get available time slots for a specific date
1323
+ * @returns Array of available slots (always an array)
1324
+ */
1325
+ async getAvailableSlots(params) {
1326
+ const response = await this.http.get("/reservation/available-slots", params);
1327
+ return Array.isArray(response) ? response : response?.data ?? [];
1328
+ }
1329
+ // ============================================
1330
+ // Protected Endpoints (requires auth)
1331
+ // ============================================
1332
+ /**
1333
+ * Create a new reservation
1334
+ * @returns Created reservation with payment info
1335
+ */
1336
+ async create(data) {
1337
+ return this.http.post("/reservations", data);
1338
+ }
1339
+ /**
1340
+ * List my reservations
1341
+ * @returns ListResponse with reservations and pagination meta
1342
+ */
1343
+ async list(params) {
1344
+ return this.http.getList("/reservations", params);
1345
+ }
1346
+ /**
1347
+ * Get upcoming reservations
1348
+ * @returns Array of upcoming reservations
1349
+ */
1350
+ async upcoming(limit = 10) {
1351
+ const response = await this.http.getList("/reservations", {
1352
+ upcoming: true,
1353
+ per_page: limit
1354
+ });
1355
+ return response.data;
1356
+ }
1357
+ /**
1358
+ * Get past reservations
1359
+ * @returns Array of past reservations
1360
+ */
1361
+ async past(limit = 10) {
1362
+ const response = await this.http.getList("/reservations", {
1363
+ past: true,
1364
+ per_page: limit
1365
+ });
1366
+ return response.data;
1367
+ }
1368
+ /**
1369
+ * Get reservation by reservation number
1370
+ */
1371
+ async get(reservationNumber) {
1372
+ return this.http.get(`/reservations/${reservationNumber}`);
1373
+ }
1374
+ /**
1375
+ * Cancel a reservation
1376
+ * @param reservationNumber - Reservation number to cancel
1377
+ * @param reason - Optional cancellation reason
1378
+ */
1379
+ async cancel(reservationNumber, reason) {
1380
+ return this.http.post(`/reservations/${reservationNumber}/cancel`, { reason });
1381
+ }
1382
+ };
1383
+
1384
+ // src/index.ts
1385
+ var Diffsome = class {
1386
+ constructor(config) {
1387
+ if (!config.apiKey) {
1388
+ throw new Error("API key is required. Get your API key from Dashboard > Settings > API Tokens");
1389
+ }
1390
+ this.http = new HttpClient(config);
1391
+ this.auth = new AuthResource(this.http);
1392
+ this.boards = new BoardsResource(this.http);
1393
+ this.blog = new BlogResource(this.http);
1394
+ this.comments = new CommentsResource(this.http);
1395
+ this.forms = new FormsResource(this.http);
1396
+ this.shop = new ShopResource(this.http);
1397
+ this.media = new MediaResource(this.http);
1398
+ this.entities = new EntitiesResource(this.http);
1399
+ this.reservation = new ReservationResource(this.http);
1400
+ }
1401
+ /**
1402
+ * Get site theme settings
1403
+ */
1404
+ async getTheme() {
1405
+ return this.http.get("/public/theme");
1406
+ }
1407
+ /**
1408
+ * Get site settings
1409
+ */
1410
+ async getSettings() {
1411
+ return this.http.get("/public/settings");
1412
+ }
1413
+ /**
1414
+ * Check if user is authenticated
1415
+ */
1416
+ isAuthenticated() {
1417
+ return this.auth.isAuthenticated();
1418
+ }
1419
+ /**
1420
+ * Set authentication token manually
1421
+ */
1422
+ setToken(token) {
1423
+ this.auth.setToken(token);
1424
+ }
1425
+ /**
1426
+ * Get current authentication token
1427
+ */
1428
+ getToken() {
1429
+ return this.auth.getToken();
1430
+ }
1431
+ /**
1432
+ * Set API key for server-to-server authentication
1433
+ * Alternative to user token authentication
1434
+ *
1435
+ * @example
1436
+ * ```typescript
1437
+ * const client = new Diffsome({
1438
+ * tenantId: 'my-site',
1439
+ * apiKey: 'pky_your_api_key_here',
1440
+ * });
1441
+ *
1442
+ * // Or set later
1443
+ * client.setApiKey('pky_your_api_key_here');
1444
+ * ```
1445
+ */
1446
+ setApiKey(apiKey) {
1447
+ this.http.setApiKey(apiKey);
1448
+ }
1449
+ /**
1450
+ * Get current API key
1451
+ */
1452
+ getApiKey() {
1453
+ return this.http.getApiKey();
1454
+ }
1455
+ };
1456
+ var index_default = Diffsome;
1457
+ export {
1458
+ Diffsome,
1459
+ DiffsomeError,
1460
+ Diffsome as Promptly,
1461
+ DiffsomeError as PromptlyError,
1462
+ index_default as default
1463
+ };