@primestyleai/tryon 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@primestyleai/tryon",
3
- "version": "3.8.0",
3
+ "version": "3.9.0",
4
4
  "description": "PrimeStyle Virtual Try-On SDK — React component & Web Component",
5
5
  "type": "module",
6
6
  "main": "dist/primestyle-tryon.js",
@@ -1,186 +0,0 @@
1
- const DEFAULT_API_URL = "https://myaifitting.com";
2
- class ApiClient {
3
- constructor(apiKey, apiUrl) {
4
- this.apiKey = apiKey;
5
- this.baseUrl = (apiUrl || DEFAULT_API_URL).replace(/\/+$/, "");
6
- }
7
- get headers() {
8
- return {
9
- "Content-Type": "application/json",
10
- Authorization: `Bearer ${this.apiKey}`
11
- };
12
- }
13
- async submitTryOn(modelImage, garmentImage) {
14
- const res = await fetch(`${this.baseUrl}/api/v1/tryon`, {
15
- method: "POST",
16
- headers: this.headers,
17
- body: JSON.stringify({ modelImage, garmentImage })
18
- });
19
- if (!res.ok) {
20
- const data = await res.json().catch(() => ({}));
21
- if (res.status === 402) {
22
- throw new PrimeStyleError(
23
- data.message || "Insufficient try-ons",
24
- "INSUFFICIENT_BALANCE"
25
- );
26
- }
27
- throw new PrimeStyleError(
28
- data.message || "Failed to submit try-on",
29
- "API_ERROR"
30
- );
31
- }
32
- return res.json();
33
- }
34
- async getStatus(jobId) {
35
- const res = await fetch(`${this.baseUrl}/api/v1/tryon/status/${jobId}`, {
36
- headers: this.headers
37
- });
38
- if (!res.ok) {
39
- const data = await res.json().catch(() => ({}));
40
- throw new PrimeStyleError(
41
- data.message || "Failed to get status",
42
- "API_ERROR"
43
- );
44
- }
45
- return res.json();
46
- }
47
- getStreamUrl() {
48
- return `${this.baseUrl}/api/v1/tryon/stream?key=${encodeURIComponent(this.apiKey)}`;
49
- }
50
- }
51
- class PrimeStyleError extends Error {
52
- constructor(message, code) {
53
- super(message);
54
- this.name = "PrimeStyleError";
55
- this.code = code;
56
- }
57
- }
58
- class SseClient {
59
- constructor(streamUrl) {
60
- this.eventSource = null;
61
- this.listeners = /* @__PURE__ */ new Map();
62
- this.reconnectTimer = null;
63
- this.reconnectAttempts = 0;
64
- this.maxReconnectAttempts = 5;
65
- this.streamUrl = streamUrl;
66
- }
67
- connect() {
68
- if (this.eventSource) return;
69
- this.eventSource = new EventSource(this.streamUrl);
70
- this.eventSource.addEventListener("vto-update", (event) => {
71
- try {
72
- const data = JSON.parse(event.data);
73
- this.emit(data.galleryId, data);
74
- } catch {
75
- }
76
- });
77
- this.eventSource.onopen = () => {
78
- this.reconnectAttempts = 0;
79
- };
80
- this.eventSource.onerror = () => {
81
- this.eventSource?.close();
82
- this.eventSource = null;
83
- this.scheduleReconnect();
84
- };
85
- }
86
- scheduleReconnect() {
87
- if (this.reconnectAttempts >= this.maxReconnectAttempts) return;
88
- if (this.listeners.size === 0) return;
89
- const delay = Math.min(1e3 * 2 ** this.reconnectAttempts, 3e4);
90
- this.reconnectAttempts++;
91
- this.reconnectTimer = setTimeout(() => {
92
- this.connect();
93
- }, delay);
94
- }
95
- onJob(jobId, callback) {
96
- if (!this.listeners.has(jobId)) {
97
- this.listeners.set(jobId, /* @__PURE__ */ new Set());
98
- }
99
- this.listeners.get(jobId).add(callback);
100
- if (!this.eventSource) {
101
- this.connect();
102
- }
103
- return () => {
104
- const jobListeners = this.listeners.get(jobId);
105
- if (jobListeners) {
106
- jobListeners.delete(callback);
107
- if (jobListeners.size === 0) {
108
- this.listeners.delete(jobId);
109
- }
110
- }
111
- if (this.listeners.size === 0) {
112
- this.disconnect();
113
- }
114
- };
115
- }
116
- emit(jobId, update) {
117
- const callbacks = this.listeners.get(jobId);
118
- if (callbacks) {
119
- callbacks.forEach((cb) => cb(update));
120
- }
121
- }
122
- disconnect() {
123
- if (this.reconnectTimer) {
124
- clearTimeout(this.reconnectTimer);
125
- this.reconnectTimer = null;
126
- }
127
- if (this.eventSource) {
128
- this.eventSource.close();
129
- this.eventSource = null;
130
- }
131
- this.listeners.clear();
132
- this.reconnectAttempts = 0;
133
- }
134
- }
135
- const MAX_DIMENSION = 512;
136
- const JPEG_QUALITY = 0.65;
137
- function compressImage(file) {
138
- return new Promise((resolve, reject) => {
139
- const reader = new FileReader();
140
- reader.onload = () => {
141
- const img = new Image();
142
- img.onload = () => {
143
- try {
144
- const canvas = document.createElement("canvas");
145
- let { width, height } = img;
146
- if (width > MAX_DIMENSION || height > MAX_DIMENSION) {
147
- if (width > height) {
148
- height = Math.round(height * MAX_DIMENSION / width);
149
- width = MAX_DIMENSION;
150
- } else {
151
- width = Math.round(width * MAX_DIMENSION / height);
152
- height = MAX_DIMENSION;
153
- }
154
- }
155
- canvas.width = width;
156
- canvas.height = height;
157
- const ctx = canvas.getContext("2d");
158
- if (!ctx) {
159
- reject(new Error("Canvas context not available"));
160
- return;
161
- }
162
- ctx.drawImage(img, 0, 0, width, height);
163
- const dataUrl = canvas.toDataURL("image/jpeg", JPEG_QUALITY);
164
- resolve(dataUrl);
165
- } catch (err) {
166
- reject(err);
167
- }
168
- };
169
- img.onerror = () => reject(new Error("Failed to load image"));
170
- img.src = reader.result;
171
- };
172
- reader.onerror = () => reject(new Error("Failed to read file"));
173
- reader.readAsDataURL(file);
174
- });
175
- }
176
- function isValidImageFile(file) {
177
- const accepted = ["image/jpeg", "image/png", "image/webp"];
178
- return accepted.includes(file.type);
179
- }
180
- export {
181
- ApiClient as A,
182
- PrimeStyleError as P,
183
- SseClient as S,
184
- compressImage as c,
185
- isValidImageFile as i
186
- };