satyamark-react 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -22,14 +22,290 @@ var src_exports = {};
22
22
  __export(src_exports, {
23
23
  init: () => init,
24
24
  onConnected: () => onConnected,
25
- onReceive: () => onReceive,
26
- process: () => process,
27
- receiveData: () => receiveData,
28
- registerStatus: () => registerStatus,
29
- sendData: () => sendData
25
+ process: () => process
30
26
  });
31
27
  module.exports = __toCommonJS(src_exports);
32
28
 
29
+ // src/core/socketClient.ts
30
+ var SocketClient = class {
31
+ constructor(url, handlers) {
32
+ this.socket = null;
33
+ this.url = url;
34
+ this.handlers = handlers;
35
+ }
36
+ connect() {
37
+ this.socket = new WebSocket(this.url);
38
+ this.socket.onopen = () => this.handlers.onOpen();
39
+ this.socket.onmessage = (event) => this.handlers.onMessage(JSON.parse(event.data));
40
+ this.socket.onclose = () => this.handlers.onClose();
41
+ this.socket.onerror = () => this.handlers.onError();
42
+ }
43
+ send(payload) {
44
+ if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
45
+ throw new Error("Socket not ready");
46
+ }
47
+ this.socket.send(JSON.stringify(payload));
48
+ }
49
+ close() {
50
+ var _a;
51
+ (_a = this.socket) == null ? void 0 : _a.close();
52
+ this.socket = null;
53
+ }
54
+ isOpen() {
55
+ var _a;
56
+ return ((_a = this.socket) == null ? void 0 : _a.readyState) === WebSocket.OPEN;
57
+ }
58
+ };
59
+
60
+ // src/utils/generateIds.ts
61
+ function generateTimestamp() {
62
+ const now = /* @__PURE__ */ new Date();
63
+ return now.getFullYear().toString() + String(now.getMonth() + 1).padStart(2, "0") + String(now.getDate()).padStart(2, "0") + String(now.getHours()).padStart(2, "0") + String(now.getMinutes()).padStart(2, "0") + String(now.getSeconds()).padStart(2, "0") + String(now.getMilliseconds()).padStart(3, "0") + Math.floor(Math.random() * 1e3).toString().padStart(3, "0");
64
+ }
65
+ function generateJobId(app_id, user_id, dataId) {
66
+ const timestamp = generateTimestamp();
67
+ const jobId = `${app_id}_${user_id}_${dataId}_${timestamp}`;
68
+ return jobId;
69
+ }
70
+
71
+ // src/core/eventBus.ts
72
+ var messageListeners = /* @__PURE__ */ new Set();
73
+ var connectionListeners = /* @__PURE__ */ new Set();
74
+ function onMessage(cb) {
75
+ messageListeners.add(cb);
76
+ return () => messageListeners.delete(cb);
77
+ }
78
+ function emitMessage(data) {
79
+ messageListeners.forEach((cb) => {
80
+ try {
81
+ cb(data);
82
+ } catch (err) {
83
+ console.error("Message listener error:", err);
84
+ }
85
+ });
86
+ }
87
+ function onConnected(cb) {
88
+ connectionListeners.add(cb);
89
+ return () => connectionListeners.delete(cb);
90
+ }
91
+ function emitConnection(context2) {
92
+ connectionListeners.forEach((cb) => {
93
+ try {
94
+ cb(context2);
95
+ } catch (err) {
96
+ console.log("Connection listener error:", err);
97
+ }
98
+ });
99
+ }
100
+
101
+ // src/utils/encryption.ts
102
+ var SECRET = "your-strong-secret";
103
+ function strToBuf(str) {
104
+ return new TextEncoder().encode(str);
105
+ }
106
+ function bufToStr(buf) {
107
+ return new TextDecoder().decode(buf);
108
+ }
109
+ function bufToBase64(buf) {
110
+ return btoa(String.fromCharCode(...new Uint8Array(buf)));
111
+ }
112
+ function base64ToBuf(base64) {
113
+ const binary = atob(base64);
114
+ const bytes = new Uint8Array(binary.length);
115
+ for (let i = 0; i < binary.length; i++) {
116
+ bytes[i] = binary.charCodeAt(i);
117
+ }
118
+ return bytes.buffer;
119
+ }
120
+ async function getKey() {
121
+ const keyMaterial = await crypto.subtle.importKey(
122
+ "raw",
123
+ strToBuf(SECRET),
124
+ { name: "PBKDF2" },
125
+ false,
126
+ ["deriveKey"]
127
+ );
128
+ return crypto.subtle.deriveKey(
129
+ {
130
+ name: "PBKDF2",
131
+ salt: strToBuf("satya_salt"),
132
+ iterations: 1e5,
133
+ hash: "SHA-256"
134
+ },
135
+ keyMaterial,
136
+ { name: "AES-GCM", length: 256 },
137
+ false,
138
+ ["encrypt", "decrypt"]
139
+ );
140
+ }
141
+ async function encrypt(text) {
142
+ const iv = crypto.getRandomValues(new Uint8Array(12));
143
+ const key = await getKey();
144
+ const encrypted = await crypto.subtle.encrypt(
145
+ { name: "AES-GCM", iv },
146
+ key,
147
+ strToBuf(text)
148
+ );
149
+ const combined = new Uint8Array(iv.length + encrypted.byteLength);
150
+ combined.set(iv);
151
+ combined.set(new Uint8Array(encrypted), iv.length);
152
+ return bufToBase64(combined.buffer);
153
+ }
154
+ async function decrypt(cipherText) {
155
+ const combined = new Uint8Array(base64ToBuf(cipherText));
156
+ const iv = combined.slice(0, 12);
157
+ const data = combined.slice(12);
158
+ const key = await getKey();
159
+ const decrypted = await crypto.subtle.decrypt(
160
+ { name: "AES-GCM", iv },
161
+ key,
162
+ data
163
+ );
164
+ return bufToStr(decrypted);
165
+ }
166
+
167
+ // src/utils/storage.ts
168
+ function getCookie(name) {
169
+ var _a;
170
+ const value = `; ${document.cookie}`;
171
+ const parts = value.split(`; ${name}=`);
172
+ if (parts.length === 2)
173
+ return (_a = parts.pop()) == null ? void 0 : _a.split(";").shift();
174
+ }
175
+ function setCookie(name, value, days = 7) {
176
+ const expires = new Date(Date.now() + days * 864e5).toUTCString();
177
+ document.cookie = `${name}=${value}; expires=${expires}; path=/; Secure; SameSite=Lax`;
178
+ }
179
+
180
+ // src/utils/manageSessions.ts
181
+ async function getSessionId() {
182
+ const raw = getCookie("satya_session");
183
+ if (!raw)
184
+ return "";
185
+ try {
186
+ return await decrypt(raw);
187
+ } catch {
188
+ return "";
189
+ }
190
+ }
191
+ async function setSessionId(sessionId) {
192
+ const encrypted = await encrypt(sessionId);
193
+ setCookie("satya_session", encrypted);
194
+ }
195
+ function clearSession() {
196
+ setCookie("satya_session", "", -1);
197
+ }
198
+
199
+ // src/core/connectionManager.ts
200
+ var isDev = false;
201
+ var context = null;
202
+ var socketClient = null;
203
+ var isConnecting = false;
204
+ var isConnected = false;
205
+ var reconnectTimer = null;
206
+ async function resolveWsUrl() {
207
+ if (isDev) {
208
+ const wsUrl2 = "ws://localhost:1000";
209
+ return wsUrl2;
210
+ }
211
+ const res = await fetch(
212
+ "https://dhirajkarangale.github.io/SatyaMark/ws.json",
213
+ { cache: "no-store" }
214
+ );
215
+ const data = await res.json();
216
+ const wsUrl = data.wsUrl;
217
+ if (!wsUrl)
218
+ throw new Error("WebSocket URL resolution failed");
219
+ return wsUrl;
220
+ }
221
+ async function init(newContext) {
222
+ if (!(newContext == null ? void 0 : newContext.app_id) || !(newContext == null ? void 0 : newContext.user_id)) {
223
+ throw new Error("init() requires valid app_id and user_id");
224
+ }
225
+ if (isConnecting || isConnected)
226
+ return;
227
+ context = newContext;
228
+ await connect();
229
+ }
230
+ async function connect() {
231
+ if (!context)
232
+ return;
233
+ if (isConnecting || isConnected)
234
+ return;
235
+ isConnecting = true;
236
+ const url = await resolveWsUrl();
237
+ socketClient = new SocketClient(url, {
238
+ onOpen: async () => {
239
+ const sessionId = await getSessionId();
240
+ socketClient == null ? void 0 : socketClient.send({
241
+ type: "handshake",
242
+ clientId: context == null ? void 0 : context.user_id,
243
+ app_id: context == null ? void 0 : context.app_id,
244
+ sessionId
245
+ });
246
+ isConnected = true;
247
+ isConnecting = false;
248
+ emitConnection(context);
249
+ },
250
+ onMessage: async (data) => {
251
+ if (data.type === "session_created" && data.sessionId) {
252
+ await setSessionId(data.sessionId);
253
+ return;
254
+ }
255
+ if (data.type === "RateLimiter") {
256
+ if (data.msg === "Invalid session") {
257
+ clearSession();
258
+ socketClient == null ? void 0 : socketClient.close();
259
+ }
260
+ return;
261
+ }
262
+ if (data.clientId === (context == null ? void 0 : context.user_id)) {
263
+ emitMessage(data);
264
+ }
265
+ },
266
+ onClose: () => {
267
+ if (!isConnected && !isConnecting)
268
+ return;
269
+ isConnected = false;
270
+ isConnecting = false;
271
+ emitConnection(null);
272
+ scheduleReconnect();
273
+ },
274
+ onError: () => {
275
+ socketClient == null ? void 0 : socketClient.close();
276
+ }
277
+ });
278
+ socketClient.connect();
279
+ }
280
+ function scheduleReconnect() {
281
+ if (reconnectTimer || !context)
282
+ return;
283
+ reconnectTimer = setTimeout(() => {
284
+ reconnectTimer = null;
285
+ connect();
286
+ }, 2e3);
287
+ }
288
+ async function sendJob(text, imageUrl, dataId) {
289
+ if (!context)
290
+ throw new Error("Satyamark is not ready Call init() first");
291
+ if (!text && !imageUrl) {
292
+ throw new Error("Provide text or imageUrl");
293
+ }
294
+ if (text && text.trim().length < 3) {
295
+ throw new Error("Text must be at least 3 characters");
296
+ }
297
+ const jobId = generateJobId(context.app_id, context.user_id, dataId);
298
+ const sessionId = await getSessionId();
299
+ socketClient == null ? void 0 : socketClient.send({
300
+ clientId: context.user_id,
301
+ sessionId,
302
+ jobId,
303
+ text,
304
+ image_url: imageUrl
305
+ });
306
+ return jobId;
307
+ }
308
+
33
309
  // src/utils/iconRegistry.ts
