@symbo.ls/sdk 2.32.2 → 2.32.4

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.
Files changed (90) hide show
  1. package/dist/cjs/config/environment.js +43 -8
  2. package/dist/cjs/index.js +12 -4
  3. package/dist/cjs/services/AdminService.js +4 -4
  4. package/dist/cjs/services/AuthService.js +36 -149
  5. package/dist/cjs/services/BaseService.js +5 -18
  6. package/dist/cjs/services/BranchService.js +10 -10
  7. package/dist/cjs/services/CollabService.js +94 -61
  8. package/dist/cjs/services/CoreService.js +19 -19
  9. package/dist/cjs/services/DnsService.js +4 -4
  10. package/dist/cjs/services/FileService.js +2 -2
  11. package/dist/cjs/services/PaymentService.js +2 -2
  12. package/dist/cjs/services/PlanService.js +12 -12
  13. package/dist/cjs/services/ProjectService.js +45 -35
  14. package/dist/cjs/services/PullRequestService.js +7 -7
  15. package/dist/cjs/services/ScreenshotService.js +304 -0
  16. package/dist/cjs/services/SubscriptionService.js +14 -14
  17. package/dist/cjs/services/index.js +4 -0
  18. package/dist/cjs/utils/TokenManager.js +16 -5
  19. package/dist/cjs/utils/changePreprocessor.js +134 -0
  20. package/dist/cjs/utils/jsonDiff.js +46 -4
  21. package/dist/cjs/utils/ordering.js +274 -0
  22. package/dist/cjs/utils/services.js +14 -1
  23. package/dist/esm/config/environment.js +43 -8
  24. package/dist/esm/index.js +1099 -417
  25. package/dist/esm/services/AdminService.js +68 -35
  26. package/dist/esm/services/AuthService.js +100 -168
  27. package/dist/esm/services/BaseService.js +64 -31
  28. package/dist/esm/services/BranchService.js +74 -41
  29. package/dist/esm/services/CollabService.js +570 -97
  30. package/dist/esm/services/CoreService.js +83 -50
  31. package/dist/esm/services/DnsService.js +68 -35
  32. package/dist/esm/services/FileService.js +66 -33
  33. package/dist/esm/services/PaymentService.js +66 -33
  34. package/dist/esm/services/PlanService.js +76 -43
  35. package/dist/esm/services/ProjectService.js +547 -66
  36. package/dist/esm/services/PullRequestService.js +71 -38
  37. package/dist/esm/services/ScreenshotService.js +992 -0
  38. package/dist/esm/services/SubscriptionService.js +78 -45
  39. package/dist/esm/services/index.js +1076 -412
  40. package/dist/esm/utils/CollabClient.js +89 -12
  41. package/dist/esm/utils/TokenManager.js +16 -5
  42. package/dist/esm/utils/changePreprocessor.js +442 -0
  43. package/dist/esm/utils/jsonDiff.js +46 -4
  44. package/dist/esm/utils/ordering.js +256 -0
  45. package/dist/esm/utils/services.js +14 -1
  46. package/dist/node/config/environment.js +43 -8
  47. package/dist/node/index.js +14 -5
  48. package/dist/node/services/AdminService.js +4 -4
  49. package/dist/node/services/AuthService.js +36 -139
  50. package/dist/node/services/BaseService.js +5 -18
  51. package/dist/node/services/BranchService.js +10 -10
  52. package/dist/node/services/CollabService.js +95 -62
  53. package/dist/node/services/CoreService.js +19 -19
  54. package/dist/node/services/DnsService.js +4 -4
  55. package/dist/node/services/FileService.js +2 -2
  56. package/dist/node/services/PaymentService.js +2 -2
  57. package/dist/node/services/PlanService.js +12 -12
  58. package/dist/node/services/ProjectService.js +45 -35
  59. package/dist/node/services/PullRequestService.js +7 -7
  60. package/dist/node/services/ScreenshotService.js +285 -0
  61. package/dist/node/services/SubscriptionService.js +14 -14
  62. package/dist/node/services/index.js +4 -0
  63. package/dist/node/utils/TokenManager.js +16 -5
  64. package/dist/node/utils/changePreprocessor.js +115 -0
  65. package/dist/node/utils/jsonDiff.js +46 -4
  66. package/dist/node/utils/ordering.js +255 -0
  67. package/dist/node/utils/services.js +14 -1
  68. package/package.json +7 -6
  69. package/src/config/environment.js +48 -9
  70. package/src/index.js +38 -22
  71. package/src/services/AdminService.js +4 -4
  72. package/src/services/AuthService.js +42 -175
  73. package/src/services/BaseService.js +7 -24
  74. package/src/services/BranchService.js +10 -10
  75. package/src/services/CollabService.js +115 -74
  76. package/src/services/CoreService.js +19 -19
  77. package/src/services/DnsService.js +4 -4
  78. package/src/services/FileService.js +2 -2
  79. package/src/services/PaymentService.js +2 -2
  80. package/src/services/PlanService.js +12 -12
  81. package/src/services/ProjectService.js +50 -35
  82. package/src/services/PullRequestService.js +7 -7
  83. package/src/services/ScreenshotService.js +258 -0
  84. package/src/services/SubscriptionService.js +14 -14
  85. package/src/services/index.js +6 -1
  86. package/src/utils/TokenManager.js +19 -5
  87. package/src/utils/changePreprocessor.js +139 -0
  88. package/src/utils/jsonDiff.js +40 -5
  89. package/src/utils/ordering.js +244 -0
  90. package/src/utils/services.js +15 -1
