dolphin-server-modules 2.12.0 → 2.12.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/client/api.d.ts +24 -0
- package/dist/client/api.js +113 -0
- package/dist/client/api.js.map +1 -0
- package/dist/client/auth.d.ts +53 -0
- package/dist/client/auth.js +121 -0
- package/dist/client/auth.js.map +1 -0
- package/dist/client/core.d.ts +97 -0
- package/dist/client/core.js +342 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/dom.d.ts +1 -0
- package/dist/client/dom.js +228 -0
- package/dist/client/dom.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +12 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/store.d.ts +18 -0
- package/dist/client/store.js +132 -0
- package/dist/client/store.js.map +1 -0
- package/dist/client.test.d.ts +1 -22
- package/dist/client.test.js +1 -1
- package/dist/client.test.js.map +1 -1
- package/dist/client_dom.test.d.ts +1 -0
- package/dist/client_dom.test.js +99 -0
- package/dist/client_dom.test.js.map +1 -0
- package/dist/server/client-serve.js +1 -1
- package/dist/server/client-serve.js.map +1 -1
- package/package.json +178 -176
- package/scripts/client.js +874 -980
package/scripts/client.js
CHANGED
|
@@ -1,980 +1,874 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.user =
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
*
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
*
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
/** @private */
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
/** @
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
this.
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
this.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const formData = new FormData(e.target);
|
|
876
|
-
const data = Object.fromEntries(formData.entries());
|
|
877
|
-
|
|
878
|
-
if (rtTopic) {
|
|
879
|
-
this.publish(rtTopic, data);
|
|
880
|
-
} else if (apiTarget) {
|
|
881
|
-
const parts = apiTarget.trim().split(' ');
|
|
882
|
-
const method = parts.length > 1 ? parts[0].toUpperCase() : 'POST';
|
|
883
|
-
const path = parts.length > 1 ? parts[1] : parts[0];
|
|
884
|
-
try {
|
|
885
|
-
const result = await this.api.request(method, path, data);
|
|
886
|
-
const resultBind = e.target.getAttribute('data-api-result');
|
|
887
|
-
if (resultBind) this._updateDOM(resultBind, result);
|
|
888
|
-
} catch (err) {
|
|
889
|
-
console.error('[Dolphin] API Submit Error:', err);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
});
|
|
894
|
-
|
|
895
|
-
// 3. Listen for clicks (RT + API)
|
|
896
|
-
document.addEventListener('click', async (e) => {
|
|
897
|
-
if (!e.target || !e.target.closest) return;
|
|
898
|
-
|
|
899
|
-
const rtBtn = e.target.closest('[data-rt-click]');
|
|
900
|
-
const apiBtn = e.target.closest('[data-api-click]');
|
|
901
|
-
|
|
902
|
-
if (rtBtn) {
|
|
903
|
-
const topic = rtBtn.getAttribute('data-rt-click');
|
|
904
|
-
const actionData = rtBtn.getAttribute('data-rt-payload');
|
|
905
|
-
const payload = actionData ? JSON.parse(actionData) : {};
|
|
906
|
-
this.publish(topic, payload);
|
|
907
|
-
} else if (apiBtn) {
|
|
908
|
-
const apiTarget = apiBtn.getAttribute('data-api-click');
|
|
909
|
-
const actionData = apiBtn.getAttribute('data-api-payload');
|
|
910
|
-
const payload = actionData ? JSON.parse(actionData) : null;
|
|
911
|
-
const parts = apiTarget.trim().split(' ');
|
|
912
|
-
const method = parts.length > 1 ? parts[0].toUpperCase() : 'POST';
|
|
913
|
-
const path = parts.length > 1 ? parts[1] : parts[0];
|
|
914
|
-
try {
|
|
915
|
-
const result = await this.api.request(method, path, payload);
|
|
916
|
-
const resultBind = apiBtn.getAttribute('data-api-result');
|
|
917
|
-
if (resultBind) this._updateDOM(resultBind, result);
|
|
918
|
-
} catch (err) {
|
|
919
|
-
console.error('[Dolphin] API Click Error:', err);
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
});
|
|
923
|
-
|
|
924
|
-
// 4. Update DOM when RT data arrives
|
|
925
|
-
// Note: Subscribe to all topics ('#') to auto-update DOM bindings
|
|
926
|
-
this.subscribe('#', (payload, topic) => {
|
|
927
|
-
this._updateDOM(topic, payload);
|
|
928
|
-
});
|
|
929
|
-
|
|
930
|
-
// 5. Auto-fetch API GET bindings
|
|
931
|
-
this._scanAndFetchAPIBinds();
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
/** @private */
|
|
935
|
-
async _scanAndFetchAPIBinds() {
|
|
936
|
-
if (typeof document === 'undefined') return;
|
|
937
|
-
const elements = document.querySelectorAll('[data-api-get]');
|
|
938
|
-
for (const el of elements) {
|
|
939
|
-
const path = el.getAttribute('data-api-get');
|
|
940
|
-
if (!path) continue;
|
|
941
|
-
try {
|
|
942
|
-
const result = await this.api.get(path);
|
|
943
|
-
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
|
|
944
|
-
el.value = typeof result === 'object' ? (result.value !== undefined ? result.value : '') : result;
|
|
945
|
-
} else {
|
|
946
|
-
el.innerHTML = typeof result === 'object' ? (result.html || result.text || JSON.stringify(result)) : result;
|
|
947
|
-
}
|
|
948
|
-
} catch(e) {
|
|
949
|
-
console.error('[Dolphin] API Get Error:', e);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
/** @private */
|
|
955
|
-
_updateDOM(topic, payload) {
|
|
956
|
-
if (typeof document === 'undefined') return;
|
|
957
|
-
const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
|
|
958
|
-
elements.forEach(el => {
|
|
959
|
-
if (el.tagName === 'INPUT' || el.tagName === 'TEXTAREA') {
|
|
960
|
-
el.value = typeof payload === 'object' ? (payload.value !== undefined ? payload.value : '') : payload;
|
|
961
|
-
} else {
|
|
962
|
-
el.innerHTML = typeof payload === 'object' ? (payload.html || payload.text || JSON.stringify(payload)) : payload;
|
|
963
|
-
}
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
969
|
-
|
|
970
|
-
if (typeof window !== 'undefined') {
|
|
971
|
-
window.DolphinClient = DolphinClient;
|
|
972
|
-
window.dolphin = new DolphinClient();
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
if (typeof module !== 'undefined' && module.exports) {
|
|
976
|
-
module.exports = { DolphinClient };
|
|
977
|
-
}
|
|
978
|
-
|
|
979
|
-
// Note: No top-level `export` here so that this file can be loaded
|
|
980
|
-
// via classic <script src="..."> in browsers / React without "Unexpected token 'export'"
|
|
1
|
+
"use strict";
|
|
2
|
+
var DolphinModule = (() => {
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/client/index.ts
|
|
22
|
+
var index_exports = {};
|
|
23
|
+
__export(index_exports, {
|
|
24
|
+
DolphinClient: () => DolphinClient
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// src/client/api.ts
|
|
28
|
+
var APIHandler = class {
|
|
29
|
+
/** @param {DolphinClient} client */
|
|
30
|
+
constructor(client) {
|
|
31
|
+
this.client = client;
|
|
32
|
+
return this._createProxy([]);
|
|
33
|
+
}
|
|
34
|
+
/** @private */
|
|
35
|
+
_createProxy(pathParts) {
|
|
36
|
+
const joined = pathParts.join("/");
|
|
37
|
+
const target = (options) => this.request("GET", joined, null, options);
|
|
38
|
+
target.get = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("GET", pathOrOptions, null, options) : this.request("GET", joined, null, pathOrOptions);
|
|
39
|
+
target.post = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("POST", pathOrBody, bodyOrOptions, options) : this.request("POST", joined, pathOrBody, bodyOrOptions);
|
|
40
|
+
target.put = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PUT", pathOrBody, bodyOrOptions, options) : this.request("PUT", joined, pathOrBody, bodyOrOptions);
|
|
41
|
+
target.patch = (pathOrBody, bodyOrOptions, options) => typeof pathOrBody === "string" ? this.request("PATCH", pathOrBody, bodyOrOptions, options) : this.request("PATCH", joined, pathOrBody, bodyOrOptions);
|
|
42
|
+
target.del = (pathOrOptions, options) => typeof pathOrOptions === "string" ? this.request("DELETE", pathOrOptions, null, options) : this.request("DELETE", joined, null, pathOrOptions);
|
|
43
|
+
target.request = (method, subPath, body, options) => {
|
|
44
|
+
const finalPath = subPath ? `${joined}/${subPath.startsWith("/") ? subPath.slice(1) : subPath}` : joined;
|
|
45
|
+
return this.request(method, finalPath, body, options);
|
|
46
|
+
};
|
|
47
|
+
const methods = ["get", "post", "put", "patch", "del", "request"];
|
|
48
|
+
return new Proxy(target, {
|
|
49
|
+
get: (t, prop) => {
|
|
50
|
+
if (typeof prop === "string" && !methods.includes(prop)) {
|
|
51
|
+
return this._createProxy([...pathParts, prop]);
|
|
52
|
+
}
|
|
53
|
+
return t[prop];
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Make an HTTP request with timeout + auto token refresh.
|
|
59
|
+
* @param {string} method
|
|
60
|
+
* @param {string} path
|
|
61
|
+
* @param {any} [body]
|
|
62
|
+
* @param {RequestInit} [options]
|
|
63
|
+
* @param {boolean} [_isRetry=false] — internal: prevent infinite refresh loop
|
|
64
|
+
* @returns {Promise<any>}
|
|
65
|
+
*/
|
|
66
|
+
async request(method, path, body = null, options = {}) {
|
|
67
|
+
const _isRetry = options._isRetry === true;
|
|
68
|
+
const url = `${this.client.httpUrl}${path.startsWith("/") ? path : "/" + path}`;
|
|
69
|
+
const controller = new AbortController();
|
|
70
|
+
const timeoutId = setTimeout(
|
|
71
|
+
() => controller.abort(),
|
|
72
|
+
this.client.options.timeout
|
|
73
|
+
);
|
|
74
|
+
const headers = {
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
...options.headers || {}
|
|
77
|
+
};
|
|
78
|
+
if (this.client.accessToken) {
|
|
79
|
+
headers["Authorization"] = `Bearer ${this.client.accessToken}`;
|
|
80
|
+
}
|
|
81
|
+
const fetchOptions = { ...options };
|
|
82
|
+
delete fetchOptions._isRetry;
|
|
83
|
+
try {
|
|
84
|
+
const response = await fetch(url, {
|
|
85
|
+
method,
|
|
86
|
+
headers,
|
|
87
|
+
signal: controller.signal,
|
|
88
|
+
...body ? { body: JSON.stringify(body) } : {},
|
|
89
|
+
...fetchOptions
|
|
90
|
+
});
|
|
91
|
+
clearTimeout(timeoutId);
|
|
92
|
+
if (response.status === 401 && !_isRetry && this.client.options.autoRefreshToken) {
|
|
93
|
+
const refreshed = await this.client.auth._silentRefresh();
|
|
94
|
+
if (refreshed) {
|
|
95
|
+
return this.request(method, path, body, { ...options, _isRetry: true });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const contentType = response.headers.get("content-type") || "";
|
|
99
|
+
const data = contentType.includes("application/json") ? await response.json() : await response.text();
|
|
100
|
+
if (!response.ok) throw { status: response.status, data };
|
|
101
|
+
if (data && typeof data === "object") {
|
|
102
|
+
if (data.accessToken) {
|
|
103
|
+
this.client.setToken(data.accessToken);
|
|
104
|
+
if (data.user) this.client.auth.user = data.user;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (this.client.options.autoBroadcast && ["POST", "PUT", "PATCH", "DELETE"].includes(method.toUpperCase())) {
|
|
108
|
+
const cleanPath = path.startsWith("/") ? path.substring(1) : path;
|
|
109
|
+
this.client.publish(cleanPath, { method: method.toUpperCase(), payload: body, result: data });
|
|
110
|
+
}
|
|
111
|
+
return data;
|
|
112
|
+
} catch (err) {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
if (err.name === "AbortError") {
|
|
115
|
+
throw { status: 408, data: { error: "Request timed out" } };
|
|
116
|
+
}
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// src/client/auth.ts
|
|
123
|
+
var AuthHandler = class {
|
|
124
|
+
/** @param {DolphinClient} client */
|
|
125
|
+
constructor(client) {
|
|
126
|
+
this.client = client;
|
|
127
|
+
this.user = null;
|
|
128
|
+
this._refreshing = false;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Login with email + password.
|
|
132
|
+
* @param {string} email
|
|
133
|
+
* @param {string} password
|
|
134
|
+
*/
|
|
135
|
+
async login(email, password) {
|
|
136
|
+
const res = await this.client.api.post("/api/auth/login", { email, password });
|
|
137
|
+
if (res.accessToken) {
|
|
138
|
+
this.client.setToken(res.accessToken);
|
|
139
|
+
this.user = res.user || null;
|
|
140
|
+
}
|
|
141
|
+
return res;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Register a new account.
|
|
145
|
+
* @param {{ email: string, password: string, [key: string]: any }} data
|
|
146
|
+
*/
|
|
147
|
+
async register(data) {
|
|
148
|
+
return this.client.api.post("/api/auth/register", data);
|
|
149
|
+
}
|
|
150
|
+
/** Get current user profile. */
|
|
151
|
+
async me() {
|
|
152
|
+
const res = await this.client.api.get("/api/auth/me");
|
|
153
|
+
if (res.success) this.user = res.data;
|
|
154
|
+
return res;
|
|
155
|
+
}
|
|
156
|
+
/** Logout and clear token. */
|
|
157
|
+
async logout() {
|
|
158
|
+
try {
|
|
159
|
+
await this.client.api.post("/api/auth/logout");
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
this.client.setToken(null);
|
|
163
|
+
this.user = null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Manually refresh the access token using the httpOnly refresh-token cookie.
|
|
167
|
+
* Called automatically on 401 if autoRefreshToken is enabled.
|
|
168
|
+
* @returns {Promise<boolean>} — true if refresh succeeded
|
|
169
|
+
*/
|
|
170
|
+
async refresh() {
|
|
171
|
+
return this._silentRefresh();
|
|
172
|
+
}
|
|
173
|
+
/** @private */
|
|
174
|
+
async _silentRefresh() {
|
|
175
|
+
if (this._refreshing) return false;
|
|
176
|
+
this._refreshing = true;
|
|
177
|
+
try {
|
|
178
|
+
const res = await this.client.api.post("/api/auth/refresh", null, { _isRetry: true });
|
|
179
|
+
if (res.accessToken) {
|
|
180
|
+
this.client.setToken(res.accessToken);
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
return false;
|
|
184
|
+
} catch {
|
|
185
|
+
this.client.setToken(null);
|
|
186
|
+
return false;
|
|
187
|
+
} finally {
|
|
188
|
+
this._refreshing = false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Verify a 2FA TOTP code after login.
|
|
193
|
+
* @param {string} code — 6-digit TOTP code
|
|
194
|
+
* @param {string} [email] — email (if not already in user)
|
|
195
|
+
*/
|
|
196
|
+
async verify2FA(code, email) {
|
|
197
|
+
const payload = {
|
|
198
|
+
code,
|
|
199
|
+
email: email || this.user?.email
|
|
200
|
+
};
|
|
201
|
+
const res = await this.client.api.post("/api/auth/2fa/verify", payload);
|
|
202
|
+
if (res.accessToken) {
|
|
203
|
+
this.client.setToken(res.accessToken);
|
|
204
|
+
if (res.user) this.user = res.user;
|
|
205
|
+
}
|
|
206
|
+
return res;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Enable 2FA — returns QR code URL and secret.
|
|
210
|
+
*/
|
|
211
|
+
async enable2FA() {
|
|
212
|
+
return this.client.api.post("/api/auth/2fa/enable");
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Disable 2FA.
|
|
216
|
+
* @param {string} code — current TOTP code to confirm
|
|
217
|
+
*/
|
|
218
|
+
async disable2FA(code) {
|
|
219
|
+
return this.client.api.post("/api/auth/2fa/disable", { code });
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Request a password reset email.
|
|
223
|
+
* @param {string} email
|
|
224
|
+
*/
|
|
225
|
+
async forgotPassword(email) {
|
|
226
|
+
return this.client.api.post("/api/auth/forgot-password", { email });
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Reset password using the token from email.
|
|
230
|
+
* @param {string} token
|
|
231
|
+
* @param {string} newPassword
|
|
232
|
+
*/
|
|
233
|
+
async resetPassword(token, newPassword) {
|
|
234
|
+
return this.client.api.post("/api/auth/reset-password", { token, newPassword });
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
// src/client/store.ts
|
|
239
|
+
var DolphinStore = class {
|
|
240
|
+
/** @param {DolphinClient} client */
|
|
241
|
+
constructor(client) {
|
|
242
|
+
this.client = client;
|
|
243
|
+
this.data = /* @__PURE__ */ new Map();
|
|
244
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
245
|
+
this.subscribed = /* @__PURE__ */ new Set();
|
|
246
|
+
return new Proxy(this, {
|
|
247
|
+
get: (target, prop) => {
|
|
248
|
+
if (prop in target) return target[prop];
|
|
249
|
+
if (typeof prop === "string") return this._getCollection(prop);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/** @private */
|
|
254
|
+
_getCollection(name) {
|
|
255
|
+
if (!this.data.has(name)) {
|
|
256
|
+
const collection = {
|
|
257
|
+
_rawItems: [],
|
|
258
|
+
items: [],
|
|
259
|
+
loading: true,
|
|
260
|
+
error: null,
|
|
261
|
+
success: false,
|
|
262
|
+
_filter: null,
|
|
263
|
+
_sort: null,
|
|
264
|
+
where: (fn) => {
|
|
265
|
+
collection._filter = fn;
|
|
266
|
+
this._applyTransform(collection);
|
|
267
|
+
return collection;
|
|
268
|
+
},
|
|
269
|
+
orderBy: (key, direction = "asc") => {
|
|
270
|
+
collection._sort = { key, direction };
|
|
271
|
+
this._applyTransform(collection);
|
|
272
|
+
return collection;
|
|
273
|
+
},
|
|
274
|
+
reset: () => {
|
|
275
|
+
collection._filter = null;
|
|
276
|
+
collection._sort = null;
|
|
277
|
+
this._applyTransform(collection);
|
|
278
|
+
return collection;
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
this.data.set(name, collection);
|
|
282
|
+
this._fetchAndSync(name);
|
|
283
|
+
}
|
|
284
|
+
return this.data.get(name);
|
|
285
|
+
}
|
|
286
|
+
/** @private */
|
|
287
|
+
async _fetchAndSync(name) {
|
|
288
|
+
const state = this.data.get(name);
|
|
289
|
+
try {
|
|
290
|
+
const res = await this.client.api.get(`/${name.toLowerCase()}`);
|
|
291
|
+
state._rawItems = Array.isArray(res) ? res : res.data || [];
|
|
292
|
+
state.loading = false;
|
|
293
|
+
state.success = true;
|
|
294
|
+
state.error = null;
|
|
295
|
+
this._applyTransform(state);
|
|
296
|
+
if (!this.subscribed.has(name)) {
|
|
297
|
+
this.client.subscribe(`db:sync/${name.toLowerCase()}`, (update) => {
|
|
298
|
+
this._handleRemoteUpdate(name, update);
|
|
299
|
+
});
|
|
300
|
+
this.subscribed.add(name);
|
|
301
|
+
}
|
|
302
|
+
} catch (e) {
|
|
303
|
+
state.loading = false;
|
|
304
|
+
state.success = false;
|
|
305
|
+
state.error = e.data?.error || e.message || "Fetch failed";
|
|
306
|
+
this._notify();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
/** @private */
|
|
310
|
+
_applyTransform(state) {
|
|
311
|
+
let result = [...state._rawItems];
|
|
312
|
+
if (state._filter) result = result.filter(state._filter);
|
|
313
|
+
if (state._sort) {
|
|
314
|
+
const { key, direction } = state._sort;
|
|
315
|
+
result.sort((a, b) => {
|
|
316
|
+
if (a[key] === b[key]) return 0;
|
|
317
|
+
return (a[key] > b[key] ? 1 : -1) * (direction === "asc" ? 1 : -1);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
state.items = result;
|
|
321
|
+
this._notify();
|
|
322
|
+
}
|
|
323
|
+
/** @private */
|
|
324
|
+
_handleRemoteUpdate(collection, update) {
|
|
325
|
+
const state = this.data.get(collection);
|
|
326
|
+
if (!state) return;
|
|
327
|
+
const { type, data } = update;
|
|
328
|
+
let items = state._rawItems;
|
|
329
|
+
if (type === "create") {
|
|
330
|
+
items = [...items, data];
|
|
331
|
+
} else if (type === "update") {
|
|
332
|
+
items = items.map((i) => i.id === data.id || i._id === data._id ? { ...i, ...data } : i);
|
|
333
|
+
} else if (type === "delete") {
|
|
334
|
+
items = items.filter((i) => {
|
|
335
|
+
if (data.id != null && i.id === data.id) return false;
|
|
336
|
+
if (data._id != null && i._id === data._id) return false;
|
|
337
|
+
return true;
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
state._rawItems = items;
|
|
341
|
+
this._applyTransform(state);
|
|
342
|
+
}
|
|
343
|
+
/** Subscribe for React useSyncExternalStore */
|
|
344
|
+
subscribe(listener) {
|
|
345
|
+
this.listeners.add(listener);
|
|
346
|
+
return () => this.listeners.delete(listener);
|
|
347
|
+
}
|
|
348
|
+
/** @param {string} collection */
|
|
349
|
+
getSnapshot(collection) {
|
|
350
|
+
return this.data.get(collection) || { items: [], loading: false, error: null, success: false };
|
|
351
|
+
}
|
|
352
|
+
/** @private */
|
|
353
|
+
_notify() {
|
|
354
|
+
this.listeners.forEach((l) => l());
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
// src/client/core.ts
|
|
359
|
+
var DolphinClient = class {
|
|
360
|
+
constructor(url = "", deviceId = "", options = {}) {
|
|
361
|
+
if (!url && typeof window !== "undefined") url = window.location.host;
|
|
362
|
+
let protocol = "http:";
|
|
363
|
+
if (url.startsWith("https://")) protocol = "https:";
|
|
364
|
+
else if (url.startsWith("http://")) protocol = "http:";
|
|
365
|
+
else if (typeof window !== "undefined") protocol = window.location.protocol;
|
|
366
|
+
this.host = (url || "localhost").replace(/\/$/, "").replace(/^https?:\/\//, "");
|
|
367
|
+
this.httpUrl = `${protocol}//${this.host}`;
|
|
368
|
+
this.deviceId = deviceId || "web_" + Math.random().toString(36).substr(2, 8);
|
|
369
|
+
this.options = {
|
|
370
|
+
timeout: 15e3,
|
|
371
|
+
chunkSize: 65536,
|
|
372
|
+
// 64 KB
|
|
373
|
+
maxReconnect: 5,
|
|
374
|
+
autoRefreshToken: true,
|
|
375
|
+
...options
|
|
376
|
+
};
|
|
377
|
+
this.socket = null;
|
|
378
|
+
this.storage = typeof localStorage !== "undefined" ? localStorage : {
|
|
379
|
+
getItem: () => null,
|
|
380
|
+
setItem: () => {
|
|
381
|
+
},
|
|
382
|
+
removeItem: () => {
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
this.accessToken = this.storage.getItem("dolphin_token");
|
|
386
|
+
this.api = new APIHandler(this);
|
|
387
|
+
this.auth = new AuthHandler(this);
|
|
388
|
+
this.store = new DolphinStore(this);
|
|
389
|
+
this.handlers = /* @__PURE__ */ new Map();
|
|
390
|
+
this.signalHandlers = /* @__PURE__ */ new Set();
|
|
391
|
+
this.fileHandlers = /* @__PURE__ */ new Set();
|
|
392
|
+
this._offlineQueue = [];
|
|
393
|
+
this.reconnectAttempts = 0;
|
|
394
|
+
}
|
|
395
|
+
/** Save or clear the access token */
|
|
396
|
+
setToken(token) {
|
|
397
|
+
this.accessToken = token;
|
|
398
|
+
token ? this.storage.setItem("dolphin_token", token) : this.storage.removeItem("dolphin_token");
|
|
399
|
+
}
|
|
400
|
+
// ── WebSocket ─────────────────────────────────────────────────────────────
|
|
401
|
+
/** Connect to the Dolphin realtime server */
|
|
402
|
+
async connect() {
|
|
403
|
+
return new Promise((resolve, reject) => {
|
|
404
|
+
const protocol = this.httpUrl.startsWith("https") ? "wss:" : "ws:";
|
|
405
|
+
const wsUrl = `${protocol}//${this.host}/realtime?deviceId=${this.deviceId}`;
|
|
406
|
+
console.log(`[Dolphin] Connecting to ${wsUrl}...`);
|
|
407
|
+
this.socket = new WebSocket(wsUrl);
|
|
408
|
+
this.socket.onopen = () => {
|
|
409
|
+
console.log(`[Dolphin] Connected as "${this.deviceId}" \u{1F42C}`);
|
|
410
|
+
this.reconnectAttempts = 0;
|
|
411
|
+
this._flushOfflineQueue();
|
|
412
|
+
resolve();
|
|
413
|
+
};
|
|
414
|
+
this.socket.onmessage = (ev) => this._handleMessage(ev.data);
|
|
415
|
+
this.socket.onclose = () => {
|
|
416
|
+
console.warn("[Dolphin] Connection closed");
|
|
417
|
+
this._maybeReconnect();
|
|
418
|
+
};
|
|
419
|
+
this.socket.onerror = (err) => {
|
|
420
|
+
console.error("[Dolphin] WebSocket error:", err);
|
|
421
|
+
reject(err);
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
/** Disconnect cleanly */
|
|
426
|
+
disconnect() {
|
|
427
|
+
if (this.socket) {
|
|
428
|
+
this.socket.onclose = null;
|
|
429
|
+
this.socket.close();
|
|
430
|
+
this.socket = null;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
/** @private */
|
|
434
|
+
_handleMessage(data) {
|
|
435
|
+
try {
|
|
436
|
+
const msg = JSON.parse(data);
|
|
437
|
+
if (msg.type && msg.from && (msg.to === this.deviceId || msg.to === "all")) {
|
|
438
|
+
if (msg.msgId && msg.type !== "ACK") this._sendAck(msg.from, msg.msgId);
|
|
439
|
+
this.signalHandlers.forEach((h) => h(msg));
|
|
440
|
+
}
|
|
441
|
+
if (msg.type === "FILE_AVAILABLE") {
|
|
442
|
+
this.fileHandlers.forEach((h) => h(msg));
|
|
443
|
+
}
|
|
444
|
+
if (msg.type === "FILE_CHUNK") {
|
|
445
|
+
this.saveFileProgress(msg.fileId, msg.chunkIndex);
|
|
446
|
+
this._dispatch("file:chunk", msg);
|
|
447
|
+
this._dispatch(`file:chunk/${msg.fileId}`, msg);
|
|
448
|
+
}
|
|
449
|
+
if (msg.type === "FILE_UPLOAD_ACK") {
|
|
450
|
+
this._dispatch(`file:upload:ack/${msg.fileId}`, msg);
|
|
451
|
+
}
|
|
452
|
+
if (msg.type === "PULL_RESPONSE") {
|
|
453
|
+
this._dispatch("pull:response", msg.payload, msg.topic);
|
|
454
|
+
this._dispatch(`pull:response/${msg.topic}`, msg.payload, msg.topic);
|
|
455
|
+
}
|
|
456
|
+
if (msg.topic && msg.payload !== void 0) {
|
|
457
|
+
this.handlers.forEach((cbs, pattern) => {
|
|
458
|
+
if (this._matchTopic(pattern, msg.topic)) {
|
|
459
|
+
cbs.forEach((cb) => cb(msg.payload, msg.topic));
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
} catch {
|
|
464
|
+
this._dispatch("raw", data);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/** @private */
|
|
468
|
+
_dispatch(pattern, payload, topic) {
|
|
469
|
+
const cbs = this.handlers.get(pattern);
|
|
470
|
+
if (cbs) cbs.forEach((cb) => cb(payload, topic || pattern));
|
|
471
|
+
}
|
|
472
|
+
/** @private */
|
|
473
|
+
_sendRaw(msg) {
|
|
474
|
+
const str = typeof msg === "string" ? msg : JSON.stringify(msg);
|
|
475
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
476
|
+
this.socket.send(str);
|
|
477
|
+
} else {
|
|
478
|
+
if (this._offlineQueue.length < 100) this._offlineQueue.push(str);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
/** Flush buffered messages after reconnect @private */
|
|
482
|
+
_flushOfflineQueue() {
|
|
483
|
+
while (this._offlineQueue.length > 0) {
|
|
484
|
+
const msg = this._offlineQueue.shift();
|
|
485
|
+
if (this.socket && this.socket.readyState === WebSocket.OPEN) {
|
|
486
|
+
this.socket.send(msg);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
/** @private */
|
|
491
|
+
_sendAck(to, msgId) {
|
|
492
|
+
this._sendRaw({ type: "ACK", from: this.deviceId, to, data: { ackId: msgId }, timestamp: Date.now() });
|
|
493
|
+
}
|
|
494
|
+
/** MQTT wildcard topic matching @private */
|
|
495
|
+
_matchTopic(pattern, topic) {
|
|
496
|
+
if (pattern === topic || pattern === "#") return true;
|
|
497
|
+
const pp = pattern.split("/");
|
|
498
|
+
const tp = topic.split("/");
|
|
499
|
+
if (pp.length !== tp.length && !pattern.includes("#")) return false;
|
|
500
|
+
for (let i = 0; i < pp.length; i++) {
|
|
501
|
+
if (pp[i] === "#") return true;
|
|
502
|
+
if (pp[i] !== "+" && pp[i] !== tp[i]) return false;
|
|
503
|
+
}
|
|
504
|
+
return pp.length === tp.length;
|
|
505
|
+
}
|
|
506
|
+
/** @private */
|
|
507
|
+
_maybeReconnect() {
|
|
508
|
+
if (this.reconnectAttempts < this.options.maxReconnect) {
|
|
509
|
+
this.reconnectAttempts++;
|
|
510
|
+
const delay = Math.pow(2, this.reconnectAttempts) * 1e3;
|
|
511
|
+
console.log(`[Dolphin] Reconnecting in ${delay / 1e3}s (attempt ${this.reconnectAttempts})...`);
|
|
512
|
+
setTimeout(() => this.connect().catch(() => {
|
|
513
|
+
}), delay);
|
|
514
|
+
} else {
|
|
515
|
+
console.error("[Dolphin] Max reconnect attempts reached.");
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// ── Pub/Sub ───────────────────────────────────────────────────────────────
|
|
519
|
+
/**
|
|
520
|
+
* Subscribe to a topic (MQTT wildcards supported: + and #).
|
|
521
|
+
* @param {string} topic
|
|
522
|
+
* @param {TopicCallback} callback
|
|
523
|
+
*/
|
|
524
|
+
subscribe(topic, callback) {
|
|
525
|
+
if (!this.handlers.has(topic)) {
|
|
526
|
+
this.handlers.set(topic, /* @__PURE__ */ new Set());
|
|
527
|
+
this._sendRaw({ type: "sub", topic });
|
|
528
|
+
}
|
|
529
|
+
this.handlers.get(topic).add(callback);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Unsubscribe from a topic.
|
|
533
|
+
* @param {string} topic
|
|
534
|
+
* @param {TopicCallback} callback
|
|
535
|
+
*/
|
|
536
|
+
unsubscribe(topic, callback) {
|
|
537
|
+
if (this.handlers.has(topic)) {
|
|
538
|
+
const cbs = this.handlers.get(topic);
|
|
539
|
+
cbs.delete(callback);
|
|
540
|
+
if (cbs.size === 0) {
|
|
541
|
+
this.handlers.delete(topic);
|
|
542
|
+
this._sendRaw({ type: "unsub", topic });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Publish a message to a topic. Queued if offline.
|
|
548
|
+
* @param {string} topic
|
|
549
|
+
* @param {any} payload
|
|
550
|
+
*/
|
|
551
|
+
publish(topic, payload) {
|
|
552
|
+
this._sendRaw({ topic, payload });
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* High-frequency data push (IoT sensors).
|
|
556
|
+
* @param {string} topic
|
|
557
|
+
* @param {any} payload
|
|
558
|
+
*/
|
|
559
|
+
pubPush(topic, payload) {
|
|
560
|
+
this._sendRaw({ type: "pub", topic, payload });
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Request historical data from a topic.
|
|
564
|
+
* @param {string} topic
|
|
565
|
+
* @param {number} [count=10]
|
|
566
|
+
*/
|
|
567
|
+
subPull(topic, count = 10) {
|
|
568
|
+
this._sendRaw({ type: "PULL_REQUEST", topic, count });
|
|
569
|
+
}
|
|
570
|
+
// ── File Transfer ─────────────────────────────────────────────────────────
|
|
571
|
+
/**
|
|
572
|
+
* Upload a file to the server in chunks.
|
|
573
|
+
* @param {string} fileId
|
|
574
|
+
* @param {Blob|ArrayBuffer|Uint8Array} fileData
|
|
575
|
+
* @param {string} [filename]
|
|
576
|
+
* @param {function(number): void} [onProgress] — progress callback (0-100)
|
|
577
|
+
* @returns {Promise<void>}
|
|
578
|
+
*/
|
|
579
|
+
async pubFile(fileId, fileData, filename = "", onProgress) {
|
|
580
|
+
let buffer;
|
|
581
|
+
if (fileData instanceof Blob) {
|
|
582
|
+
buffer = await fileData.arrayBuffer();
|
|
583
|
+
} else if (fileData instanceof ArrayBuffer) {
|
|
584
|
+
buffer = fileData;
|
|
585
|
+
} else {
|
|
586
|
+
buffer = fileData.buffer || fileData;
|
|
587
|
+
}
|
|
588
|
+
const bytes = new Uint8Array(buffer);
|
|
589
|
+
const chunkSize = this.options.chunkSize;
|
|
590
|
+
const totalChunks = Math.ceil(bytes.length / chunkSize);
|
|
591
|
+
this._sendRaw({
|
|
592
|
+
type: "FILE_UPLOAD_START",
|
|
593
|
+
fileId,
|
|
594
|
+
name: filename,
|
|
595
|
+
size: bytes.length,
|
|
596
|
+
totalChunks,
|
|
597
|
+
chunkSize
|
|
598
|
+
});
|
|
599
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
600
|
+
const chunk = bytes.slice(i * chunkSize, (i + 1) * chunkSize);
|
|
601
|
+
const b64 = this._uint8ToBase64(chunk);
|
|
602
|
+
this._sendRaw({
|
|
603
|
+
type: "FILE_UPLOAD_CHUNK",
|
|
604
|
+
fileId,
|
|
605
|
+
chunkIndex: i,
|
|
606
|
+
totalChunks,
|
|
607
|
+
data: b64
|
|
608
|
+
});
|
|
609
|
+
if (onProgress) onProgress(Math.round((i + 1) / totalChunks * 100));
|
|
610
|
+
if (i % 10 === 0) await new Promise((r) => setTimeout(r, 0));
|
|
611
|
+
}
|
|
612
|
+
this._sendRaw({ type: "FILE_UPLOAD_DONE", fileId });
|
|
613
|
+
}
|
|
614
|
+
/** @private */
|
|
615
|
+
_uint8ToBase64(uint8) {
|
|
616
|
+
let binary = "";
|
|
617
|
+
for (let i = 0; i < uint8.length; i++) binary += String.fromCharCode(uint8[i]);
|
|
618
|
+
if (typeof btoa !== "undefined") return btoa(binary);
|
|
619
|
+
return Buffer.from(binary, "binary").toString("base64");
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Download a file from the server by chunks.
|
|
623
|
+
* @param {string} fileId
|
|
624
|
+
* @param {number} [startChunk=0]
|
|
625
|
+
*/
|
|
626
|
+
subFile(fileId, startChunk = 0) {
|
|
627
|
+
this._sendRaw({ type: "FILE_REQUEST", fileId, startChunk });
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Resume a file download from saved progress.
|
|
631
|
+
* @param {string} fileId
|
|
632
|
+
*/
|
|
633
|
+
resumeFile(fileId) {
|
|
634
|
+
const last = parseInt(this.storage.getItem(`dolphin_file_${fileId}`) || "-1");
|
|
635
|
+
this.subFile(fileId, last + 1);
|
|
636
|
+
}
|
|
637
|
+
/**
|
|
638
|
+
* Save download chunk progress.
|
|
639
|
+
* @param {string} fileId
|
|
640
|
+
* @param {number} chunkIndex
|
|
641
|
+
*/
|
|
642
|
+
saveFileProgress(fileId, chunkIndex) {
|
|
643
|
+
this.storage.setItem(`dolphin_file_${fileId}`, chunkIndex.toString());
|
|
644
|
+
}
|
|
645
|
+
// ── Signaling ─────────────────────────────────────────────────────────────
|
|
646
|
+
/**
|
|
647
|
+
* @param {function(SignalMessage): void} handler
|
|
648
|
+
*/
|
|
649
|
+
onSignal(handler) {
|
|
650
|
+
this.signalHandlers.add(handler);
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* @param {function(SignalMessage): void} handler
|
|
654
|
+
*/
|
|
655
|
+
offSignal(handler) {
|
|
656
|
+
this.signalHandlers.delete(handler);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* @param {function(FileMetadata): void} handler
|
|
660
|
+
*/
|
|
661
|
+
onFileAvailable(handler) {
|
|
662
|
+
this.fileHandlers.add(handler);
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* @param {function(FileMetadata): void} handler
|
|
666
|
+
*/
|
|
667
|
+
offFileAvailable(handler) {
|
|
668
|
+
this.fileHandlers.delete(handler);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// src/client/dom.ts
|
|
673
|
+
function attachDOMBinding(clientProto) {
|
|
674
|
+
clientProto._initDOMBinding = function() {
|
|
675
|
+
if (this._domInitialized) return;
|
|
676
|
+
this._domInitialized = true;
|
|
677
|
+
document.addEventListener("input", (e) => {
|
|
678
|
+
if (!e.target || !e.target.getAttribute) return;
|
|
679
|
+
const topic = e.target.getAttribute("data-rt-push");
|
|
680
|
+
if (topic) {
|
|
681
|
+
const payload = { name: e.target.name, value: e.target.value };
|
|
682
|
+
this.pubPush(topic, payload);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
document.addEventListener("submit", async (e) => {
|
|
686
|
+
if (!e.target || !e.target.getAttribute) return;
|
|
687
|
+
const rtTopic = e.target.getAttribute("data-rt-submit");
|
|
688
|
+
const apiTarget = e.target.getAttribute("data-api-submit");
|
|
689
|
+
if (rtTopic || apiTarget) {
|
|
690
|
+
e.preventDefault();
|
|
691
|
+
const formData = new FormData(e.target);
|
|
692
|
+
const data = Object.fromEntries(formData.entries());
|
|
693
|
+
if (rtTopic) {
|
|
694
|
+
this.publish(rtTopic, data);
|
|
695
|
+
} else if (apiTarget) {
|
|
696
|
+
const parts = apiTarget.trim().split(" ");
|
|
697
|
+
const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
|
|
698
|
+
const path = parts.length > 1 ? parts[1] : parts[0];
|
|
699
|
+
try {
|
|
700
|
+
const result = await this.api.request(method, path, data);
|
|
701
|
+
const resultBind = e.target.getAttribute("data-api-result");
|
|
702
|
+
if (resultBind) this._updateDOM(resultBind, result);
|
|
703
|
+
const redirect = e.target.getAttribute("data-api-redirect");
|
|
704
|
+
if (redirect) window.location.href = redirect;
|
|
705
|
+
if (e.target.hasAttribute("data-api-reload")) window.location.reload();
|
|
706
|
+
} catch (err) {
|
|
707
|
+
console.error("[Dolphin] API Submit Error:", err);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
document.addEventListener("click", async (e) => {
|
|
713
|
+
if (!e.target || !e.target.closest) return;
|
|
714
|
+
const rtBtn = e.target.closest("[data-rt-click]");
|
|
715
|
+
const apiBtn = e.target.closest("[data-api-click]");
|
|
716
|
+
if (rtBtn) {
|
|
717
|
+
const topic = rtBtn.getAttribute("data-rt-click");
|
|
718
|
+
const actionData = rtBtn.getAttribute("data-rt-payload");
|
|
719
|
+
const payload = actionData ? JSON.parse(actionData) : {};
|
|
720
|
+
this.publish(topic, payload);
|
|
721
|
+
} else if (apiBtn) {
|
|
722
|
+
const apiTarget = apiBtn.getAttribute("data-api-click");
|
|
723
|
+
const actionData = apiBtn.getAttribute("data-api-payload");
|
|
724
|
+
const payload = actionData ? JSON.parse(actionData) : null;
|
|
725
|
+
const parts = apiTarget.trim().split(" ");
|
|
726
|
+
const method = parts.length > 1 ? parts[0].toUpperCase() : "POST";
|
|
727
|
+
const path = parts.length > 1 ? parts[1] : parts[0];
|
|
728
|
+
try {
|
|
729
|
+
const result = await this.api.request(method, path, payload);
|
|
730
|
+
const resultBind = apiBtn.getAttribute("data-api-result");
|
|
731
|
+
if (resultBind) this._updateDOM(resultBind, result);
|
|
732
|
+
const redirect = apiBtn.getAttribute("data-api-redirect");
|
|
733
|
+
if (redirect) window.location.href = redirect;
|
|
734
|
+
if (apiBtn.hasAttribute("data-api-reload")) window.location.reload();
|
|
735
|
+
} catch (err) {
|
|
736
|
+
console.error("[Dolphin] API Click Error:", err);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
this.subscribe("#", (payload, topic) => {
|
|
741
|
+
this._updateDOM(topic, payload);
|
|
742
|
+
});
|
|
743
|
+
this._scanAndFetchAPIBinds();
|
|
744
|
+
};
|
|
745
|
+
clientProto._scanAndFetchAPIBinds = async function() {
|
|
746
|
+
if (typeof document === "undefined") return;
|
|
747
|
+
const elements = document.querySelectorAll("[data-api-get]");
|
|
748
|
+
for (const el of Array.from(elements)) {
|
|
749
|
+
const path = el.getAttribute("data-api-get");
|
|
750
|
+
if (!path) continue;
|
|
751
|
+
try {
|
|
752
|
+
const result = await this.api.get(path);
|
|
753
|
+
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
754
|
+
el.value = typeof result === "object" ? result.value !== void 0 ? result.value : "" : result;
|
|
755
|
+
} else {
|
|
756
|
+
el.innerHTML = typeof result === "object" ? result.html || result.text || JSON.stringify(result) : result;
|
|
757
|
+
}
|
|
758
|
+
} catch (e) {
|
|
759
|
+
console.error("[Dolphin] API Get Error:", e);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
};
|
|
763
|
+
clientProto._updateDOM = function(topic, payload) {
|
|
764
|
+
if (typeof document === "undefined") return;
|
|
765
|
+
const elements = document.querySelectorAll(`[data-rt-bind="${topic}"]`);
|
|
766
|
+
elements.forEach((el) => {
|
|
767
|
+
if (el.getAttribute("data-rt-type") === "context" && typeof payload === "object" && payload !== null) {
|
|
768
|
+
const processNode = (node) => {
|
|
769
|
+
if (node.hasAttribute("data-rt-text")) {
|
|
770
|
+
const key = node.getAttribute("data-rt-text");
|
|
771
|
+
if (key && payload[key] !== void 0 && payload[key] !== null) node.textContent = payload[key];
|
|
772
|
+
}
|
|
773
|
+
if (node.hasAttribute("data-rt-html")) {
|
|
774
|
+
const key = node.getAttribute("data-rt-html");
|
|
775
|
+
if (key && payload[key] !== void 0 && payload[key] !== null) node.innerHTML = payload[key];
|
|
776
|
+
}
|
|
777
|
+
if (node.hasAttribute("data-rt-attr")) {
|
|
778
|
+
const attrStr = node.getAttribute("data-rt-attr");
|
|
779
|
+
if (attrStr) {
|
|
780
|
+
attrStr.split(",").forEach((b) => {
|
|
781
|
+
const parts = b.split(":");
|
|
782
|
+
if (parts.length === 2) {
|
|
783
|
+
const attrName = parts[0].trim();
|
|
784
|
+
const key = parts[1].trim();
|
|
785
|
+
if (attrName && key && payload[key] !== void 0 && payload[key] !== null) {
|
|
786
|
+
node.setAttribute(attrName, payload[key]);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
if (node.hasAttribute("data-rt-class")) {
|
|
793
|
+
const classStr = node.getAttribute("data-rt-class");
|
|
794
|
+
if (classStr) {
|
|
795
|
+
classStr.split(",").forEach((b) => {
|
|
796
|
+
const parts = b.split(":");
|
|
797
|
+
if (parts.length === 2) {
|
|
798
|
+
const className = parts[0].trim();
|
|
799
|
+
const key = parts[1].trim();
|
|
800
|
+
if (payload[key]) {
|
|
801
|
+
node.classList.add(className);
|
|
802
|
+
} else {
|
|
803
|
+
node.classList.remove(className);
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (node.hasAttribute("data-rt-if")) {
|
|
810
|
+
const key = node.getAttribute("data-rt-if");
|
|
811
|
+
if (key) {
|
|
812
|
+
if (payload[key]) {
|
|
813
|
+
node.style.display = "";
|
|
814
|
+
} else {
|
|
815
|
+
node.style.display = "none";
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
if (node.hasAttribute("data-rt-hide")) {
|
|
820
|
+
const key = node.getAttribute("data-rt-hide");
|
|
821
|
+
if (key) {
|
|
822
|
+
if (payload[key]) {
|
|
823
|
+
node.style.display = "none";
|
|
824
|
+
} else {
|
|
825
|
+
node.style.display = "";
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
processNode(el);
|
|
831
|
+
el.querySelectorAll("[data-rt-text], [data-rt-html], [data-rt-attr], [data-rt-class], [data-rt-if], [data-rt-hide]").forEach(processNode);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const template = el.getAttribute("data-rt-template");
|
|
835
|
+
if (template && typeof payload === "object" && payload !== null) {
|
|
836
|
+
if (Array.isArray(payload)) {
|
|
837
|
+
let combinedHTML = "";
|
|
838
|
+
for (const item of payload) {
|
|
839
|
+
let finalItemHTML = template;
|
|
840
|
+
for (let key in item) {
|
|
841
|
+
finalItemHTML = finalItemHTML.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), item[key] !== void 0 && item[key] !== null ? item[key] : "");
|
|
842
|
+
}
|
|
843
|
+
combinedHTML += finalItemHTML;
|
|
844
|
+
}
|
|
845
|
+
el.innerHTML = combinedHTML;
|
|
846
|
+
} else {
|
|
847
|
+
let finalHTML = template;
|
|
848
|
+
for (let key in payload) {
|
|
849
|
+
finalHTML = finalHTML.replace(new RegExp(`\\{\\{${key}\\}\\}`, "g"), payload[key] !== void 0 && payload[key] !== null ? payload[key] : "");
|
|
850
|
+
}
|
|
851
|
+
el.innerHTML = finalHTML;
|
|
852
|
+
}
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
|
|
856
|
+
el.value = typeof payload === "object" ? payload.value !== void 0 ? payload.value : "" : payload;
|
|
857
|
+
} else {
|
|
858
|
+
el.innerHTML = typeof payload === "object" ? payload.html || payload.text || JSON.stringify(payload) : payload;
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
// src/client/index.ts
|
|
865
|
+
attachDOMBinding(DolphinClient.prototype);
|
|
866
|
+
if (typeof window !== "undefined") {
|
|
867
|
+
window.DolphinClient = DolphinClient;
|
|
868
|
+
window.dolphin = new DolphinClient();
|
|
869
|
+
}
|
|
870
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
871
|
+
module.exports = { DolphinClient };
|
|
872
|
+
}
|
|
873
|
+
return __toCommonJS(index_exports);
|
|
874
|
+
})();
|