34
310
  var ICON_URLS = {
35
311
  correct: "https://res.cloudinary.com/dfamljkyo/image/upload/v1768325442/correct_gjiadc.png",
@@ -61,201 +337,21 @@ function loadIcon(key) {
61
337
  iconLoadMap.set(key, promise);
62
338
  return promise;
63
339
  }
64
- function preloadIcons() {
65
- const run = () => {
66
- Object.keys(ICON_URLS).forEach(loadIcon);
67
- };
68
- if ("requestIdleCallback" in window) {
69
- window.requestIdleCallback(run);
70
- } else {
71
- setTimeout(run, 0);
72
- }
73
- }
74
340
  function ensureIconLoaded(key) {
75
341
  return loadIcon(key).catch(() => {
76
342
  });
77
343
  }
78
344
 
79
- // src/satyamark_connect.ts
80
- var wsUrl = "wss://satyamark.onrender.com";
81
- var socket = null;
82
- var storedConnectionData = null;
83
- var listeners = [];
84
- var onConnectedCb = null;
85
- function onReceive(cb) {
86
- listeners.push(cb);
87
- return () => {
88
- const idx = listeners.indexOf(cb);
89
- if (idx !== -1)
90
- listeners.splice(idx, 1);
91
- };
92
- }
93
- function onConnected(cb) {
94
- onConnectedCb = cb;
95
- }
96
- function init(connectionData, options) {
97
- var _a;
98
- preloadIcons();
99
- if (socket && socket.readyState == WebSocket.OPEN && storedConnectionData == connectionData) {
100
- console.log("Already Connected: ", connectionData);
101
- return;
102
- }
103
- socket = new WebSocket(wsUrl);
104
- onConnectedCb = (_a = options == null ? void 0 : options.onConnected) != null ? _a : onConnectedCb;
105
- socket.onopen = () => {
106
- console.log("Connected to server: ", connectionData.user_id);
107
- safeSend({
108
- type: "handshake",
109
- clientId: connectionData.user_id,
110
- appId: connectionData.app_id
111
- });
112
- onConnectedCb == null ? void 0 : onConnectedCb(connectionData);
113
- };
114
- socket.onmessage = (event) => {
115
- receiveData(JSON.parse(event.data));
116
- };
117
- socket.onclose = () => {
118
- console.log("Server connection closed");
119
- };
120
- storedConnectionData = connectionData;
121
- }
122
- function isSocketOpen() {
123
- return socket && socket.readyState === WebSocket.OPEN;
124
- }
125
- function assert(condition, message) {
126
- if (!condition)
127
- throw new Error(message);
128
- }
129
- function safeSend(msg) {
130
- if (!storedConnectionData) {
131
- console.warn("No connectionData found. Call connect() first.");
132
- return;
133
- }
134
- if (!socket || socket.readyState !== WebSocket.OPEN) {
135
- console.warn("Socket not ready");
136
- return;
137
- }
138
- socket.send(JSON.stringify(msg));
139
- }
140
- function uniqueTimestamp() {
141
- const now = /* @__PURE__ */ new Date();
142
- const yyyy = now.getFullYear();
143
- const MM = String(now.getMonth() + 1).padStart(2, "0");
144
- const dd = String(now.getDate()).padStart(2, "0");
145
- const hh = String(now.getHours()).padStart(2, "0");
146
- const mm = String(now.getMinutes()).padStart(2, "0");
147
- const ss = String(now.getSeconds()).padStart(2, "0");
148
- const ms = String(now.getMilliseconds()).padStart(3, "0");
149
- const micro = String(Math.floor(Math.random() * 1e3)).padStart(3, "0");
150
- return `${yyyy}${MM}${dd}${hh}${mm}${ss}${ms}${micro}`;
151
- }
152
- function sendData(text, image_url, dataId) {
153
- if (!storedConnectionData) {
154
- console.log("No connectionData found. Call connect() first.");
155
- return;
156
- }
157
- if (!socket || socket.readyState !== WebSocket.OPEN) {
158
- console.log("Socket not ready");
159
- return;
160
- }
161
- assert(storedConnectionData, "Call init() before sendData()");
162
- assert(isSocketOpen(), "WebSocket is not ready");
163
- assert(dataId, "dataId is required");
164
- assert(text || image_url, "Provide text or image_url");
165
- if (text)
166
- assert(text.trim().length >= 3, "Text must be at least 3 characters");
167
- const { app_id, user_id } = storedConnectionData;
168
- const timestamp = uniqueTimestamp();
169
- const jobId = `${app_id}_${user_id}_${dataId}_${timestamp}`;
170
- const data = {
171
- clientId: user_id,
172
- jobId,
173
- text,
174
- image_url
175
- };
176
- socket.send(JSON.stringify(data));
177
- return jobId;
178
- }
179
- function receiveData(data) {
180
- if (!storedConnectionData || data.clientId != storedConnectionData.user_id)
181
- return;
182
- for (const cb of Array.from(listeners)) {
183
- try {
184
- cb(data);
185
- } catch (err) {
186
- console.log("listener error", err);
187
- }
188
- }
189
- }
190
-
191
- // src/satyamark_process.ts
192
- var mergeText = (texts) => texts.join(", ");
193
- var isValidImageUrl = (url) => {
194
- return new Promise((resolve) => {
195
- const img = new Image();
196
- img.onload = () => resolve(true);
197
- img.onerror = () => resolve(false);
198
- img.src = url;
199
- });
200
- };
201
- var getFirstValidImage = async (urls) => {
202
- for (const url of urls) {
203
- if (await isValidImageUrl(url))
204
- return url;
205
- }
206
- return null;
207
- };
208
- var extractFromDiv = (root) => {
209
- var _a;
210
- const images = Array.from(root.querySelectorAll("img")).map((img) => img.src);
211
- const text = [];
212
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
213
- let node;
214
- while (node = walker.nextNode()) {
215
- const trimmed = (_a = node.textContent) == null ? void 0 : _a.trim();
216
- if (trimmed)
217
- text.push(trimmed);
218
- }
219
- return { text, images };
220
- };
221
- async function process(divRef, dataId) {
222
- if (!divRef) {
223
- throw new Error("Invalid root element");
224
- }
225
- if (!dataId) {
226
- throw new Error("dataId is required");
227
- }
228
- const { text, images } = extractFromDiv(divRef);
229
- const mergedText = mergeText(text);
230
- const validImage = await getFirstValidImage(images);
231
- if (!mergedText && !validImage) {
232
- throw new Error("No valid text or image found in the element");
233
- }
234
- if (mergedText && mergedText.length < 3) {
235
- throw new Error("Extracted text is too short");
236
- }
237
- const jobId = sendData(mergedText, validImage != null ? validImage : "", dataId);
238
- return jobId;
239
- }
240
-
241
- // src/satyamark_status_controller.ts
242
- var jobMap = {};
345
+ // src/core/status_controller.ts
243
346
  var DEFAULT_ICON_SIZE = 20;
244
347
  var satyamark_url = "https://satyamark.vercel.app/chat";
245
- function registerStatus(jobId, rootElement, options = {}) {
348
+ function updateIcon(containerRef, data) {
246
349
  var _a;
247
- jobMap[jobId] = {
248
- root: rootElement,
249
- iconSize: (_a = options.iconSize) != null ? _a : DEFAULT_ICON_SIZE
250
- };
251
- updateIcon(jobId, "pending", null);
252
- }
253
- function updateIcon(jobId, rawMark, data) {
254
- const entry = jobMap[jobId];
255
- if (!entry)
256
- return;
257
- const { root, iconSize } = entry;
258
- const mark = rawMark in ICON_URLS ? rawMark : "pending";
350
+ const root = containerRef;
351
+ const iconSize = DEFAULT_ICON_SIZE;
352
+ let mark = "pending";
353
+ if (data && data.mark)
354
+ mark = (_a = data.mark) == null ? void 0 : _a.toLowerCase();
259
355
  ensureIconLoaded(mark);
260
356
  const container = root.querySelector("[data-satyamark-status-container]");
261
357
  if (!container)
@@ -311,19 +407,109 @@ function updateIcon(jobId, rawMark, data) {
311
407
  icon.onclick = null;
312
408
  }
313
409
  }
314
- onReceive((data) => {
315
- var _a, _b;
316
- if (!(data == null ? void 0 : data.jobId))
410
+
411
+ // src/utils/process_data.ts
412
+ var mergeText = (texts) => texts.join(", ");
413
+ var isValidImageUrl = (url) => {
414
+ return new Promise((resolve) => {
415
+ const img = new Image();
416
+ img.onload = () => resolve(true);
417
+ img.onerror = () => resolve(false);
418
+ img.src = url;
419
+ });
420
+ };
421
+ var getFirstValidImage = async (urls) => {
422
+ for (const url of urls) {
423
+ if (await isValidImageUrl(url))
424
+ return url;
425
+ }
426
+ return null;
427
+ };
428
+ var extractFromDiv = (root) => {
429
+ var _a;
430
+ const images = Array.from(root.querySelectorAll("img")).map((img) => img.src);
431
+ const text = [];
432
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
433
+ let node;
434
+ while (node = walker.nextNode()) {
435
+ const trimmed = (_a = node.textContent) == null ? void 0 : _a.trim();
436
+ if (trimmed)
437
+ text.push(trimmed);
438
+ }
439
+ return { text, images };
440
+ };
441
+ async function process_data(divRef, dataId) {
442
+ if (!divRef) {
443
+ throw new Error("Invalid root element");
444
+ }
445
+ if (!dataId) {
446
+ throw new Error("dataId is required");
447
+ }
448
+ const { text, images } = extractFromDiv(divRef);
449
+ const mergedText = mergeText(text);
450
+ const validImage = await getFirstValidImage(images);
451
+ if (!mergedText && !validImage) {
452
+ throw new Error("No valid text or image found in the element");
453
+ }
454
+ if (mergedText && mergedText.length < 3) {
455
+ throw new Error("Extracted text is too short");
456
+ }
457
+ const image_url = validImage ? validImage : "";
458
+ return { text: mergedText, image_url };
459
+ }
460
+
461
+ // src/core/process.ts
462
+ var isConnected2 = false;
463
+ var isSendingJobs = false;
464
+ var jobMap = /* @__PURE__ */ new Map();
465
+ var process_queue = [];
466
+ function process(containerRef, dataId) {
467
+ process_queue.push({ containerRef, dataId });
468
+ void sendJobs();
469
+ }
470
+ async function sendJobs() {
471
+ if (isSendingJobs || !isConnected2)
317
472
  return;
318
- updateIcon(data.jobId, (_b = (_a = data.mark) == null ? void 0 : _a.toLowerCase()) != null ? _b : "pending", data);
473
+ isSendingJobs = true;
474
+ while (process_queue.length > 0) {
475
+ const item = process_queue[0];
476
+ if (!item)
477
+ break;
478
+ const { containerRef, dataId } = item;
479
+ try {
480
+ const { text, image_url } = await process_data(containerRef, dataId);
481
+ const jobId = await sendJob(text, image_url, dataId);
482
+ jobMap.set(jobId, containerRef);
483
+ process_queue.shift();
484
+ updateIcon(containerRef, null);
485
+ } catch (error) {
486
+ isSendingJobs = false;
487
+ setTimeout(() => {
488
+ void sendJobs();
489
+ }, 1e3);
490
+ return;
491
+ }
492
+ }
493
+ isSendingJobs = false;
494
+ }
495
+ onMessage((data) => {
496
+ if (!data || !data.jobId)
497
+ return;
498
+ const containerRef = jobMap.get(data.jobId);
499
+ if (!containerRef)
500
+ return;
501
+ jobMap.delete(data.jobId);
502
+ updateIcon(containerRef, data);
503
+ });
504
+ onConnected((data) => {
505
+ isConnected2 = !!data;
506
+ if (isConnected2) {
507
+ void sendJobs();
508
+ }
319
509
  });
320
510
  // Annotate the CommonJS export names for ESM import in node:
321
511
  0 && (module.exports = {
322
512
  init,
323
513
  onConnected,
324
- onReceive,
325
- process,
326
- receiveData,
327
- registerStatus,
328
- sendData
514
+ process
329
515
  });