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