@@ -1,4 +1,7 @@
1
1
  import { BaseService } from './BaseService.js'
2
+ import { computeOrdersForTuples } from '../utils/ordering.js'
3
+ import { preprocessChanges } from '../utils/changePreprocessor.js'
4
+ import { deepStringifyFunctions } from '@domql/utils'
2
5
 
3
6
  export class ProjectService extends BaseService {
4
7
  // ==================== PROJECT METHODS ====================
@@ -16,7 +19,7 @@ export class ProjectService extends BaseService {
16
19
  }
17
20
  throw new Error(response.message)
18
21
  } catch (error) {
19
- throw new Error(`Failed to create project: ${error.message}`)
22
+ throw new Error(`Failed to create project: ${error.message}`, { cause: error })
20
23
  }
21
24
  }
22
25
 
@@ -44,7 +47,7 @@ export class ProjectService extends BaseService {
44
47
  }
45
48
  throw new Error(response.message)
46
49
  } catch (error) {
47
- throw new Error(`Failed to get projects: ${error.message}`)
50
+ throw new Error(`Failed to get projects: ${error.message}`, { cause: error })
48
51
  }
49
52
  }
50
53
 
@@ -81,7 +84,7 @@ export class ProjectService extends BaseService {
81
84
  }
82
85
  throw new Error(response.message)
83
86
  } catch (error) {
84
- throw new Error(`Failed to list public projects: ${error.message}`)
87
+ throw new Error(`Failed to list public projects: ${error.message}`, { cause: error })
85
88
  }
86
89
  }
87
90
 
@@ -106,7 +109,7 @@ export class ProjectService extends BaseService {
106
109
  }
107
110
  throw new Error(response.message)
108
111
  } catch (error) {
109
- throw new Error(`Failed to get project: ${error.message}`)
112
+ throw new Error(`Failed to get project: ${error.message}`, { cause: error })
110
113
  }
111
114
  }
112
115
 
@@ -134,7 +137,7 @@ export class ProjectService extends BaseService {
134
137
  }
135
138
  throw new Error(response.message)
136
139
  } catch (error) {
137
- throw new Error(`Failed to get public project: ${error.message}`)
140
+ throw new Error(`Failed to get public project: ${error.message}`, { cause: error })
138
141
  }
139
142
  }
140
143
 
@@ -162,7 +165,7 @@ export class ProjectService extends BaseService {
162
165
 
163
166
  throw new Error(response.message)
164
167
  } catch (error) {
165
- throw new Error(`Failed to get project by key: ${error.message}`)
168
+ throw new Error(`Failed to get project by key: ${error.message}`, { cause: error })
166
169
  }
167
170
  }
168
171
 
