@01.software/sdk 0.4.0 → 0.4.1
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/auth.d.cts +1 -1
- package/dist/auth.d.ts +1 -1
- package/dist/{const-BpirbGBD.d.cts → const-BsO3aVX_.d.cts} +1 -1
- package/dist/{const-qZSQiSSC.d.ts → const-DZyvV9wU.d.ts} +1 -1
- package/dist/index.cjs +57 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +57 -8
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-CZiaL4Wr.d.cts → payload-types-BsjHfeAF.d.cts} +62 -2
- package/dist/{payload-types-CZiaL4Wr.d.ts → payload-types-BsjHfeAF.d.ts} +62 -2
- package/dist/realtime.cjs +5 -3
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +5 -3
- package/dist/realtime.js.map +1 -1
- package/dist/ui/flow.cjs +264 -36
- package/dist/ui/flow.cjs.map +1 -1
- package/dist/ui/flow.d.cts +104 -3
- package/dist/ui/flow.d.ts +104 -3
- package/dist/ui/flow.js +252 -21
- package/dist/ui/flow.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/rich-text.cjs +8 -1
- package/dist/ui/rich-text.cjs.map +1 -1
- package/dist/ui/rich-text.d.cts +20 -1
- package/dist/ui/rich-text.d.ts +20 -1
- package/dist/ui/rich-text.js +8 -1
- package/dist/ui/rich-text.js.map +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/{webhook-DuTqrH9x.d.ts → webhook-BBWl8O2f.d.ts} +2 -2
- package/dist/{webhook-3iL9OEyq.d.cts → webhook-CkL56e65.d.cts} +2 -2
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +2 -1
|
@@ -1302,6 +1302,9 @@ interface VideoCategory {
|
|
|
1302
1302
|
*/
|
|
1303
1303
|
generateSlug?: boolean | null;
|
|
1304
1304
|
slug?: string | null;
|
|
1305
|
+
/**
|
|
1306
|
+
* Short summary for listing/cards
|
|
1307
|
+
*/
|
|
1305
1308
|
description?: string | null;
|
|
1306
1309
|
image?: (string | null) | Image;
|
|
1307
1310
|
parent?: (string | null) | VideoCategory;
|
|
@@ -1324,6 +1327,9 @@ interface VideoTag {
|
|
|
1324
1327
|
*/
|
|
1325
1328
|
generateSlug?: boolean | null;
|
|
1326
1329
|
slug?: string | null;
|
|
1330
|
+
/**
|
|
1331
|
+
* Short summary for listing/cards
|
|
1332
|
+
*/
|
|
1327
1333
|
description?: string | null;
|
|
1328
1334
|
image?: (string | null) | Image;
|
|
1329
1335
|
parent?: (string | null) | VideoTag;
|
|
@@ -1450,6 +1456,9 @@ interface ProductCategory {
|
|
|
1450
1456
|
*/
|
|
1451
1457
|
generateSlug?: boolean | null;
|
|
1452
1458
|
slug?: string | null;
|
|
1459
|
+
/**
|
|
1460
|
+
* Short summary for listing/cards
|
|
1461
|
+
*/
|
|
1453
1462
|
description?: string | null;
|
|
1454
1463
|
image?: (string | null) | Image;
|
|
1455
1464
|
parent?: (string | null) | ProductCategory;
|
|
@@ -1472,6 +1481,9 @@ interface ProductTag {
|
|
|
1472
1481
|
*/
|
|
1473
1482
|
generateSlug?: boolean | null;
|
|
1474
1483
|
slug?: string | null;
|
|
1484
|
+
/**
|
|
1485
|
+
* Short summary for listing/cards
|
|
1486
|
+
*/
|
|
1475
1487
|
description?: string | null;
|
|
1476
1488
|
image?: (string | null) | Image;
|
|
1477
1489
|
parent?: (string | null) | ProductTag;
|
|
@@ -1490,7 +1502,7 @@ interface Brand {
|
|
|
1490
1502
|
tenant?: (string | null) | Tenant;
|
|
1491
1503
|
name: string;
|
|
1492
1504
|
/**
|
|
1493
|
-
* Short
|
|
1505
|
+
* Short summary for listing/cards
|
|
1494
1506
|
*/
|
|
1495
1507
|
description?: string | null;
|
|
1496
1508
|
content?: {
|
|
@@ -1810,6 +1822,9 @@ interface CustomerGroup {
|
|
|
1810
1822
|
*/
|
|
1811
1823
|
generateSlug?: boolean | null;
|
|
1812
1824
|
slug?: string | null;
|
|
1825
|
+
/**
|
|
1826
|
+
* Short summary for listing/cards
|
|
1827
|
+
*/
|
|
1813
1828
|
description?: string | null;
|
|
1814
1829
|
color?: string | null;
|
|
1815
1830
|
image?: (string | null) | Image;
|
|
@@ -2213,7 +2228,7 @@ interface Post {
|
|
|
2213
2228
|
title: string;
|
|
2214
2229
|
subtitle?: string | null;
|
|
2215
2230
|
/**
|
|
2216
|
-
*
|
|
2231
|
+
* Short summary for listing/cards
|
|
2217
2232
|
*/
|
|
2218
2233
|
description?: string | null;
|
|
2219
2234
|
content?: {
|
|
@@ -2332,6 +2347,9 @@ interface PostCategory {
|
|
|
2332
2347
|
*/
|
|
2333
2348
|
generateSlug?: boolean | null;
|
|
2334
2349
|
slug?: string | null;
|
|
2350
|
+
/**
|
|
2351
|
+
* Short summary for listing/cards
|
|
2352
|
+
*/
|
|
2335
2353
|
description?: string | null;
|
|
2336
2354
|
image?: (string | null) | Image;
|
|
2337
2355
|
parent?: (string | null) | PostCategory;
|
|
@@ -2354,6 +2372,9 @@ interface PostTag {
|
|
|
2354
2372
|
*/
|
|
2355
2373
|
generateSlug?: boolean | null;
|
|
2356
2374
|
slug?: string | null;
|
|
2375
|
+
/**
|
|
2376
|
+
* Short summary for listing/cards
|
|
2377
|
+
*/
|
|
2357
2378
|
description?: string | null;
|
|
2358
2379
|
image?: (string | null) | Image;
|
|
2359
2380
|
parent?: (string | null) | PostTag;
|
|
@@ -2371,6 +2392,9 @@ interface Document {
|
|
|
2371
2392
|
_order?: string | null;
|
|
2372
2393
|
tenant?: (string | null) | Tenant;
|
|
2373
2394
|
title: string;
|
|
2395
|
+
/**
|
|
2396
|
+
* Short summary for listing/cards
|
|
2397
|
+
*/
|
|
2374
2398
|
description?: string | null;
|
|
2375
2399
|
content: {
|
|
2376
2400
|
root: {
|
|
@@ -2480,6 +2504,9 @@ interface DocumentType {
|
|
|
2480
2504
|
*/
|
|
2481
2505
|
generateSlug?: boolean | null;
|
|
2482
2506
|
slug?: string | null;
|
|
2507
|
+
/**
|
|
2508
|
+
* Short summary for listing/cards
|
|
2509
|
+
*/
|
|
2483
2510
|
description?: string | null;
|
|
2484
2511
|
image?: (string | null) | Image;
|
|
2485
2512
|
color?: string | null;
|
|
@@ -2501,6 +2528,9 @@ interface DocumentCategory {
|
|
|
2501
2528
|
*/
|
|
2502
2529
|
generateSlug?: boolean | null;
|
|
2503
2530
|
slug?: string | null;
|
|
2531
|
+
/**
|
|
2532
|
+
* Short summary for listing/cards
|
|
2533
|
+
*/
|
|
2504
2534
|
description?: string | null;
|
|
2505
2535
|
image?: (string | null) | Image;
|
|
2506
2536
|
parent?: (string | null) | DocumentCategory;
|
|
@@ -2518,6 +2548,9 @@ interface Playlist {
|
|
|
2518
2548
|
_order?: string | null;
|
|
2519
2549
|
tenant?: (string | null) | Tenant;
|
|
2520
2550
|
title: string;
|
|
2551
|
+
/**
|
|
2552
|
+
* Short summary for listing/cards
|
|
2553
|
+
*/
|
|
2521
2554
|
description?: string | null;
|
|
2522
2555
|
videos?: (string | Video)[] | null;
|
|
2523
2556
|
categories?: (string | PlaylistCategory)[] | null;
|
|
@@ -2593,6 +2626,9 @@ interface PlaylistCategory {
|
|
|
2593
2626
|
*/
|
|
2594
2627
|
generateSlug?: boolean | null;
|
|
2595
2628
|
slug?: string | null;
|
|
2629
|
+
/**
|
|
2630
|
+
* Short summary for listing/cards
|
|
2631
|
+
*/
|
|
2596
2632
|
description?: string | null;
|
|
2597
2633
|
image?: (string | null) | Image;
|
|
2598
2634
|
parent?: (string | null) | PlaylistCategory;
|
|
@@ -2615,6 +2651,9 @@ interface PlaylistTag {
|
|
|
2615
2651
|
*/
|
|
2616
2652
|
generateSlug?: boolean | null;
|
|
2617
2653
|
slug?: string | null;
|
|
2654
|
+
/**
|
|
2655
|
+
* Short summary for listing/cards
|
|
2656
|
+
*/
|
|
2618
2657
|
description?: string | null;
|
|
2619
2658
|
image?: (string | null) | Image;
|
|
2620
2659
|
parent?: (string | null) | PlaylistTag;
|
|
@@ -2666,6 +2705,9 @@ interface Gallery {
|
|
|
2666
2705
|
_order?: string | null;
|
|
2667
2706
|
tenant?: (string | null) | Tenant;
|
|
2668
2707
|
title: string;
|
|
2708
|
+
/**
|
|
2709
|
+
* Short summary for listing/cards
|
|
2710
|
+
*/
|
|
2669
2711
|
description?: string | null;
|
|
2670
2712
|
videos?: (string | Video)[] | null;
|
|
2671
2713
|
categories?: (string | GalleryCategory)[] | null;
|
|
@@ -2737,6 +2779,9 @@ interface GalleryCategory {
|
|
|
2737
2779
|
*/
|
|
2738
2780
|
generateSlug?: boolean | null;
|
|
2739
2781
|
slug?: string | null;
|
|
2782
|
+
/**
|
|
2783
|
+
* Short summary for listing/cards
|
|
2784
|
+
*/
|
|
2740
2785
|
description?: string | null;
|
|
2741
2786
|
image?: (string | null) | Image;
|
|
2742
2787
|
parent?: (string | null) | GalleryCategory;
|
|
@@ -2759,6 +2804,9 @@ interface GalleryTag {
|
|
|
2759
2804
|
*/
|
|
2760
2805
|
generateSlug?: boolean | null;
|
|
2761
2806
|
slug?: string | null;
|
|
2807
|
+
/**
|
|
2808
|
+
* Short summary for listing/cards
|
|
2809
|
+
*/
|
|
2762
2810
|
description?: string | null;
|
|
2763
2811
|
image?: (string | null) | Image;
|
|
2764
2812
|
parent?: (string | null) | GalleryTag;
|
|
@@ -2805,6 +2853,9 @@ interface Flow {
|
|
|
2805
2853
|
id: string;
|
|
2806
2854
|
tenant?: (string | null) | Tenant;
|
|
2807
2855
|
title: string;
|
|
2856
|
+
/**
|
|
2857
|
+
* Short summary for listing/cards
|
|
2858
|
+
*/
|
|
2808
2859
|
description?: string | null;
|
|
2809
2860
|
videos?: (string | Video)[] | null;
|
|
2810
2861
|
canvas: {
|
|
@@ -2875,6 +2926,9 @@ interface FlowCategory {
|
|
|
2875
2926
|
*/
|
|
2876
2927
|
generateSlug?: boolean | null;
|
|
2877
2928
|
slug?: string | null;
|
|
2929
|
+
/**
|
|
2930
|
+
* Short summary for listing/cards
|
|
2931
|
+
*/
|
|
2878
2932
|
description?: string | null;
|
|
2879
2933
|
image?: (string | null) | Image;
|
|
2880
2934
|
parent?: (string | null) | FlowCategory;
|
|
@@ -2897,6 +2951,9 @@ interface FlowTag {
|
|
|
2897
2951
|
*/
|
|
2898
2952
|
generateSlug?: boolean | null;
|
|
2899
2953
|
slug?: string | null;
|
|
2954
|
+
/**
|
|
2955
|
+
* Short summary for listing/cards
|
|
2956
|
+
*/
|
|
2900
2957
|
description?: string | null;
|
|
2901
2958
|
image?: (string | null) | Image;
|
|
2902
2959
|
parent?: (string | null) | FlowTag;
|
|
@@ -3011,6 +3068,9 @@ interface Form {
|
|
|
3011
3068
|
_order?: string | null;
|
|
3012
3069
|
tenant?: (string | null) | Tenant;
|
|
3013
3070
|
title: string;
|
|
3071
|
+
/**
|
|
3072
|
+
* Short summary for listing/cards
|
|
3073
|
+
*/
|
|
3014
3074
|
description?: string | null;
|
|
3015
3075
|
status: 'active' | 'inactive';
|
|
3016
3076
|
/**
|
|
@@ -1302,6 +1302,9 @@ interface VideoCategory {
|
|
|
1302
1302
|
*/
|
|
1303
1303
|
generateSlug?: boolean | null;
|
|
1304
1304
|
slug?: string | null;
|
|
1305
|
+
/**
|
|
1306
|
+
* Short summary for listing/cards
|
|
1307
|
+
*/
|
|
1305
1308
|
description?: string | null;
|
|
1306
1309
|
image?: (string | null) | Image;
|
|
1307
1310
|
parent?: (string | null) | VideoCategory;
|
|
@@ -1324,6 +1327,9 @@ interface VideoTag {
|
|
|
1324
1327
|
*/
|
|
1325
1328
|
generateSlug?: boolean | null;
|
|
1326
1329
|
slug?: string | null;
|
|
1330
|
+
/**
|
|
1331
|
+
* Short summary for listing/cards
|
|
1332
|
+
*/
|
|
1327
1333
|
description?: string | null;
|
|
1328
1334
|
image?: (string | null) | Image;
|
|
1329
1335
|
parent?: (string | null) | VideoTag;
|
|
@@ -1450,6 +1456,9 @@ interface ProductCategory {
|
|
|
1450
1456
|
*/
|
|
1451
1457
|
generateSlug?: boolean | null;
|
|
1452
1458
|
slug?: string | null;
|
|
1459
|
+
/**
|
|
1460
|
+
* Short summary for listing/cards
|
|
1461
|
+
*/
|
|
1453
1462
|
description?: string | null;
|
|
1454
1463
|
image?: (string | null) | Image;
|
|
1455
1464
|
parent?: (string | null) | ProductCategory;
|
|
@@ -1472,6 +1481,9 @@ interface ProductTag {
|
|
|
1472
1481
|
*/
|
|
1473
1482
|
generateSlug?: boolean | null;
|
|
1474
1483
|
slug?: string | null;
|
|
1484
|
+
/**
|
|
1485
|
+
* Short summary for listing/cards
|
|
1486
|
+
*/
|
|
1475
1487
|
description?: string | null;
|
|
1476
1488
|
image?: (string | null) | Image;
|
|
1477
1489
|
parent?: (string | null) | ProductTag;
|
|
@@ -1490,7 +1502,7 @@ interface Brand {
|
|
|
1490
1502
|
tenant?: (string | null) | Tenant;
|
|
1491
1503
|
name: string;
|
|
1492
1504
|
/**
|
|
1493
|
-
* Short
|
|
1505
|
+
* Short summary for listing/cards
|
|
1494
1506
|
*/
|
|
1495
1507
|
description?: string | null;
|
|
1496
1508
|
content?: {
|
|
@@ -1810,6 +1822,9 @@ interface CustomerGroup {
|
|
|
1810
1822
|
*/
|
|
1811
1823
|
generateSlug?: boolean | null;
|
|
1812
1824
|
slug?: string | null;
|
|
1825
|
+
/**
|
|
1826
|
+
* Short summary for listing/cards
|
|
1827
|
+
*/
|
|
1813
1828
|
description?: string | null;
|
|
1814
1829
|
color?: string | null;
|
|
1815
1830
|
image?: (string | null) | Image;
|
|
@@ -2213,7 +2228,7 @@ interface Post {
|
|
|
2213
2228
|
title: string;
|
|
2214
2229
|
subtitle?: string | null;
|
|
2215
2230
|
/**
|
|
2216
|
-
*
|
|
2231
|
+
* Short summary for listing/cards
|
|
2217
2232
|
*/
|
|
2218
2233
|
description?: string | null;
|
|
2219
2234
|
content?: {
|
|
@@ -2332,6 +2347,9 @@ interface PostCategory {
|
|
|
2332
2347
|
*/
|
|
2333
2348
|
generateSlug?: boolean | null;
|
|
2334
2349
|
slug?: string | null;
|
|
2350
|
+
/**
|
|
2351
|
+
* Short summary for listing/cards
|
|
2352
|
+
*/
|
|
2335
2353
|
description?: string | null;
|
|
2336
2354
|
image?: (string | null) | Image;
|
|
2337
2355
|
parent?: (string | null) | PostCategory;
|
|
@@ -2354,6 +2372,9 @@ interface PostTag {
|
|
|
2354
2372
|
*/
|
|
2355
2373
|
generateSlug?: boolean | null;
|
|
2356
2374
|
slug?: string | null;
|
|
2375
|
+
/**
|
|
2376
|
+
* Short summary for listing/cards
|
|
2377
|
+
*/
|
|
2357
2378
|
description?: string | null;
|
|
2358
2379
|
image?: (string | null) | Image;
|
|
2359
2380
|
parent?: (string | null) | PostTag;
|
|
@@ -2371,6 +2392,9 @@ interface Document {
|
|
|
2371
2392
|
_order?: string | null;
|
|
2372
2393
|
tenant?: (string | null) | Tenant;
|
|
2373
2394
|
title: string;
|
|
2395
|
+
/**
|
|
2396
|
+
* Short summary for listing/cards
|
|
2397
|
+
*/
|
|
2374
2398
|
description?: string | null;
|
|
2375
2399
|
content: {
|
|
2376
2400
|
root: {
|
|
@@ -2480,6 +2504,9 @@ interface DocumentType {
|
|
|
2480
2504
|
*/
|
|
2481
2505
|
generateSlug?: boolean | null;
|
|
2482
2506
|
slug?: string | null;
|
|
2507
|
+
/**
|
|
2508
|
+
* Short summary for listing/cards
|
|
2509
|
+
*/
|
|
2483
2510
|
description?: string | null;
|
|
2484
2511
|
image?: (string | null) | Image;
|
|
2485
2512
|
color?: string | null;
|
|
@@ -2501,6 +2528,9 @@ interface DocumentCategory {
|
|
|
2501
2528
|
*/
|
|
2502
2529
|
generateSlug?: boolean | null;
|
|
2503
2530
|
slug?: string | null;
|
|
2531
|
+
/**
|
|
2532
|
+
* Short summary for listing/cards
|
|
2533
|
+
*/
|
|
2504
2534
|
description?: string | null;
|
|
2505
2535
|
image?: (string | null) | Image;
|
|
2506
2536
|
parent?: (string | null) | DocumentCategory;
|
|
@@ -2518,6 +2548,9 @@ interface Playlist {
|
|
|
2518
2548
|
_order?: string | null;
|
|
2519
2549
|
tenant?: (string | null) | Tenant;
|
|
2520
2550
|
title: string;
|
|
2551
|
+
/**
|
|
2552
|
+
* Short summary for listing/cards
|
|
2553
|
+
*/
|
|
2521
2554
|
description?: string | null;
|
|
2522
2555
|
videos?: (string | Video)[] | null;
|
|
2523
2556
|
categories?: (string | PlaylistCategory)[] | null;
|
|
@@ -2593,6 +2626,9 @@ interface PlaylistCategory {
|
|
|
2593
2626
|
*/
|
|
2594
2627
|
generateSlug?: boolean | null;
|
|
2595
2628
|
slug?: string | null;
|
|
2629
|
+
/**
|
|
2630
|
+
* Short summary for listing/cards
|
|
2631
|
+
*/
|
|
2596
2632
|
description?: string | null;
|
|
2597
2633
|
image?: (string | null) | Image;
|
|
2598
2634
|
parent?: (string | null) | PlaylistCategory;
|
|
@@ -2615,6 +2651,9 @@ interface PlaylistTag {
|
|
|
2615
2651
|
*/
|
|
2616
2652
|
generateSlug?: boolean | null;
|
|
2617
2653
|
slug?: string | null;
|
|
2654
|
+
/**
|
|
2655
|
+
* Short summary for listing/cards
|
|
2656
|
+
*/
|
|
2618
2657
|
description?: string | null;
|
|
2619
2658
|
image?: (string | null) | Image;
|
|
2620
2659
|
parent?: (string | null) | PlaylistTag;
|
|
@@ -2666,6 +2705,9 @@ interface Gallery {
|
|
|
2666
2705
|
_order?: string | null;
|
|
2667
2706
|
tenant?: (string | null) | Tenant;
|
|
2668
2707
|
title: string;
|
|
2708
|
+
/**
|
|
2709
|
+
* Short summary for listing/cards
|
|
2710
|
+
*/
|
|
2669
2711
|
description?: string | null;
|
|
2670
2712
|
videos?: (string | Video)[] | null;
|
|
2671
2713
|
categories?: (string | GalleryCategory)[] | null;
|
|
@@ -2737,6 +2779,9 @@ interface GalleryCategory {
|
|
|
2737
2779
|
*/
|
|
2738
2780
|
generateSlug?: boolean | null;
|
|
2739
2781
|
slug?: string | null;
|
|
2782
|
+
/**
|
|
2783
|
+
* Short summary for listing/cards
|
|
2784
|
+
*/
|
|
2740
2785
|
description?: string | null;
|
|
2741
2786
|
image?: (string | null) | Image;
|
|
2742
2787
|
parent?: (string | null) | GalleryCategory;
|
|
@@ -2759,6 +2804,9 @@ interface GalleryTag {
|
|
|
2759
2804
|
*/
|
|
2760
2805
|
generateSlug?: boolean | null;
|
|
2761
2806
|
slug?: string | null;
|
|
2807
|
+
/**
|
|
2808
|
+
* Short summary for listing/cards
|
|
2809
|
+
*/
|
|
2762
2810
|
description?: string | null;
|
|
2763
2811
|
image?: (string | null) | Image;
|
|
2764
2812
|
parent?: (string | null) | GalleryTag;
|
|
@@ -2805,6 +2853,9 @@ interface Flow {
|
|
|
2805
2853
|
id: string;
|
|
2806
2854
|
tenant?: (string | null) | Tenant;
|
|
2807
2855
|
title: string;
|
|
2856
|
+
/**
|
|
2857
|
+
* Short summary for listing/cards
|
|
2858
|
+
*/
|
|
2808
2859
|
description?: string | null;
|
|
2809
2860
|
videos?: (string | Video)[] | null;
|
|
2810
2861
|
canvas: {
|
|
@@ -2875,6 +2926,9 @@ interface FlowCategory {
|
|
|
2875
2926
|
*/
|
|
2876
2927
|
generateSlug?: boolean | null;
|
|
2877
2928
|
slug?: string | null;
|
|
2929
|
+
/**
|
|
2930
|
+
* Short summary for listing/cards
|
|
2931
|
+
*/
|
|
2878
2932
|
description?: string | null;
|
|
2879
2933
|
image?: (string | null) | Image;
|
|
2880
2934
|
parent?: (string | null) | FlowCategory;
|
|
@@ -2897,6 +2951,9 @@ interface FlowTag {
|
|
|
2897
2951
|
*/
|
|
2898
2952
|
generateSlug?: boolean | null;
|
|
2899
2953
|
slug?: string | null;
|
|
2954
|
+
/**
|
|
2955
|
+
* Short summary for listing/cards
|
|
2956
|
+
*/
|
|
2900
2957
|
description?: string | null;
|
|
2901
2958
|
image?: (string | null) | Image;
|
|
2902
2959
|
parent?: (string | null) | FlowTag;
|
|
@@ -3011,6 +3068,9 @@ interface Form {
|
|
|
3011
3068
|
_order?: string | null;
|
|
3012
3069
|
tenant?: (string | null) | Tenant;
|
|
3013
3070
|
title: string;
|
|
3071
|
+
/**
|
|
3072
|
+
* Short summary for listing/cards
|
|
3073
|
+
*/
|
|
3014
3074
|
description?: string | null;
|
|
3015
3075
|
status: 'active' | 'inactive';
|
|
3016
3076
|
/**
|
package/dist/realtime.cjs
CHANGED
|
@@ -202,9 +202,11 @@ var RealtimeConnection = class {
|
|
|
202
202
|
|
|
203
203
|
// src/core/client/types.ts
|
|
204
204
|
function resolveApiUrl() {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
if (typeof process !== "undefined" && process.env) {
|
|
206
|
+
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
207
|
+
if (envUrl) {
|
|
208
|
+
return envUrl.replace(/\/$/, "");
|
|
209
|
+
}
|
|
208
210
|
}
|
|
209
211
|
return "https://api.01.software";
|
|
210
212
|
}
|
package/dist/realtime.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/realtime.ts","../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["export { useRealtimeQuery } from './core/query/realtime-hooks'\nexport type {\n UseRealtimeQueryOptions,\n UseRealtimeQueryResult,\n RealtimeEvent,\n} from './core/query/realtime-hooks'\nexport { RealtimeConnection, type RealtimeListener } from './core/query/realtime'\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/realtime.ts","../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["export { useRealtimeQuery } from './core/query/realtime-hooks'\nexport type {\n UseRealtimeQueryOptions,\n UseRealtimeQueryResult,\n RealtimeEvent,\n} from './core/query/realtime-hooks'\nexport { RealtimeConnection, type RealtimeListener } from './core/query/realtime'\n","import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n if (typeof process !== 'undefined' && process.env) {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":"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","names":[]}
|
package/dist/realtime.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { R as RealtimeEvent } from './realtime-DupPIYx-.cjs';
|
|
2
2
|
export { a as RealtimeConnection, b as RealtimeListener } from './realtime-DupPIYx-.cjs';
|
|
3
|
-
import { P as PublicCollection } from './const-
|
|
4
|
-
import './payload-types-
|
|
3
|
+
import { P as PublicCollection } from './const-BsO3aVX_.cjs';
|
|
4
|
+
import './payload-types-BsjHfeAF.cjs';
|
|
5
5
|
|
|
6
6
|
interface UseRealtimeQueryOptions {
|
|
7
7
|
/** Filter events to specific collections. Empty/undefined = all collections. */
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { R as RealtimeEvent } from './realtime-DupPIYx-.js';
|
|
2
2
|
export { a as RealtimeConnection, b as RealtimeListener } from './realtime-DupPIYx-.js';
|
|
3
|
-
import { P as PublicCollection } from './const-
|
|
4
|
-
import './payload-types-
|
|
3
|
+
import { P as PublicCollection } from './const-DZyvV9wU.js';
|
|
4
|
+
import './payload-types-BsjHfeAF.js';
|
|
5
5
|
|
|
6
6
|
interface UseRealtimeQueryOptions {
|
|
7
7
|
/** Filter events to specific collections. Empty/undefined = all collections. */
|
package/dist/realtime.js
CHANGED
|
@@ -176,9 +176,11 @@ var RealtimeConnection = class {
|
|
|
176
176
|
|
|
177
177
|
// src/core/client/types.ts
|
|
178
178
|
function resolveApiUrl() {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
179
|
+
if (typeof process !== "undefined" && process.env) {
|
|
180
|
+
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
181
|
+
if (envUrl) {
|
|
182
|
+
return envUrl.replace(/\/$/, "");
|
|
183
|
+
}
|
|
182
184
|
}
|
|
183
185
|
return "https://api.01.software";
|
|
184
186
|
}
|
package/dist/realtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,sBAAsB;;;ACa/B,IAAM,0BAA0B;AAChC,IAAM,sBAAsB;AAC5B,IAAM,2BAA2B;AACjC,IAAM,uBAAuB;AAEtB,IAAM,qBAAN,MAAyB;AAAA,EAQ9B,YACU,SACA,WACA,UACA,aACR;AAJQ;AACA;AACA;AACA;AAXV,SAAQ,kBAA0C;AAClD,SAAQ,mBAAmB;AAC3B,SAAQ,kBAAkB;AAC1B,SAAQ,iBAAuD;AAC/D,SAAQ,YAAY,oBAAI,IAAsB;AAC9C,SAAQ,aAAa;AAAA,EAOlB;AAAA,EAEH,IAAI,YAAqB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,YAAY,IAAkC;AAC5C,SAAK,UAAU,IAAI,EAAE;AACrB,WAAO,MAAM,KAAK,UAAU,OAAO,EAAE;AAAA,EACvC;AAAA,EAEA,UAAgB;AACd,QAAI,KAAK,gBAAiB;AAE1B,SAAK,kBAAkB,IAAI,gBAAgB;AAC3C,SAAK,YAAY,KAAK,gBAAgB,MAAM;AAAA,EAC9C;AAAA,EAEA,aAAmB;AACjB,SAAK,aAAa;AAClB,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,MAAM;AAC3B,WAAK,kBAAkB;AAAA,IACzB;AACA,SAAK,mBAAmB;AACxB,SAAK,kBAAkB;AAAA,EACzB;AAAA,EAEc,YAAY,QAAoC;AAAA;AAhEhE;AAiEI,YAAM,QAAQ,KAAK,SAAS;AAC5B,UAAI,CAAC,OAAO;AACV,aAAK;AACL,YAAI,KAAK,mBAAmB,sBAAsB;AAEhD,eAAK,aAAa;AAClB,eAAK,kBAAkB;AACvB;AAAA,QACF;AACA,aAAK,kBAAkB;AACvB;AAAA,MACF;AACA,WAAK,kBAAkB;AAEvB,YAAM,WAAS,UAAK,gBAAL,mBAAkB,UAC7B,gBAAgB,KAAK,YAAY,KAAK,GAAG,CAAC,KAC1C;AACJ,YAAM,MAAM,GAAG,KAAK,OAAO,qBAAqB,MAAM;AAEtD,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,KAAK;AAAA,UAChC,SAAS;AAAA,YACP,gBAAgB,KAAK;AAAA,YACrB,eAAe,UAAU,KAAK;AAAA,UAChC;AAAA,UACA;AAAA,QACF,CAAC;AAED,YAAI,CAAC,SAAS,IAAI;AAChB,cAAI,SAAS,WAAW,KAAK;AAE3B,iBAAK,kBAAkB;AACvB;AAAA,UACF;AACA,gBAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,EAAE;AAAA,QAC7D;AAEA,YAAI,CAAC,SAAS,MAAM;AAClB,gBAAM,IAAI,MAAM,0BAA0B;AAAA,QAC5C;AAEA,aAAK,aAAa;AAClB,aAAK,mBAAmB;AAExB,cAAM,KAAK,WAAW,SAAS,MAAM,MAAM;AAAA,MAC7C,SAAQ;AACN,YAAI,OAAO,QAAS;AACpB,aAAK,aAAa;AAClB,aAAK,kBAAkB;AAAA,MACzB;AAAA,IACF;AAAA;AAAA,EAEc,WACZ,MACA,QACe;AAAA;AAxHnB;AAyHI,YAAM,SAAS,KAAK,UAAU;AAC9B,YAAM,UAAU,IAAI,YAAY;AAChC,UAAI,SAAS;AACb,UAAI,eAAe;AACnB,UAAI,cAAc;AAElB,UAAI;AACF,eAAO,MAAM;AACX,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,cAAI,QAAQ,OAAO,QAAS;AAE5B,oBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAChD,gBAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,oBAAS,WAAM,IAAI,MAAV,YAAe;AAExB,qBAAW,QAAQ,OAAO;AACxB,gBAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,6BAAe,KAAK,MAAM,CAAC;AAAA,YAC7B,WAAW,KAAK,WAAW,QAAQ,GAAG;AACpC,8BAAgB,cAAc,OAAO,MAAM,KAAK,MAAM,CAAC;AAAA,YACzD,WAAW,SAAS,IAAI;AAEtB,kBAAI,iBAAiB,uBAAuB,aAAa;AACvD,oBAAI;AACF,wBAAM,QAAuB,KAAK,MAAM,WAAW;AACnD,6BAAW,YAAY,KAAK,WAAW;AACrC,wBAAI;AACF,+BAAS,KAAK;AAAA,oBAChB,SAAQ;AAAA,oBAER;AAAA,kBACF;AAAA,gBACF,SAAQ;AAAA,gBAER;AAAA,cACF;AACA,6BAAe;AACf,4BAAc;AAAA,YAChB;AAAA,UAEF;AAAA,QACF;AAAA,MACF,SAAQ;AAAA,MAER,UAAE;AACA,eAAO,YAAY;AACnB,aAAK,aAAa;AAClB,YAAI,CAAC,OAAO,SAAS;AACnB,eAAK,kBAAkB;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK,eAAgB;AAEzB,UAAM,QAAQ,KAAK;AAAA,MACjB,0BACE,KAAK,IAAI,0BAA0B,KAAK,gBAAgB;AAAA,MAC1D;AAAA,IACF;AACA,SAAK;AAEL,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,iBAAiB;AACtB,WAAK,kBAAkB,IAAI,gBAAgB;AAC3C,WAAK,YAAY,KAAK,gBAAgB,MAAM;AAAA,IAC9C,GAAG,KAAK;AAAA,EACV;AACF;;;AC7KO,SAAS,gBAAwB;AACtC,QAAM,SACJ,QAAQ,IAAI,oBAAoB,QAAQ,IAAI;AAC9C,MAAI,QAAQ;AACV,WAAO,OAAO,QAAQ,OAAO,EAAE;AAAA,EACjC;AACA,SAAO;AACT;;;ACtBO,SAAS,eAA2C,YAAe;AACxE,SAAO;AAAA,IACL,KAAK,CAAC,UAAU;AAAA,IAChB,OAAO,MAAM,CAAC,YAAY,MAAM;AAAA,IAChC,MAAM,CAAC,YAA8B,CAAC,YAAY,QAAQ,OAAO;AAAA,IACjE,SAAS,MAAM,CAAC,YAAY,QAAQ;AAAA,IACpC,QAAQ,CAAC,IAAY,YACnB,CAAC,YAAY,UAAU,IAAI,OAAO;AAAA,IACpC,WAAW,MAAM,CAAC,YAAY,UAAU;AAAA,IACxC,UAAU,CAAC,YACT,CAAC,YAAY,YAAY,OAAO;AAAA,EACpC;AACF;;;AHqBO,SAAS,iBAAiB,SAKN;AACzB,QAAM,EAAE,WAAW,UAAU,aAAa,UAAU,KAAK,IAAI;AAC7D,QAAM,cAAc,eAAe;AACnC,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,SAA+B,IAAI;AACrE,QAAM,gBAAgB,OAAkC,IAAI;AAE5D,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,UAAW;AAE5B,UAAM,UAAU,cAAc;AAC9B,UAAM,OAAO,IAAI;AAAA,MACf;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AACA,kBAAc,UAAU;AAGxB,UAAM,iBAAiB,KAAK,YAAY,CAAC,UAAU;AACjD,mBAAa,KAAK;AAGlB,YAAM,OAAO,eAAe,MAAM,UAA8B;AAChE,kBAAY,kBAAkB,EAAE,UAAU,KAAK,IAAI,CAAC;AAAA,IACtD,CAAC;AAGD,UAAM,eAAe,YAAY,MAAM;AACrC,mBAAa,KAAK,SAAS;AAAA,IAC7B,GAAG,GAAI;AAEP,SAAK,QAAQ;AAEb,WAAO,MAAM;AACX,WAAK,WAAW;AAChB,qBAAe;AACf,oBAAc,YAAY;AAC1B,oBAAc,UAAU;AACxB,mBAAa,KAAK;AAAA,IACpB;AAAA,EAEF,GAAG,CAAC,WAAW,SAAS,2CAAa,KAAK,IAAI,CAAC;AAE/C,SAAO,EAAE,WAAW,UAAU;AAChC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/core/query/realtime-hooks.ts","../src/core/query/realtime.ts","../src/core/client/types.ts","../src/core/query/query-keys.ts"],"sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { RealtimeConnection, type RealtimeEvent } from './realtime'\nimport { resolveApiUrl, type PublicCollection } from '../client/types'\nimport { collectionKeys } from './query-keys'\n\nexport type { RealtimeEvent }\n\nexport interface UseRealtimeQueryOptions {\n /** Filter events to specific collections. Empty/undefined = all collections. */\n collections?: PublicCollection[]\n /** Enable/disable the SSE connection. Default: true. */\n enabled?: boolean\n}\n\nexport interface UseRealtimeQueryResult {\n /** Whether the SSE connection is currently active. */\n connected: boolean\n /** The last received event, or null. */\n lastEvent: RealtimeEvent | null\n}\n\n/**\n * React hook that subscribes to real-time collection change events via SSE.\n * Automatically invalidates React Query cache when changes are detected.\n *\n * @example\n * ```tsx\n * const { connected } = useRealtimeQuery({\n * clientKey: 'your-key',\n * getToken: () => client.customer.getToken(),\n * collections: ['products', 'orders'],\n * })\n * ```\n */\nexport function useRealtimeQuery(options: {\n clientKey: string\n getToken: () => string | null\n collections?: PublicCollection[]\n enabled?: boolean\n}): UseRealtimeQueryResult {\n const { clientKey, getToken, collections, enabled = true } = options\n const queryClient = useQueryClient()\n const [connected, setConnected] = useState(false)\n const [lastEvent, setLastEvent] = useState<RealtimeEvent | null>(null)\n const connectionRef = useRef<RealtimeConnection | null>(null)\n\n useEffect(() => {\n if (!enabled || !clientKey) return\n\n const baseUrl = resolveApiUrl()\n const conn = new RealtimeConnection(\n baseUrl,\n clientKey,\n getToken,\n collections,\n )\n connectionRef.current = conn\n\n // Listen for events and invalidate queries\n const removeListener = conn.addListener((event) => {\n setLastEvent(event)\n\n // Invalidate all queries for the changed collection\n const keys = collectionKeys(event.collection as PublicCollection)\n queryClient.invalidateQueries({ queryKey: keys.all })\n })\n\n // Track connection state\n const pollInterval = setInterval(() => {\n setConnected(conn.connected)\n }, 1000)\n\n conn.connect()\n\n return () => {\n conn.disconnect()\n removeListener()\n clearInterval(pollInterval)\n connectionRef.current = null\n setConnected(false)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [clientKey, enabled, collections?.join(',')])\n\n return { connected, lastEvent }\n}\n","/**\n * Fetch-based SSE connection manager for real-time collection change events.\n * Uses fetch + ReadableStream to support custom auth headers (unlike native EventSource).\n */\n\nexport interface RealtimeEvent {\n collection: string\n operation: string\n id: string | null\n timestamp: string\n}\n\nexport type RealtimeListener = (event: RealtimeEvent) => void\n\nconst INITIAL_RECONNECT_DELAY = 1_000\nconst MAX_RECONNECT_DELAY = 30_000\nconst RECONNECT_BACKOFF_FACTOR = 2\nconst MAX_NO_TOKEN_RETRIES = 5\n\nexport class RealtimeConnection {\n private abortController: AbortController | null = null\n private reconnectAttempt = 0\n private noTokenAttempts = 0\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null\n private listeners = new Set<RealtimeListener>()\n private _connected = false\n\n constructor(\n private baseUrl: string,\n private clientKey: string,\n private getToken: () => string | null,\n private collections?: string[],\n ) {}\n\n get connected(): boolean {\n return this._connected\n }\n\n addListener(fn: RealtimeListener): () => void {\n this.listeners.add(fn)\n return () => this.listeners.delete(fn)\n }\n\n connect(): void {\n if (this.abortController) return // Already connecting/connected\n\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }\n\n disconnect(): void {\n this._connected = false\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer)\n this.reconnectTimer = null\n }\n if (this.abortController) {\n this.abortController.abort()\n this.abortController = null\n }\n this.reconnectAttempt = 0\n this.noTokenAttempts = 0\n }\n\n private async startStream(signal: AbortSignal): Promise<void> {\n const token = this.getToken()\n if (!token) {\n this.noTokenAttempts++\n if (this.noTokenAttempts >= MAX_NO_TOKEN_RETRIES) {\n // Stop reconnecting — no token available after multiple attempts\n this._connected = false\n this.abortController = null\n return\n }\n this.scheduleReconnect()\n return\n }\n this.noTokenAttempts = 0\n\n const params = this.collections?.length\n ? `?collections=${this.collections.join(',')}`\n : ''\n const url = `${this.baseUrl}/api/events/stream${params}`\n\n try {\n const response = await fetch(url, {\n headers: {\n 'X-Client-Key': this.clientKey,\n Authorization: `Bearer ${token}`,\n },\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 401) {\n // Token expired — try reconnecting (will get fresh token)\n this.scheduleReconnect()\n return\n }\n throw new Error(`SSE connection failed: ${response.status}`)\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body')\n }\n\n this._connected = true\n this.reconnectAttempt = 0\n\n await this.readStream(response.body, signal)\n } catch {\n if (signal.aborted) return // Intentional disconnect\n this._connected = false\n this.scheduleReconnect()\n }\n }\n\n private async readStream(\n body: ReadableStream<Uint8Array>,\n signal: AbortSignal,\n ): Promise<void> {\n const reader = body.getReader()\n const decoder = new TextDecoder()\n let buffer = ''\n let currentEvent = ''\n let currentData = ''\n\n try {\n while (true) {\n const { done, value } = await reader.read()\n if (done || signal.aborted) break\n\n buffer += decoder.decode(value, { stream: true })\n const lines = buffer.split('\\n')\n buffer = lines.pop() ?? '' // Keep incomplete last line in buffer\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7)\n } else if (line.startsWith('data: ')) {\n currentData += (currentData ? '\\n' : '') + line.slice(6)\n } else if (line === '') {\n // Empty line = end of event\n if (currentEvent === 'collection:change' && currentData) {\n try {\n const event: RealtimeEvent = JSON.parse(currentData)\n for (const listener of this.listeners) {\n try {\n listener(event)\n } catch {\n // Listener error — ignore\n }\n }\n } catch {\n // Malformed JSON — ignore\n }\n }\n currentEvent = ''\n currentData = ''\n }\n // Ignore comment lines (: heartbeat)\n }\n }\n } catch {\n // Stream read error\n } finally {\n reader.releaseLock()\n this._connected = false\n if (!signal.aborted) {\n this.scheduleReconnect()\n }\n }\n }\n\n private scheduleReconnect(): void {\n if (this.reconnectTimer) return\n\n const delay = Math.min(\n INITIAL_RECONNECT_DELAY *\n Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),\n MAX_RECONNECT_DELAY,\n )\n this.reconnectAttempt++\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null\n this.abortController = new AbortController()\n this.startStream(this.abortController.signal)\n }, delay)\n }\n}\n","import type { Sort, Where } from 'payload'\n\nimport type { Collection, PublicCollection } from '../collection/const'\n\nexport type { Collection, PublicCollection }\n\n// ============================================================================\n// API URL Configuration\n// ============================================================================\n\ndeclare const __DEFAULT_API_URL__: string\n\n/**\n * API URL을 반환합니다.\n * 환경변수 SOFTWARE_API_URL 또는 NEXT_PUBLIC_SOFTWARE_API_URL로 오버라이드 가능.\n * 빌드 시 버전에 따라 기본값 결정: dev 빌드 → api-dev, 정식 → api.01.software\n */\nexport function resolveApiUrl(): string {\n if (typeof process !== 'undefined' && process.env) {\n const envUrl =\n process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL\n if (envUrl) {\n return envUrl.replace(/\\/$/, '')\n }\n }\n return __DEFAULT_API_URL__\n}\n\n// ============================================================================\n// Client Configuration\n// ============================================================================\n\nexport interface ClientBrowserConfig {\n clientKey: string\n /**\n * Customer authentication options.\n * Used to initialize CustomerAuth on BrowserClient.\n */\n customer?: {\n /**\n * Persist token in localStorage. Defaults to `true`.\n * - `true` (default): uses key `'customer-token'`\n * - `string`: uses the given string as localStorage key\n * - `false`: disables persistence (token/onTokenChange used instead)\n *\n * Handles SSR safely (no-op on server).\n * When enabled, `token` and `onTokenChange` are ignored.\n */\n persist?: boolean | string\n /** Initial token (e.g. from SSR cookie) */\n token?: string\n /** Called when token changes (login/logout) — use to persist in localStorage/cookie */\n onTokenChange?: (token: string | null) => void\n }\n}\n\nexport interface ClientServerConfig extends ClientBrowserConfig {\n secretKey: string\n}\n\nexport interface ClientMetadata {\n userAgent?: string\n timestamp: number\n}\n\nexport interface ClientState {\n metadata: ClientMetadata\n}\n\n// ============================================================================\n// API Response Types\n// ============================================================================\n\nexport interface PaginationMeta {\n page: number\n limit: number\n totalDocs: number\n totalPages: number\n hasNextPage: boolean\n hasPrevPage: boolean\n pagingCounter: number\n prevPage: number | null\n nextPage: number | null\n}\n\n// ============================================================================\n// Payload CMS Native Response Types\n// ============================================================================\n\n/**\n * Payload CMS Find (List) Response\n * GET /api/{collection}\n */\nexport interface PayloadFindResponse<T = unknown> {\n docs: T[]\n totalDocs: number\n limit: number\n totalPages: number\n page: number\n pagingCounter: number\n hasPrevPage: boolean\n hasNextPage: boolean\n prevPage: number | null\n nextPage: number | null\n}\n\n/**\n * Payload CMS Create/Update Response\n * POST /api/{collection}\n * PATCH /api/{collection}/{id}\n */\nexport interface PayloadMutationResponse<T = unknown> {\n message: string\n doc: T\n errors?: unknown[]\n}\n\n// ============================================================================\n// Query Options\n// ============================================================================\n\nexport interface ApiQueryOptions {\n page?: number\n limit?: number\n sort?: Sort\n where?: Where\n depth?: number\n select?: Record<string, boolean>\n}\n\nexport interface ApiQueryReactOptions {\n keepPreviousData?: boolean\n}\n\n// ============================================================================\n// Debug & Retry Configuration\n// ============================================================================\n\nexport interface DebugConfig {\n logRequests?: boolean\n logResponses?: boolean\n logErrors?: boolean\n}\n\nexport interface RetryConfig {\n maxRetries?: number\n retryableStatuses?: number[]\n retryDelay?: (attempt: number) => number\n}\n\n\n// ============================================================================\n// Collection Types (re-exported from collection/const)\n// ============================================================================\n\n// ============================================================================\n// Type Utilities\n// ============================================================================\n\nexport type DeepPartial<T> = {\n [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]\n}\n\nexport type ExtractArrayType<T> = T extends (infer U)[] ? U : never\n","import type { PublicCollection, ApiQueryOptions } from '../client/types'\n\nexport function collectionKeys<T extends PublicCollection>(collection: T) {\n return {\n all: [collection] as const,\n lists: () => [collection, 'list'] as const,\n list: (options?: ApiQueryOptions) => [collection, 'list', options] as const,\n details: () => [collection, 'detail'] as const,\n detail: (id: string, options?: ApiQueryOptions) =>\n [collection, 'detail', id, options] as const,\n infinites: () => [collection, 'infinite'] as const,\n infinite: (options?: Omit<ApiQueryOptions, 'page'>) =>\n [collection, 'infinite', options] as const,\n }\n}\n\nexport const customerKeys = {\n all: ['customer'] as const,\n me: () => ['customer', 'me'] as const,\n}\n"],"mappings":"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","names":[]}
|