@symbo.ls/sdk 3.2.3 → 3.2.6
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 +141 -0
- package/dist/cjs/config/environment.js +94 -10
- package/dist/cjs/index.js +152 -12
- package/dist/cjs/services/AdminService.js +351 -0
- package/dist/cjs/services/AuthService.js +738 -305
- package/dist/cjs/services/BaseService.js +158 -6
- package/dist/cjs/services/BranchService.js +484 -0
- package/dist/cjs/services/CollabService.js +439 -116
- package/dist/cjs/services/DnsService.js +340 -0
- package/dist/cjs/services/FeatureFlagService.js +175 -0
- package/dist/cjs/services/FileService.js +201 -0
- package/dist/cjs/services/IntegrationService.js +538 -0
- package/dist/cjs/services/MetricsService.js +62 -0
- package/dist/cjs/services/PaymentService.js +271 -0
- package/dist/cjs/services/PlanService.js +426 -0
- package/dist/cjs/services/ProjectService.js +1207 -0
- package/dist/cjs/services/PullRequestService.js +503 -0
- package/dist/cjs/services/ScreenshotService.js +304 -0
- package/dist/cjs/services/SubscriptionService.js +396 -0
- package/dist/cjs/services/TrackingService.js +661 -0
- package/dist/cjs/services/WaitlistService.js +148 -0
- package/dist/cjs/services/index.js +60 -4
- package/dist/cjs/state/RootStateManager.js +2 -23
- package/dist/cjs/state/rootEventBus.js +9 -0
- package/dist/cjs/utils/CollabClient.js +78 -12
- package/dist/cjs/utils/TokenManager.js +16 -3
- package/dist/cjs/utils/changePreprocessor.js +199 -0
- package/dist/cjs/utils/jsonDiff.js +46 -4
- package/dist/cjs/utils/ordering.js +309 -0
- package/dist/cjs/utils/services.js +285 -128
- package/dist/cjs/utils/validation.js +0 -3
- package/dist/esm/config/environment.js +94 -10
- package/dist/esm/index.js +47862 -18248
- package/dist/esm/services/AdminService.js +1132 -0
- package/dist/esm/services/AuthService.js +1493 -386
- package/dist/esm/services/BaseService.js +757 -6
- package/dist/esm/services/BranchService.js +1265 -0
- package/dist/esm/services/CollabService.js +24956 -16089
- package/dist/esm/services/DnsService.js +1121 -0
- package/dist/esm/services/FeatureFlagService.js +956 -0
- package/dist/esm/services/FileService.js +982 -0
- package/dist/esm/services/IntegrationService.js +1319 -0
- package/dist/esm/services/MetricsService.js +843 -0
- package/dist/esm/services/PaymentService.js +1052 -0
- package/dist/esm/services/PlanService.js +1207 -0
- package/dist/esm/services/ProjectService.js +2526 -0
- package/dist/esm/services/PullRequestService.js +1284 -0
- package/dist/esm/services/ScreenshotService.js +1085 -0
- package/dist/esm/services/SubscriptionService.js +1177 -0
- package/dist/esm/services/TrackingService.js +18454 -0
- package/dist/esm/services/WaitlistService.js +929 -0
- package/dist/esm/services/index.js +47373 -18027
- package/dist/esm/state/RootStateManager.js +11 -23
- package/dist/esm/state/rootEventBus.js +9 -0
- package/dist/esm/utils/CollabClient.js +17526 -16120
- package/dist/esm/utils/TokenManager.js +16 -3
- package/dist/esm/utils/changePreprocessor.js +542 -0
- package/dist/esm/utils/jsonDiff.js +958 -43
- package/dist/esm/utils/ordering.js +291 -0
- package/dist/esm/utils/services.js +285 -128
- package/dist/esm/utils/validation.js +116 -50
- package/dist/node/config/environment.js +94 -10
- package/dist/node/index.js +183 -16
- package/dist/node/services/AdminService.js +332 -0
- package/dist/node/services/AuthService.js +742 -310
- package/dist/node/services/BaseService.js +148 -6
- package/dist/node/services/BranchService.js +465 -0
- package/dist/node/services/CollabService.js +439 -116
- package/dist/node/services/DnsService.js +321 -0
- package/dist/node/services/FeatureFlagService.js +156 -0
- package/dist/node/services/FileService.js +182 -0
- package/dist/node/services/IntegrationService.js +519 -0
- package/dist/node/services/MetricsService.js +43 -0
- package/dist/node/services/PaymentService.js +252 -0
- package/dist/node/services/PlanService.js +407 -0
- package/dist/node/services/ProjectService.js +1188 -0
- package/dist/node/services/PullRequestService.js +484 -0
- package/dist/node/services/ScreenshotService.js +285 -0
- package/dist/node/services/SubscriptionService.js +377 -0
- package/dist/node/services/TrackingService.js +632 -0
- package/dist/node/services/WaitlistService.js +129 -0
- package/dist/node/services/index.js +60 -4
- package/dist/node/state/RootStateManager.js +2 -23
- package/dist/node/state/rootEventBus.js +9 -0
- package/dist/node/utils/CollabClient.js +77 -11
- package/dist/node/utils/TokenManager.js +16 -3
- package/dist/node/utils/changePreprocessor.js +180 -0
- package/dist/node/utils/jsonDiff.js +46 -4
- package/dist/node/utils/ordering.js +290 -0
- package/dist/node/utils/services.js +285 -128
- package/dist/node/utils/validation.js +0 -3
- package/package.json +30 -18
- package/src/config/environment.js +95 -10
- package/src/index.js +190 -23
- package/src/services/AdminService.js +374 -0
- package/src/services/AuthService.js +874 -328
- package/src/services/BaseService.js +166 -6
- package/src/services/BranchService.js +536 -0
- package/src/services/CollabService.js +557 -148
- package/src/services/DnsService.js +366 -0
- package/src/services/FeatureFlagService.js +174 -0
- package/src/services/FileService.js +213 -0
- package/src/services/IntegrationService.js +548 -0
- package/src/services/MetricsService.js +40 -0
- package/src/services/PaymentService.js +287 -0
- package/src/services/PlanService.js +468 -0
- package/src/services/ProjectService.js +1366 -0
- package/src/services/PullRequestService.js +537 -0
- package/src/services/ScreenshotService.js +258 -0
- package/src/services/SubscriptionService.js +425 -0
- package/src/services/TrackingService.js +853 -0
- package/src/services/WaitlistService.js +130 -0
- package/src/services/index.js +79 -5
- package/src/services/tests/BranchService/createBranch.test.js +153 -0
- package/src/services/tests/BranchService/deleteBranch.test.js +173 -0
- package/src/services/tests/BranchService/getBranchChanges.test.js +146 -0
- package/src/services/tests/BranchService/listBranches.test.js +87 -0
- package/src/services/tests/BranchService/mergeBranch.test.js +210 -0
- package/src/services/tests/BranchService/publishVersion.test.js +183 -0
- package/src/services/tests/BranchService/renameBranch.test.js +240 -0
- package/src/services/tests/BranchService/resetBranch.test.js +152 -0
- package/src/services/tests/FeatureFlagService/adminFeatureFlags.test.js +67 -0
- package/src/services/tests/FeatureFlagService/getFeatureFlags.test.js +75 -0
- package/src/services/tests/FileService/createFileFormData.test.js +74 -0
- package/src/services/tests/FileService/getFileUrl.test.js +69 -0
- package/src/services/tests/FileService/updateProjectIcon.test.js +109 -0
- package/src/services/tests/FileService/uploadDocument.test.js +36 -0
- package/src/services/tests/FileService/uploadFile.test.js +78 -0
- package/src/services/tests/FileService/uploadFileWithValidation.test.js +114 -0
- package/src/services/tests/FileService/uploadImage.test.js +36 -0
- package/src/services/tests/FileService/uploadMultipleFiles.test.js +111 -0
- package/src/services/tests/FileService/validateFile.test.js +63 -0
- package/src/services/tests/PlanService/createPlan.test.js +104 -0
- package/src/services/tests/PlanService/createPlanWithValidation.test.js +523 -0
- package/src/services/tests/PlanService/deletePlan.test.js +92 -0
- package/src/services/tests/PlanService/getActivePlans.test.js +123 -0
- package/src/services/tests/PlanService/getAdminPlans.test.js +84 -0
- package/src/services/tests/PlanService/getPlan.test.js +50 -0
- package/src/services/tests/PlanService/getPlanByKey.test.js +109 -0
- package/src/services/tests/PlanService/getPlanWithValidation.test.js +85 -0
- package/src/services/tests/PlanService/getPlans.test.js +53 -0
- package/src/services/tests/PlanService/getPlansByPriceRange.test.js +109 -0
- package/src/services/tests/PlanService/getPlansWithValidation.test.js +48 -0
- package/src/services/tests/PlanService/initializePlans.test.js +75 -0
- package/src/services/tests/PlanService/updatePlan.test.js +111 -0
- package/src/services/tests/PlanService/updatePlanWithValidation.test.js +556 -0
- package/src/state/RootStateManager.js +37 -32
- package/src/state/rootEventBus.js +19 -0
- package/src/utils/CollabClient.js +99 -12
- package/src/utils/TokenManager.js +20 -3
- package/src/utils/changePreprocessor.js +239 -0
- package/src/utils/jsonDiff.js +40 -5
- package/src/utils/ordering.js +271 -0
- package/src/utils/services.js +306 -139
- package/src/utils/validation.js +0 -3
- package/dist/cjs/services/AIService.js +0 -155
- package/dist/cjs/services/BasedService.js +0 -1185
- package/dist/cjs/services/CoreService.js +0 -2295
- package/dist/cjs/services/SocketService.js +0 -309
- package/dist/cjs/services/SymstoryService.js +0 -571
- package/dist/cjs/utils/basedQuerys.js +0 -181
- package/dist/cjs/utils/symstoryClient.js +0 -259
- package/dist/esm/services/AIService.js +0 -185
- package/dist/esm/services/BasedService.js +0 -5262
- package/dist/esm/services/CoreService.js +0 -2827
- package/dist/esm/services/SocketService.js +0 -456
- package/dist/esm/services/SymstoryService.js +0 -7025
- package/dist/esm/utils/basedQuerys.js +0 -163
- package/dist/esm/utils/symstoryClient.js +0 -354
- package/dist/node/services/AIService.js +0 -136
- package/dist/node/services/BasedService.js +0 -1156
- package/dist/node/services/CoreService.js +0 -2266
- package/dist/node/services/SocketService.js +0 -280
- package/dist/node/services/SymstoryService.js +0 -542
- package/dist/node/utils/basedQuerys.js +0 -162
- package/dist/node/utils/symstoryClient.js +0 -230
- package/src/services/AIService.js +0 -150
- package/src/services/BasedService.js +0 -1302
- package/src/services/CoreService.js +0 -2548
- package/src/services/SocketService.js +0 -336
- package/src/services/SymstoryService.js +0 -649
- package/src/utils/basedQuerys.js +0 -164
- package/src/utils/symstoryClient.js +0 -252
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import { BaseService } from "./BaseService.js";
|
|
2
|
+
class ScreenshotService extends BaseService {
|
|
3
|
+
constructor(config) {
|
|
4
|
+
super(config);
|
|
5
|
+
this._debounceTimers = /* @__PURE__ */ new Map();
|
|
6
|
+
this._inflightRefreshes = /* @__PURE__ */ new Map();
|
|
7
|
+
}
|
|
8
|
+
// ==================== PROJECT-LEVEL OPERATIONS ====================
|
|
9
|
+
async createScreenshotProject(payload) {
|
|
10
|
+
this._requireReady("createScreenshotProject");
|
|
11
|
+
try {
|
|
12
|
+
const response = await this._request("/screenshots/projects", {
|
|
13
|
+
method: "POST",
|
|
14
|
+
body: JSON.stringify(payload),
|
|
15
|
+
methodName: "createScreenshotProject"
|
|
16
|
+
});
|
|
17
|
+
if (response.success) {
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
throw new Error(response.message);
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(`Failed to create screenshot project: ${error.message}`, { cause: error });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async getProjectScreenshots(projectKey, params = {}) {
|
|
26
|
+
this._requireReady("getProjectScreenshots");
|
|
27
|
+
if (!projectKey) {
|
|
28
|
+
throw new Error("projectKey is required");
|
|
29
|
+
}
|
|
30
|
+
const { type = "all", status, limit = 50, offset = 0 } = params;
|
|
31
|
+
const qs = new URLSearchParams();
|
|
32
|
+
if (type) {
|
|
33
|
+
qs.set("type", type);
|
|
34
|
+
}
|
|
35
|
+
if (status) {
|
|
36
|
+
qs.set("status", status);
|
|
37
|
+
}
|
|
38
|
+
if (limit != null) {
|
|
39
|
+
qs.set("limit", String(limit));
|
|
40
|
+
}
|
|
41
|
+
if (offset != null) {
|
|
42
|
+
qs.set("offset", String(offset));
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const response = await this._request(
|
|
46
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}${qs.toString() ? `?${qs.toString()}` : ""}`,
|
|
47
|
+
{ method: "GET", methodName: "getProjectScreenshots" }
|
|
48
|
+
);
|
|
49
|
+
if (response.success) {
|
|
50
|
+
return response;
|
|
51
|
+
}
|
|
52
|
+
throw new Error(response.message);
|
|
53
|
+
} catch (error) {
|
|
54
|
+
throw new Error(`Failed to get project screenshots: ${error.message}`, { cause: error });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async reprocessProjectScreenshots(projectKey, body = {}) {
|
|
58
|
+
this._requireReady("reprocessProjectScreenshots");
|
|
59
|
+
if (!projectKey) {
|
|
60
|
+
throw new Error("projectKey is required");
|
|
61
|
+
}
|
|
62
|
+
try {
|
|
63
|
+
const response = await this._request(
|
|
64
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}/reprocess`,
|
|
65
|
+
{ method: "POST", body: JSON.stringify(body), methodName: "reprocessProjectScreenshots" }
|
|
66
|
+
);
|
|
67
|
+
if (response.success) {
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
throw new Error(response.message);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
throw new Error(`Failed to reprocess screenshots: ${error.message}`, { cause: error });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async recreateProjectScreenshots(projectKey, body = {}) {
|
|
76
|
+
this._requireReady("recreateProjectScreenshots");
|
|
77
|
+
if (!projectKey) {
|
|
78
|
+
throw new Error("projectKey is required");
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const response = await this._request(
|
|
82
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}/recreate`,
|
|
83
|
+
{ method: "POST", body: JSON.stringify(body), methodName: "recreateProjectScreenshots" }
|
|
84
|
+
);
|
|
85
|
+
if (response.success) {
|
|
86
|
+
return response;
|
|
87
|
+
}
|
|
88
|
+
throw new Error(response.message);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw new Error(`Failed to recreate screenshots: ${error.message}`, { cause: error });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async deleteProjectScreenshots(projectKey) {
|
|
94
|
+
this._requireReady("deleteProjectScreenshots");
|
|
95
|
+
if (!projectKey) {
|
|
96
|
+
throw new Error("projectKey is required");
|
|
97
|
+
}
|
|
98
|
+
try {
|
|
99
|
+
const response = await this._request(
|
|
100
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}`,
|
|
101
|
+
{ method: "DELETE", methodName: "deleteProjectScreenshots" }
|
|
102
|
+
);
|
|
103
|
+
if (response.success) {
|
|
104
|
+
return response;
|
|
105
|
+
}
|
|
106
|
+
throw new Error(response.message);
|
|
107
|
+
} catch (error) {
|
|
108
|
+
throw new Error(`Failed to delete project screenshots: ${error.message}`, { cause: error });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// ==================== THUMBNAIL ====================
|
|
112
|
+
async getThumbnailCandidate(projectKey, options = {}) {
|
|
113
|
+
this._requireReady("getThumbnailCandidate");
|
|
114
|
+
if (!projectKey) {
|
|
115
|
+
throw new Error("projectKey is required");
|
|
116
|
+
}
|
|
117
|
+
const { includeData = false } = options;
|
|
118
|
+
const qs = new URLSearchParams();
|
|
119
|
+
if (includeData) {
|
|
120
|
+
qs.set("include_data", "true");
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const response = await this._request(
|
|
124
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}/thumbnail/candidate${qs.toString() ? `?${qs.toString()}` : ""}`,
|
|
125
|
+
{ method: "GET", methodName: "getThumbnailCandidate" }
|
|
126
|
+
);
|
|
127
|
+
if (response.success) {
|
|
128
|
+
return response;
|
|
129
|
+
}
|
|
130
|
+
throw new Error(response.message);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw new Error(`Failed to get thumbnail candidate: ${error.message}`, { cause: error });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async updateProjectThumbnail(projectKey, body = {}) {
|
|
136
|
+
this._requireReady("updateProjectThumbnail");
|
|
137
|
+
if (!projectKey) {
|
|
138
|
+
throw new Error("projectKey is required");
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
const response = await this._request(
|
|
142
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}/thumbnail`,
|
|
143
|
+
{ method: "POST", body: JSON.stringify(body), methodName: "updateProjectThumbnail" }
|
|
144
|
+
);
|
|
145
|
+
if (response.success) {
|
|
146
|
+
return response;
|
|
147
|
+
}
|
|
148
|
+
throw new Error(response.message);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
throw new Error(`Failed to update project thumbnail: ${error.message}`, { cause: error });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// ==================== INDIVIDUAL SHOTS ====================
|
|
154
|
+
async getPageScreenshot(screenshotId, format = "json") {
|
|
155
|
+
this._requireReady("getPageScreenshot");
|
|
156
|
+
if (!screenshotId) {
|
|
157
|
+
throw new Error("screenshotId is required");
|
|
158
|
+
}
|
|
159
|
+
const qs = new URLSearchParams();
|
|
160
|
+
if (format) {
|
|
161
|
+
qs.set("format", format);
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
return await this._request(
|
|
165
|
+
`/screenshots/pages/${encodeURIComponent(screenshotId)}${qs.toString() ? `?${qs.toString()}` : ""}`,
|
|
166
|
+
{ method: "GET", methodName: "getPageScreenshot" }
|
|
167
|
+
);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
throw new Error(`Failed to get page screenshot: ${error.message}`, { cause: error });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async getComponentScreenshot(screenshotId, format = "json") {
|
|
173
|
+
this._requireReady("getComponentScreenshot");
|
|
174
|
+
if (!screenshotId) {
|
|
175
|
+
throw new Error("screenshotId is required");
|
|
176
|
+
}
|
|
177
|
+
const qs = new URLSearchParams();
|
|
178
|
+
if (format) {
|
|
179
|
+
qs.set("format", format);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
return await this._request(
|
|
183
|
+
`/screenshots/components/${encodeURIComponent(screenshotId)}${qs.toString() ? `?${qs.toString()}` : ""}`,
|
|
184
|
+
{ method: "GET", methodName: "getComponentScreenshot" }
|
|
185
|
+
);
|
|
186
|
+
} catch (error) {
|
|
187
|
+
throw new Error(`Failed to get component screenshot: ${error.message}`, { cause: error });
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async getScreenshotByKey(projectKey, type, key, format = "json") {
|
|
191
|
+
this._requireReady("getScreenshotByKey");
|
|
192
|
+
if (!projectKey) {
|
|
193
|
+
throw new Error("projectKey is required");
|
|
194
|
+
}
|
|
195
|
+
if (!type || !["component", "page"].includes(String(type))) {
|
|
196
|
+
throw new Error("type must be 'component' or 'page'");
|
|
197
|
+
}
|
|
198
|
+
if (!key) {
|
|
199
|
+
throw new Error("key is required");
|
|
200
|
+
}
|
|
201
|
+
const qs = new URLSearchParams();
|
|
202
|
+
if (format) {
|
|
203
|
+
qs.set("format", format);
|
|
204
|
+
}
|
|
205
|
+
const sub = type === "component" ? "components" : "pages";
|
|
206
|
+
try {
|
|
207
|
+
return await this._request(
|
|
208
|
+
`/screenshots/projects/${encodeURIComponent(projectKey)}/${sub}/${encodeURIComponent(key)}${qs.toString() ? `?${qs.toString()}` : ""}`,
|
|
209
|
+
{ method: "GET", methodName: "getScreenshotByKey" }
|
|
210
|
+
);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
throw new Error(`Failed to get screenshot by key: ${error.message}`, { cause: error });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async getQueueStatistics() {
|
|
216
|
+
this._requireReady("getQueueStatistics");
|
|
217
|
+
try {
|
|
218
|
+
const response = await this._request("/screenshots/queue/stats", {
|
|
219
|
+
method: "GET",
|
|
220
|
+
methodName: "getQueueStatistics"
|
|
221
|
+
});
|
|
222
|
+
if (response.success) {
|
|
223
|
+
return response;
|
|
224
|
+
}
|
|
225
|
+
throw new Error(response.message);
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw new Error(`Failed to get queue statistics: ${error.message}`, { cause: error });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
// ==================== COMBINATION/DEBOUNCED ====================
|
|
231
|
+
/**
|
|
232
|
+
* Debounced thumbnail refresh that recreates screenshots and then updates thumbnail.
|
|
233
|
+
* Subsequent calls within debounce window reset the timer.
|
|
234
|
+
*/
|
|
235
|
+
async refreshThumbnail(projectKey, options = {}) {
|
|
236
|
+
this._requireReady("refreshThumbnail");
|
|
237
|
+
if (!projectKey) {
|
|
238
|
+
throw new Error("projectKey is required");
|
|
239
|
+
}
|
|
240
|
+
const {
|
|
241
|
+
debounceMs = 15e3,
|
|
242
|
+
waitAfterRecreateMs = 2e4,
|
|
243
|
+
recreate = {
|
|
244
|
+
process_pages: true,
|
|
245
|
+
process_components: false,
|
|
246
|
+
process_descriptions: false,
|
|
247
|
+
force: false,
|
|
248
|
+
priority: 5
|
|
249
|
+
},
|
|
250
|
+
thumbnail = {
|
|
251
|
+
strategy: "auto",
|
|
252
|
+
force: true
|
|
253
|
+
}
|
|
254
|
+
} = options;
|
|
255
|
+
const existingTimer = this._debounceTimers.get(projectKey);
|
|
256
|
+
if (existingTimer) {
|
|
257
|
+
clearTimeout(existingTimer);
|
|
258
|
+
}
|
|
259
|
+
const executionPromise = await new Promise((resolve) => {
|
|
260
|
+
const timer = setTimeout(async () => {
|
|
261
|
+
try {
|
|
262
|
+
await this.recreateProjectScreenshots(projectKey, recreate);
|
|
263
|
+
await new Promise((resolveDelay) => {
|
|
264
|
+
setTimeout(resolveDelay, waitAfterRecreateMs);
|
|
265
|
+
});
|
|
266
|
+
const result = await this.updateProjectThumbnail(projectKey, thumbnail);
|
|
267
|
+
resolve(result);
|
|
268
|
+
} catch (e) {
|
|
269
|
+
resolve({ success: false, error: (e == null ? void 0 : e.message) || String(e) });
|
|
270
|
+
} finally {
|
|
271
|
+
this._debounceTimers.delete(projectKey);
|
|
272
|
+
this._inflightRefreshes.delete(projectKey);
|
|
273
|
+
}
|
|
274
|
+
}, debounceMs);
|
|
275
|
+
this._debounceTimers.set(projectKey, timer);
|
|
276
|
+
});
|
|
277
|
+
this._inflightRefreshes.set(projectKey, executionPromise);
|
|
278
|
+
return executionPromise;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
var ScreenshotService_default = ScreenshotService;
|
|
282
|
+
export {
|
|
283
|
+
ScreenshotService,
|
|
284
|
+
ScreenshotService_default as default
|
|
285
|
+
};
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
import { BaseService } from "./BaseService.js";
|
|
2
|
+
class SubscriptionService extends BaseService {
|
|
3
|
+
// ==================== SUBSCRIPTION METHODS ====================
|
|
4
|
+
/**
|
|
5
|
+
* Create a new subscription checkout session
|
|
6
|
+
*/
|
|
7
|
+
async createSubscription(subscriptionData) {
|
|
8
|
+
this._requireReady("createSubscription");
|
|
9
|
+
if (!subscriptionData || typeof subscriptionData !== "object") {
|
|
10
|
+
throw new Error("Subscription data is required");
|
|
11
|
+
}
|
|
12
|
+
const { projectId, planId, pricingKey = "monthly", seats = 1, successUrl, cancelUrl } = subscriptionData;
|
|
13
|
+
if (!projectId) {
|
|
14
|
+
throw new Error("Project ID is required");
|
|
15
|
+
}
|
|
16
|
+
if (!planId) {
|
|
17
|
+
throw new Error("Plan ID is required");
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const response = await this._request("/subscriptions", {
|
|
21
|
+
method: "POST",
|
|
22
|
+
body: JSON.stringify({
|
|
23
|
+
projectId,
|
|
24
|
+
planId,
|
|
25
|
+
pricingKey,
|
|
26
|
+
seats,
|
|
27
|
+
successUrl,
|
|
28
|
+
cancelUrl
|
|
29
|
+
}),
|
|
30
|
+
methodName: "createSubscription"
|
|
31
|
+
});
|
|
32
|
+
if (response.success) {
|
|
33
|
+
return response.data;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(response.message);
|
|
36
|
+
} catch (error) {
|
|
37
|
+
throw new Error(`Failed to create subscription: ${error.message}`, { cause: error });
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get subscription status and usage for a project
|
|
42
|
+
*/
|
|
43
|
+
async getProjectStatus(projectId) {
|
|
44
|
+
this._requireReady("getProjectStatus");
|
|
45
|
+
if (!projectId) {
|
|
46
|
+
throw new Error("Project ID is required");
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const response = await this._request(`/subscriptions/project/${projectId}`, {
|
|
50
|
+
method: "GET",
|
|
51
|
+
methodName: "getProjectStatus"
|
|
52
|
+
});
|
|
53
|
+
if (response.success) {
|
|
54
|
+
return response.data;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(response.message);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(`Failed to get project subscription status: ${error.message}`, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get usage data for a subscription
|
|
63
|
+
*/
|
|
64
|
+
async getUsage(subscriptionId) {
|
|
65
|
+
this._requireReady("getUsage");
|
|
66
|
+
if (!subscriptionId) {
|
|
67
|
+
throw new Error("Subscription ID is required");
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const response = await this._request(`/subscriptions/${subscriptionId}/usage`, {
|
|
71
|
+
method: "GET",
|
|
72
|
+
methodName: "getUsage"
|
|
73
|
+
});
|
|
74
|
+
if (response.success) {
|
|
75
|
+
return response.data;
|
|
76
|
+
}
|
|
77
|
+
throw new Error(response.message);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new Error(`Failed to get subscription usage: ${error.message}`, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Cancel a subscription
|
|
84
|
+
*/
|
|
85
|
+
async cancelSubscription(subscriptionId) {
|
|
86
|
+
this._requireReady("cancelSubscription");
|
|
87
|
+
if (!subscriptionId) {
|
|
88
|
+
throw new Error("Subscription ID is required");
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const response = await this._request(`/subscriptions/${subscriptionId}/cancel`, {
|
|
92
|
+
method: "POST",
|
|
93
|
+
methodName: "cancelSubscription"
|
|
94
|
+
});
|
|
95
|
+
if (response.success) {
|
|
96
|
+
return response.data;
|
|
97
|
+
}
|
|
98
|
+
throw new Error(response.message);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
throw new Error(`Failed to cancel subscription: ${error.message}`, { cause: error });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* List invoices for a subscription
|
|
105
|
+
*/
|
|
106
|
+
async listInvoices(subscriptionId, options = {}) {
|
|
107
|
+
this._requireReady("listInvoices");
|
|
108
|
+
if (!subscriptionId) {
|
|
109
|
+
throw new Error("Subscription ID is required");
|
|
110
|
+
}
|
|
111
|
+
const { page = 1, limit = 20, status, startingAfter } = options;
|
|
112
|
+
try {
|
|
113
|
+
const queryParams = new URLSearchParams();
|
|
114
|
+
if (page) {
|
|
115
|
+
queryParams.append("page", page.toString());
|
|
116
|
+
}
|
|
117
|
+
if (limit) {
|
|
118
|
+
queryParams.append("limit", limit.toString());
|
|
119
|
+
}
|
|
120
|
+
if (status) {
|
|
121
|
+
queryParams.append("status", status);
|
|
122
|
+
}
|
|
123
|
+
if (startingAfter) {
|
|
124
|
+
queryParams.append("startingAfter", startingAfter);
|
|
125
|
+
}
|
|
126
|
+
const queryString = queryParams.toString();
|
|
127
|
+
const url = `/subscriptions/${subscriptionId}/invoices${queryString ? `?${queryString}` : ""}`;
|
|
128
|
+
const response = await this._request(url, {
|
|
129
|
+
method: "GET",
|
|
130
|
+
methodName: "listInvoices"
|
|
131
|
+
});
|
|
132
|
+
if (response.success) {
|
|
133
|
+
return response.data;
|
|
134
|
+
}
|
|
135
|
+
throw new Error(response.message);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
throw new Error(`Failed to list invoices: ${error.message}`, { cause: error });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Get customer portal URL for a subscription
|
|
142
|
+
*/
|
|
143
|
+
async getPortalUrl(subscriptionId, returnUrl) {
|
|
144
|
+
this._requireReady("getPortalUrl");
|
|
145
|
+
if (!subscriptionId) {
|
|
146
|
+
throw new Error("Subscription ID is required");
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const queryParams = new URLSearchParams();
|
|
150
|
+
if (returnUrl) {
|
|
151
|
+
queryParams.append("returnUrl", returnUrl);
|
|
152
|
+
}
|
|
153
|
+
const queryString = queryParams.toString();
|
|
154
|
+
const url = `/subscriptions/${subscriptionId}/portal${queryString ? `?${queryString}` : ""}`;
|
|
155
|
+
const response = await this._request(url, {
|
|
156
|
+
method: "GET",
|
|
157
|
+
methodName: "getPortalUrl"
|
|
158
|
+
});
|
|
159
|
+
if (response.success) {
|
|
160
|
+
return response.data;
|
|
161
|
+
}
|
|
162
|
+
throw new Error(response.message);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
throw new Error(`Failed to get portal URL: ${error.message}`, { cause: error });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// ==================== SUBSCRIPTION HELPER METHODS ====================
|
|
168
|
+
/**
|
|
169
|
+
* Helper method to create subscription with validation
|
|
170
|
+
*/
|
|
171
|
+
async createSubscriptionWithValidation(subscriptionData) {
|
|
172
|
+
if (!subscriptionData || typeof subscriptionData !== "object") {
|
|
173
|
+
throw new Error("Subscription data must be a valid object");
|
|
174
|
+
}
|
|
175
|
+
const requiredFields = ["projectId", "planId"];
|
|
176
|
+
for (const field of requiredFields) {
|
|
177
|
+
if (!subscriptionData[field]) {
|
|
178
|
+
throw new Error(`Required field '${field}' is missing`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (subscriptionData.seats != null) {
|
|
182
|
+
if (!Number.isInteger(subscriptionData.seats) || subscriptionData.seats < 1) {
|
|
183
|
+
throw new Error("Seats must be a positive integer");
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (subscriptionData.pricingKey && !["monthly", "yearly"].includes(subscriptionData.pricingKey)) {
|
|
187
|
+
throw new Error('Pricing key must be either "monthly" or "yearly"');
|
|
188
|
+
}
|
|
189
|
+
return await this.createSubscription(subscriptionData);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Helper method to check if project has active subscription
|
|
193
|
+
*/
|
|
194
|
+
async hasActiveSubscription(projectId) {
|
|
195
|
+
try {
|
|
196
|
+
const status = await this.getProjectStatus(projectId);
|
|
197
|
+
return status.hasSubscription === true;
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new Error(`Failed to check subscription status: ${error.message}`, { cause: error });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Helper method to get subscription details for a project
|
|
204
|
+
*/
|
|
205
|
+
async getProjectSubscription(projectId) {
|
|
206
|
+
try {
|
|
207
|
+
const status = await this.getProjectStatus(projectId);
|
|
208
|
+
if (!status.hasSubscription) {
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
return status.subscription;
|
|
212
|
+
} catch (error) {
|
|
213
|
+
throw new Error(`Failed to get project subscription: ${error.message}`, { cause: error });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Helper method to get usage with limits for a project
|
|
218
|
+
*/
|
|
219
|
+
async getProjectUsage(projectId) {
|
|
220
|
+
try {
|
|
221
|
+
const status = await this.getProjectStatus(projectId);
|
|
222
|
+
if (!status.hasSubscription) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
return status.usage;
|
|
226
|
+
} catch (error) {
|
|
227
|
+
throw new Error(`Failed to get project usage: ${error.message}`, { cause: error });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Helper method to get invoices with pagination
|
|
232
|
+
*/
|
|
233
|
+
async getInvoicesWithPagination(subscriptionId, options = {}) {
|
|
234
|
+
try {
|
|
235
|
+
const result = await this.listInvoices(subscriptionId, options);
|
|
236
|
+
return {
|
|
237
|
+
invoices: result.data || [],
|
|
238
|
+
pagination: result.pagination || {}
|
|
239
|
+
};
|
|
240
|
+
} catch (error) {
|
|
241
|
+
throw new Error(`Failed to get invoices with pagination: ${error.message}`, { cause: error });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Helper method to check if subscription is active
|
|
246
|
+
*/
|
|
247
|
+
async isSubscriptionActive(subscriptionId) {
|
|
248
|
+
try {
|
|
249
|
+
const usage = await this.getUsage(subscriptionId);
|
|
250
|
+
return usage && usage.subscription && usage.subscription.status === "active";
|
|
251
|
+
} catch (error) {
|
|
252
|
+
throw new Error(`Failed to check subscription status: ${error.message}`, { cause: error });
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Helper method to get subscription limits
|
|
257
|
+
*/
|
|
258
|
+
async getSubscriptionLimits(subscriptionId) {
|
|
259
|
+
try {
|
|
260
|
+
const usage = await this.getUsage(subscriptionId);
|
|
261
|
+
return usage.limits || {};
|
|
262
|
+
} catch (error) {
|
|
263
|
+
throw new Error(`Failed to get subscription limits: ${error.message}`, { cause: error });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Change subscription (unified endpoint for all pricing changes)
|
|
268
|
+
*/
|
|
269
|
+
async changeSubscription(changeData) {
|
|
270
|
+
this._requireReady("changeSubscription");
|
|
271
|
+
if (!changeData || typeof changeData !== "object") {
|
|
272
|
+
throw new Error("Change data is required");
|
|
273
|
+
}
|
|
274
|
+
const { subscriptionId, planId, pricingKey, seats = 1, projectId, successUrl, cancelUrl } = changeData;
|
|
275
|
+
if (!subscriptionId) {
|
|
276
|
+
throw new Error("Subscription ID is required");
|
|
277
|
+
}
|
|
278
|
+
if (!pricingKey) {
|
|
279
|
+
throw new Error("Pricing key is required");
|
|
280
|
+
}
|
|
281
|
+
try {
|
|
282
|
+
const response = await this._request(`/subscriptions/${subscriptionId}/change`, {
|
|
283
|
+
method: "POST",
|
|
284
|
+
body: JSON.stringify({
|
|
285
|
+
planId,
|
|
286
|
+
pricingKey,
|
|
287
|
+
seats,
|
|
288
|
+
projectId,
|
|
289
|
+
successUrl,
|
|
290
|
+
cancelUrl
|
|
291
|
+
}),
|
|
292
|
+
methodName: "changeSubscription"
|
|
293
|
+
});
|
|
294
|
+
if (response.success) {
|
|
295
|
+
return response.data;
|
|
296
|
+
}
|
|
297
|
+
throw new Error(response.message);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
throw new Error(`Failed to change subscription: ${error.message}`, { cause: error });
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Downgrade subscription to free plan
|
|
304
|
+
*/
|
|
305
|
+
async downgrade(downgradeData) {
|
|
306
|
+
this._requireReady("downgrade");
|
|
307
|
+
if (!downgradeData || typeof downgradeData !== "object") {
|
|
308
|
+
throw new Error("Downgrade data is required");
|
|
309
|
+
}
|
|
310
|
+
const { subscriptionId, reason } = downgradeData;
|
|
311
|
+
if (!subscriptionId) {
|
|
312
|
+
throw new Error("Subscription ID is required");
|
|
313
|
+
}
|
|
314
|
+
try {
|
|
315
|
+
const response = await this._request(`/subscriptions/${subscriptionId}/downgrade`, {
|
|
316
|
+
method: "POST",
|
|
317
|
+
body: JSON.stringify({
|
|
318
|
+
reason
|
|
319
|
+
}),
|
|
320
|
+
methodName: "downgrade"
|
|
321
|
+
});
|
|
322
|
+
if (response.success) {
|
|
323
|
+
return response.data;
|
|
324
|
+
}
|
|
325
|
+
throw new Error(response.message);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
throw new Error(`Failed to downgrade subscription: ${error.message}`, { cause: error });
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Helper method to change subscription with validation
|
|
332
|
+
*/
|
|
333
|
+
async changeSubscriptionWithValidation(changeData) {
|
|
334
|
+
if (!changeData || typeof changeData !== "object") {
|
|
335
|
+
throw new Error("Change data must be a valid object");
|
|
336
|
+
}
|
|
337
|
+
const requiredFields = ["subscriptionId", "pricingKey"];
|
|
338
|
+
for (const field of requiredFields) {
|
|
339
|
+
if (!changeData[field]) {
|
|
340
|
+
throw new Error(`Required field '${field}' is missing`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (changeData.seats != null) {
|
|
344
|
+
if (!Number.isInteger(changeData.seats) || changeData.seats < 1) {
|
|
345
|
+
throw new Error("Seats must be a positive integer");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
if (typeof changeData.subscriptionId !== "string") {
|
|
349
|
+
throw new Error("Subscription ID must be a valid string");
|
|
350
|
+
}
|
|
351
|
+
if (changeData.planId && typeof changeData.planId !== "string") {
|
|
352
|
+
throw new Error("Plan ID must be a valid string");
|
|
353
|
+
}
|
|
354
|
+
return await this.changeSubscription(changeData);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Helper method to downgrade subscription with validation
|
|
358
|
+
*/
|
|
359
|
+
async downgradeWithValidation(downgradeData) {
|
|
360
|
+
if (!downgradeData || typeof downgradeData !== "object") {
|
|
361
|
+
throw new Error("Downgrade data must be a valid object");
|
|
362
|
+
}
|
|
363
|
+
if (!downgradeData.subscriptionId) {
|
|
364
|
+
throw new Error("Subscription ID is required");
|
|
365
|
+
}
|
|
366
|
+
if (typeof downgradeData.subscriptionId !== "string") {
|
|
367
|
+
throw new Error("Subscription ID must be a valid string");
|
|
368
|
+
}
|
|
369
|
+
if (downgradeData.reason && typeof downgradeData.reason !== "string") {
|
|
370
|
+
throw new Error("Reason must be a valid string");
|
|
371
|
+
}
|
|
372
|
+
return await this.downgrade(downgradeData);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
export {
|
|
376
|
+
SubscriptionService
|
|
377
|
+
};
|