@@ -202,7 +205,7 @@ export class ProjectService extends BaseService {
202
205
 
203
206
  throw new Error(response.message)
204
207
  } catch (error) {
205
- throw new Error(`Failed to get project data by key: ${error.message}`)
208
+ throw new Error(`Failed to get project data by key: ${error.message}`, { cause: error })
206
209
  }
207
210
  }
208
211
 
@@ -222,7 +225,7 @@ export class ProjectService extends BaseService {
222
225
  }
223
226
  throw new Error(response.message)
224
227
  } catch (error) {
225
- throw new Error(`Failed to update project: ${error.message}`)
228
+ throw new Error(`Failed to update project: ${error.message}`, { cause: error })
226
229
  }
227
230
  }
228
231
 
@@ -245,7 +248,7 @@ export class ProjectService extends BaseService {
245
248
  }
246
249
  throw new Error(response.message)
247
250
  } catch (error) {
248
- throw new Error(`Failed to update project components: ${error.message}`)
251
+ throw new Error(`Failed to update project components: ${error.message}`, { cause: error })
249
252
  }
250
253
  }
251
254
 
@@ -265,7 +268,7 @@ export class ProjectService extends BaseService {
265
268
  }
266
269
  throw new Error(response.message)
267
270
  } catch (error) {
268
- throw new Error(`Failed to update project settings: ${error.message}`)
271
+ throw new Error(`Failed to update project settings: ${error.message}`, { cause: error })
269
272
  }
270
273
  }
271
274
 
@@ -285,7 +288,7 @@ export class ProjectService extends BaseService {
285
288
  }
286
289
  throw new Error(response.message)
287
290
  } catch (error) {
288
- throw new Error(`Failed to update project name: ${error.message}`)
291
+ throw new Error(`Failed to update project name: ${error.message}`, { cause: error })
289
292
  }
290
293
  }
291
294
 
@@ -306,7 +309,7 @@ export class ProjectService extends BaseService {
306
309
  }
307
310
  throw new Error(response.message)
308
311
  } catch (error) {
309
- throw new Error(`Failed to update project package: ${error.message}`)
312
+ throw new Error(`Failed to update project package: ${error.message}`, { cause: error })
310
313
  }
311
314
  }
312
315
 
@@ -326,7 +329,7 @@ export class ProjectService extends BaseService {
326
329
  }
327
330
  throw new Error(response.message)
328
331
  } catch (error) {
329
- throw new Error(`Failed to duplicate project: ${error.message}`)
332
+ throw new Error(`Failed to duplicate project: ${error.message}`, { cause: error })
330
333
  }
331
334
  }
332
335
 
@@ -345,7 +348,7 @@ export class ProjectService extends BaseService {
345
348
  }
346
349
  throw new Error(response.message)
347
350
  } catch (error) {
348
- throw new Error(`Failed to remove project: ${error.message}`)
351
+ throw new Error(`Failed to remove project: ${error.message}`, { cause: error })
349
352
  }
350
353
  }
351
354
 
@@ -365,7 +368,8 @@ export class ProjectService extends BaseService {
365
368
  throw new Error(response.message)
366
369
  } catch (error) {
367
370
  throw new Error(
368
- `Failed to check project key availability: ${error.message}`
371
+ `Failed to check project key availability: ${error.message}`,
372
+ { cause: error }
369
373
  )
370
374
  }
371
375
  }
@@ -387,7 +391,7 @@ export class ProjectService extends BaseService {
387
391
  }
388
392
  throw new Error(response.message)
389
393
  } catch (error) {
390
- throw new Error(`Failed to get project members: ${error.message}`)
394
+ throw new Error(`Failed to get project members: ${error.message}`, { cause: error })
391
395
  }
392
396
  }
393
397
 
@@ -427,7 +431,7 @@ export class ProjectService extends BaseService {
427
431
  }
428
432
  throw new Error(response.message)
429
433
  } catch (error) {
430
- throw new Error(`Failed to invite member: ${error.message}`)
434
+ throw new Error(`Failed to invite member: ${error.message}`, { cause: error })
431
435
  }
432
436
  }
433
437
 
@@ -447,7 +451,7 @@ export class ProjectService extends BaseService {
447
451
  }
448
452
  throw new Error(response.message)
449
453
  } catch (error) {
450
- throw new Error(`Failed to accept invite: ${error.message}`)
454
+ throw new Error(`Failed to accept invite: ${error.message}`, { cause: error })
451
455
  }
452
456
  }
453
457
 
@@ -470,7 +474,7 @@ export class ProjectService extends BaseService {
470
474
  }
471
475
  throw new Error(response.message)
472
476
  } catch (error) {
473
- throw new Error(`Failed to update member role: ${error.message}`)
477
+ throw new Error(`Failed to update member role: ${error.message}`, { cause: error })
474
478
  }
475
479
  }
476
480
 
@@ -492,7 +496,7 @@ export class ProjectService extends BaseService {
492
496
  }
493
497
  throw new Error(response.message)
494
498
  } catch (error) {
495
- throw new Error(`Failed to remove member: ${error.message}`)
499
+ throw new Error(`Failed to remove member: ${error.message}`, { cause: error })
496
500
  }
497
501
  }
498
502
 
@@ -514,7 +518,7 @@ export class ProjectService extends BaseService {
514
518
  }
515
519
  throw new Error(response.message)
516
520
  } catch (error) {
517
- throw new Error(`Failed to get available libraries: ${error.message}`)
521
+ throw new Error(`Failed to get available libraries: ${error.message}`, { cause: error })
518
522
  }
519
523
  }
520
524
 
@@ -533,7 +537,7 @@ export class ProjectService extends BaseService {
533
537
  }
534
538
  throw new Error(response.message)
535
539
  } catch (error) {
536
- throw new Error(`Failed to get project libraries: ${error.message}`)
540
+ throw new Error(`Failed to get project libraries: ${error.message}`, { cause: error })
537
541
  }
538
542
  }
539
543
 
@@ -549,11 +553,11 @@ export class ProjectService extends BaseService {
549
553
  methodName: 'addProjectLibraries'
550
554
  })
551
555
  if (response.success) {
552
- return response.data
556
+ return response
553
557
  }
554
558
  throw new Error(response.message)
555
559
  } catch (error) {
556
- throw new Error(`Failed to add project libraries: ${error.message}`)
560
+ throw new Error(`Failed to add project libraries: ${error.message}`, { cause: error })
557
561
  }
558
562
  }
559
563
 
@@ -573,7 +577,7 @@ export class ProjectService extends BaseService {
573
577
  }
574
578
  throw new Error(response.message)
575
579
  } catch (error) {
576
- throw new Error(`Failed to remove project libraries: ${error.message}`)
580
+ throw new Error(`Failed to remove project libraries: ${error.message}`, { cause: error })
577
581
  }
578
582
  }
579
583
 
@@ -594,14 +598,25 @@ export class ProjectService extends BaseService {
594
598
 
595
599
  const { message, branch = 'main', type = 'patch' } = options
596
600
 
601
+ // Preprocess into granular changes and derive orders using current state if available
602
+ const state = this._context && this._context.state
603
+ const { granularChanges, orders: preprocessorOrders } = preprocessChanges(state, changes, options)
604
+ const derivedOrders = options.orders || (preprocessorOrders && preprocessorOrders.length
605
+ ? preprocessorOrders
606
+ : (state ? computeOrdersForTuples(state, granularChanges) : []))
607
+
608
+ const stringify = (val) => deepStringifyFunctions(val, Array.isArray(val) ? [] : {})
609
+
597
610
  try {
598
611
  const response = await this._request(`/projects/${projectId}/changes`, {
599
612
  method: 'POST',
600
613
  body: JSON.stringify({
601
- changes,
614
+ changes: stringify(changes),
615
+ granularChanges: stringify(granularChanges),
602
616
  message,
603
617
  branch,
604
- type
618
+ type,
619
+ ...(derivedOrders && derivedOrders.length ? { orders: derivedOrders } : {})
605
620
  }),
606
621
  methodName: 'applyProjectChanges'
607
622
  })
@@ -611,7 +626,7 @@ export class ProjectService extends BaseService {
611
626
  }
612
627
  throw new Error(response.message)
613
628
  } catch (error) {
614
- throw new Error(`Failed to apply project changes: ${error.message}`)
629
+ throw new Error(`Failed to apply project changes: ${error.message}`, { cause: error })
615
630
  }
616
631
  }
617
632
 
@@ -650,7 +665,7 @@ export class ProjectService extends BaseService {
650
665
  }
651
666
  throw new Error(response.message)
652
667
  } catch (error) {
653
- throw new Error(`Failed to get project data: ${error.message}`)
668
+ throw new Error(`Failed to get project data: ${error.message}`, { cause: error })
654
669
  }
655
670
  }
656
671
 
@@ -684,7 +699,7 @@ export class ProjectService extends BaseService {
684
699
  }
685
700
  throw new Error(response.message)
686
701
  } catch (error) {
687
- throw new Error(`Failed to get project versions: ${error.message}`)
702
+ throw new Error(`Failed to get project versions: ${error.message}`, { cause: error })
688
703
  }
689
704
  }
690
705
 
@@ -719,7 +734,7 @@ export class ProjectService extends BaseService {
719
734
  }
720
735
  throw new Error(response.message)
721
736
  } catch (error) {
722
- throw new Error(`Failed to restore project version: ${error.message}`)
737
+ throw new Error(`Failed to restore project version: ${error.message}`, { cause: error })
723
738
  }
724
739
  }
725
740
 
@@ -846,7 +861,7 @@ export class ProjectService extends BaseService {
846
861
 
847
862
  throw new Error(response.message)
848
863
  } catch (error) {
849
- throw new Error(`Failed to get favorite projects: ${error.message}`)
864
+ throw new Error(`Failed to get favorite projects: ${error.message}`, { cause: error })
850
865
  }
851
866
  }
852
867
 
@@ -867,7 +882,7 @@ export class ProjectService extends BaseService {
867
882
 
868
883
  throw new Error(response.message)
869
884
  } catch (error) {
870
- throw new Error(`Failed to add favorite project: ${error.message}`)
885
+ throw new Error(`Failed to add favorite project: ${error.message}`, { cause: error })
871
886
  }
872
887
  }
873
888
 
@@ -888,7 +903,7 @@ export class ProjectService extends BaseService {
888
903
 
889
904
  throw new Error(response.message)
890
905
  } catch (error) {
891
- throw new Error(`Failed to remove favorite project: ${error.message}`)
906
+ throw new Error(`Failed to remove favorite project: ${error.message}`, { cause: error })
892
907
  }
893
908
  }
894
909
 
@@ -925,7 +940,7 @@ export class ProjectService extends BaseService {
925
940
 
926
941
  throw new Error(response.message)
927
942
  } catch (error) {
928
- throw new Error(`Failed to get recent projects: ${error.message}`)
943
+ throw new Error(`Failed to get recent projects: ${error.message}`, { cause: error })
929
944
  }
930
945
  }
931
946
  }
@@ -33,7 +33,7 @@ export class PullRequestService extends BaseService {
33
33
  }
34
34
  throw new Error(response.message)
35
35
  } catch (error) {
36
- throw new Error(`Failed to create pull request: ${error.message}`)
36
+ throw new Error(`Failed to create pull request: ${error.message}`, { cause: error })
37
37
  }
38
38
  }
39
39
 
@@ -74,7 +74,7 @@ export class PullRequestService extends BaseService {
74
74
  }
75
75
  throw new Error(response.message)
76
76
  } catch (error) {
77
- throw new Error(`Failed to list pull requests: ${error.message}`)
77
+ throw new Error(`Failed to list pull requests: ${error.message}`, { cause: error })
78
78
  }
79
79
  }
80
80
 
@@ -103,7 +103,7 @@ export class PullRequestService extends BaseService {
103
103
  }
104
104
  throw new Error(response.message)
105
105
  } catch (error) {
106
- throw new Error(`Failed to get pull request: ${error.message}`)
106
+ throw new Error(`Failed to get pull request: ${error.message}`, { cause: error })
107
107
  }
108
108
  }
109
109
 
@@ -140,7 +140,7 @@ export class PullRequestService extends BaseService {
140
140
  }
141
141
  throw new Error(response.message)
142
142
  } catch (error) {
143
- throw new Error(`Failed to review pull request: ${error.message}`)
143
+ throw new Error(`Failed to review pull request: ${error.message}`, { cause: error })
144
144
  }
145
145
  }
146
146
 
@@ -173,7 +173,7 @@ export class PullRequestService extends BaseService {
173
173
  }
174
174
  throw new Error(response.message)
175
175
  } catch (error) {
176
- throw new Error(`Failed to add pull request comment: ${error.message}`)
176
+ throw new Error(`Failed to add pull request comment: ${error.message}`, { cause: error })
177
177
  }
178
178
  }
179
179
 
@@ -210,7 +210,7 @@ export class PullRequestService extends BaseService {
210
210
  ) {
211
211
  throw new Error(`Pull request has merge conflicts: ${error.message}`)
212
212
  }
213
- throw new Error(`Failed to merge pull request: ${error.message}`)
213
+ throw new Error(`Failed to merge pull request: ${error.message}`, { cause: error })
214
214
  }
215
215
  }
216
216
 
@@ -239,7 +239,7 @@ export class PullRequestService extends BaseService {
239
239
  }
240
240
  throw new Error(response.message)
241
241
  } catch (error) {
242
- throw new Error(`Failed to get pull request diff: ${error.message}`)
242
+ throw new Error(`Failed to get pull request diff: ${error.message}`, { cause: error })
243
243
  }
244
244
  }
245
245
 
@@ -0,0 +1,258 @@
1
+ import { BaseService } from './BaseService.js'
2
+
3
+ export class ScreenshotService extends BaseService {
4
+ constructor (config) {
5
+ super(config)
6
+ this._debounceTimers = new Map()
7
+ this._inflightRefreshes = new Map()
8
+ }
9
+
10
+ // ==================== PROJECT-LEVEL OPERATIONS ====================
11
+
12
+ async createScreenshotProject (payload) {
13
+ this._requireReady('createScreenshotProject')
14
+ try {
15
+ const response = await this._request('/screenshots/projects', {
16
+ method: 'POST',
17
+ body: JSON.stringify(payload),
18
+ methodName: 'createScreenshotProject'
19
+ })
20
+ if (response.success) {return response}
21
+ throw new Error(response.message)
22
+ } catch (error) {
23
+ throw new Error(`Failed to create screenshot project: ${error.message}`, { cause: error })
24
+ }
25
+ }
26
+
27
+ async getProjectScreenshots (projectKey, params = {}) {
28
+ this._requireReady('getProjectScreenshots')
29
+ if (!projectKey) {throw new Error('projectKey is required')}
30
+ const { type = 'all', status, limit = 50, offset = 0 } = params
31
+ const qs = new URLSearchParams()
32
+ if (type) {qs.set('type', type)}
33
+ if (status) {qs.set('status', status)}
34
+ if (limit != null) {qs.set('limit', String(limit))}
35
+ if (offset != null) {qs.set('offset', String(offset))}
36
+ try {
37
+ const response = await this._request(
38
+ `/screenshots/projects/${encodeURIComponent(projectKey)}${qs.toString() ? `?${qs.toString()}` : ''}`,
39
+ { method: 'GET', methodName: 'getProjectScreenshots' }
40
+ )
41
+ if (response.success) {return response}
42
+ throw new Error(response.message)
43
+ } catch (error) {
44
+ throw new Error(`Failed to get project screenshots: ${error.message}`, { cause: error })
45
+ }
46
+ }
47
+
48
+ async reprocessProjectScreenshots (projectKey, body = {}) {
49
+ this._requireReady('reprocessProjectScreenshots')
50
+ if (!projectKey) {throw new Error('projectKey is required')}
51
+ try {
52
+ const response = await this._request(
53
+ `/screenshots/projects/${encodeURIComponent(projectKey)}/reprocess`,
54
+ { method: 'POST', body: JSON.stringify(body), methodName: 'reprocessProjectScreenshots' }
55
+ )
56
+ if (response.success) {return response}
57
+ throw new Error(response.message)
58
+ } catch (error) {
59
+ throw new Error(`Failed to reprocess screenshots: ${error.message}`, { cause: error })
60
+ }
61
+ }
62
+
63
+ async recreateProjectScreenshots (projectKey, body = {}) {
64
+ this._requireReady('recreateProjectScreenshots')
65
+ if (!projectKey) {throw new Error('projectKey is required')}
66
+ try {
67
+ const response = await this._request(
68
+ `/screenshots/projects/${encodeURIComponent(projectKey)}/recreate`,
69
+ { method: 'POST', body: JSON.stringify(body), methodName: 'recreateProjectScreenshots' }
70
+ )
71
+ if (response.success) {return response}
72
+ throw new Error(response.message)
73
+ } catch (error) {
74
+ throw new Error(`Failed to recreate screenshots: ${error.message}`, { cause: error })
75
+ }
76
+ }
77
+
78
+ async deleteProjectScreenshots (projectKey) {
79
+ this._requireReady('deleteProjectScreenshots')
80
+ if (!projectKey) {throw new Error('projectKey is required')}
81
+ try {
82
+ const response = await this._request(
83
+ `/screenshots/projects/${encodeURIComponent(projectKey)}`,
84
+ { method: 'DELETE', methodName: 'deleteProjectScreenshots' }
85
+ )
86
+ if (response.success) {return response}
87
+ throw new Error(response.message)
88
+ } catch (error) {
89
+ throw new Error(`Failed to delete project screenshots: ${error.message}`, { cause: error })
90
+ }
91
+ }
92
+
93
+ // ==================== THUMBNAIL ====================
94
+
95
+ async getThumbnailCandidate (projectKey, options = {}) {
96
+ this._requireReady('getThumbnailCandidate')
97
+ if (!projectKey) {throw new Error('projectKey is required')}
98
+ const { includeData = false } = options
99
+ const qs = new URLSearchParams()
100
+ if (includeData) {qs.set('include_data', 'true')}
101
+ try {
102
+ const response = await this._request(
103
+ `/screenshots/projects/${encodeURIComponent(projectKey)}/thumbnail/candidate${qs.toString() ? `?${qs.toString()}` : ''}`,
104
+ { method: 'GET', methodName: 'getThumbnailCandidate' }
105
+ )
106
+ if (response.success) {return response}
107
+ throw new Error(response.message)
108
+ } catch (error) {
109
+ throw new Error(`Failed to get thumbnail candidate: ${error.message}`, { cause: error })
110
+ }
111
+ }
112
+
113
+ async updateProjectThumbnail (projectKey, body = {}) {
114
+ this._requireReady('updateProjectThumbnail')
115
+ if (!projectKey) {throw new Error('projectKey is required')}
116
+ try {
117
+ const response = await this._request(
118
+ `/screenshots/projects/${encodeURIComponent(projectKey)}/thumbnail`,
119
+ { method: 'POST', body: JSON.stringify(body), methodName: 'updateProjectThumbnail' }
120
+ )
121
+ if (response.success) {return response}
122
+ throw new Error(response.message)
123
+ } catch (error) {
124
+ throw new Error(`Failed to update project thumbnail: ${error.message}`, { cause: error })
125
+ }
126
+ }
127
+
128
+ // ==================== INDIVIDUAL SHOTS ====================
129
+
130
+ async getPageScreenshot (screenshotId, format = 'json') {
131
+ this._requireReady('getPageScreenshot')
132
+ if (!screenshotId) {throw new Error('screenshotId is required')}
133
+ const qs = new URLSearchParams()
134
+ if (format) {qs.set('format', format)}
135
+ try {
136
+ return await this._request(
137
+ `/screenshots/pages/${encodeURIComponent(screenshotId)}${qs.toString() ? `?${qs.toString()}` : ''}`,
138
+ { method: 'GET', methodName: 'getPageScreenshot' }
139
+ )
140
+ } catch (error) {
141
+ throw new Error(`Failed to get page screenshot: ${error.message}`, { cause: error })
142
+ }
143
+ }
144
+
145
+ async getComponentScreenshot (screenshotId, format = 'json') {
146
+ this._requireReady('getComponentScreenshot')
147
+ if (!screenshotId) {throw new Error('screenshotId is required')}
148
+ const qs = new URLSearchParams()
149
+ if (format) {qs.set('format', format)}
150
+ try {
151
+ return await this._request(
152
+ `/screenshots/components/${encodeURIComponent(screenshotId)}${qs.toString() ? `?${qs.toString()}` : ''}`,
153
+ { method: 'GET', methodName: 'getComponentScreenshot' }
154
+ )
155
+ } catch (error) {
156
+ throw new Error(`Failed to get component screenshot: ${error.message}`, { cause: error })
157
+ }
158
+ }
159
+
160
+ async getScreenshotByKey (projectKey, type, key, format = 'json') {
161
+ this._requireReady('getScreenshotByKey')
162
+ if (!projectKey) {throw new Error('projectKey is required')}
163
+ if (!type || !['component', 'page'].includes(String(type))) {
164
+ throw new Error("type must be 'component' or 'page'")
165
+ }
166
+ if (!key) {throw new Error('key is required')}
167
+ const qs = new URLSearchParams()
168
+ if (format) {qs.set('format', format)}
169
+ const sub = type === 'component' ? 'components' : 'pages'
170
+ try {
171
+ return await this._request(
172
+ `/screenshots/projects/${encodeURIComponent(projectKey)}/${sub}/${encodeURIComponent(key)}${qs.toString() ? `?${qs.toString()}` : ''}`,
173
+ { method: 'GET', methodName: 'getScreenshotByKey' }
174
+ )
175
+ } catch (error) {
176
+ throw new Error(`Failed to get screenshot by key: ${error.message}`, { cause: error })
177
+ }
178
+ }
179
+
180
+ async getQueueStatistics () {
181
+ this._requireReady('getQueueStatistics')
182
+ try {
183
+ const response = await this._request('/screenshots/queue/stats', {
184
+ method: 'GET',
185
+ methodName: 'getQueueStatistics'
186
+ })
187
+ if (response.success) {return response}
188
+ throw new Error(response.message)
189
+ } catch (error) {
190
+ throw new Error(`Failed to get queue statistics: ${error.message}`, { cause: error })
191
+ }
192
+ }
193
+
194
+ // ==================== COMBINATION/DEBOUNCED ====================
195
+
196
+ /**
197
+ * Debounced thumbnail refresh that recreates screenshots and then updates thumbnail.
198
+ * Subsequent calls within debounce window reset the timer.
199
+ */
200
+ async refreshThumbnail (projectKey, options = {}) {
201
+ this._requireReady('refreshThumbnail')
202
+ if (!projectKey) {throw new Error('projectKey is required')}
203
+
204
+ const {
205
+ debounceMs = 15000,
206
+ waitAfterRecreateMs = 20000,
207
+ recreate = {
208
+ process_pages: true,
209
+ process_components: false,
210
+ process_descriptions: false,
211
+ force: false,
212
+ priority: 5
213
+ },
214
+ thumbnail = {
215
+ strategy: 'auto',
216
+ force: true
217
+ }
218
+ } = options
219
+
220
+ // Clear existing debounce timer if present
221
+ const existingTimer = this._debounceTimers.get(projectKey)
222
+ if (existingTimer) {
223
+ clearTimeout(existingTimer)
224
+ }
225
+
226
+ // Wrap execution in a promise we store, so callers can await the outcome
227
+ const executionPromise = await new Promise(resolve => {
228
+ const timer = setTimeout(async () => {
229
+ try {
230
+ // Step 1: queue screenshot recreation (non-blocking server-side)
231
+ await this.recreateProjectScreenshots(projectKey, recreate)
232
+
233
+ // Step 2: wait for some time to allow processing to progress
234
+ await new Promise(resolveDelay => { setTimeout(resolveDelay, waitAfterRecreateMs) })
235
+
236
+ // Step 3: update thumbnail using best candidate
237
+ const result = await this.updateProjectThumbnail(projectKey, thumbnail)
238
+ resolve(result)
239
+ } catch (e) {
240
+ // Resolve with error object but do not throw to avoid unhandled rejections
241
+ resolve({ success: false, error: e?.message || String(e) })
242
+ } finally {
243
+ this._debounceTimers.delete(projectKey)
244
+ this._inflightRefreshes.delete(projectKey)
245
+ }
246
+ }, debounceMs)
247
+
248
+ this._debounceTimers.set(projectKey, timer)
249
+ })
250
+
251
+ this._inflightRefreshes.set(projectKey, executionPromise)
252
+ return executionPromise
253
+ }
254
+ }
255
+
256
+ export default ScreenshotService
257
+
258
+