satyamark-react 0.0.12 → 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,218 +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 = null;
49
- var socket = null;
50
- var storedConnectionData = null;
51
- var listeners = [];
52
- var onConnectedCb = null;
53
- async function getWsUrl() {
54
- if (wsUrl)
55
- return wsUrl;
56
- const res = await fetch(
57
- "https://dhirajkarangale.github.io/SatyaMark/ws.json",
58
- { cache: "no-store" }
59
- );
60
- const data = await res.json();
61
- wsUrl = data.wsUrl;
62
- return wsUrl;
63
- }
64
- function onReceive(cb) {
65
- listeners.push(cb);
66
- return () => {
67
- const idx = listeners.indexOf(cb);
68
- if (idx !== -1)
69
- listeners.splice(idx, 1);
70
- };
71
- }
72
- function onConnected(cb) {
73
- onConnectedCb = cb;
74
- }
75
- async function init(connectionData, options) {
76
- var _a;
77
- preloadIcons();
78
- if (socket && socket.readyState == WebSocket.OPEN && storedConnectionData == connectionData) {
79
- console.log("Already Connected: ", connectionData);
80
- return;
81
- }
82
- const url = await getWsUrl();
83
- if (url) {
84
- socket = new WebSocket(url);
85
- } else {
86
- console.error("WebSocket endpoint resolution failed. Unable to establish connection.");
87
- return;
88
- }
89
- onConnectedCb = (_a = options == null ? void 0 : options.onConnected) != null ? _a : onConnectedCb;
90
- socket.onopen = () => {
91
- console.log("Connected to server: ", connectionData.user_id);
92
- safeSend({
93
- type: "handshake",
94
- clientId: connectionData.user_id,
95
- appId: connectionData.app_id
96
- });
97
- onConnectedCb == null ? void 0 : onConnectedCb(connectionData);
98
- };
99
- socket.onmessage = (event) => {
100
- receiveData(JSON.parse(event.data));
101
- };
102
- socket.onclose = () => {
103
- console.log("Server connection closed");
104
- };
105
- storedConnectionData = connectionData;
106
- }
107
- function isSocketOpen() {
108
- return socket && socket.readyState === WebSocket.OPEN;
109
- }
110
- function assert(condition, message) {
111
- if (!condition)
112
- throw new Error(message);
113
- }
114
- function safeSend(msg) {
115
- if (!storedConnectionData) {
116
- console.warn("No connectionData found. Call connect() first.");
117
- return;
118
- }
119
- if (!socket || socket.readyState !== WebSocket.OPEN) {
120
- console.warn("Socket not ready");
121
- return;
122
- }
123
- socket.send(JSON.stringify(msg));
124
- }
125
- function uniqueTimestamp() {
126
- const now = /* @__PURE__ */ new Date();
127
- const yyyy = now.getFullYear();
128
- const MM = String(now.getMonth() + 1).padStart(2, "0");
129
- const dd = String(now.getDate()).padStart(2, "0");
130
- const hh = String(now.getHours()).padStart(2, "0");
131
- const mm = String(now.getMinutes()).padStart(2, "0");
132
- const ss = String(now.getSeconds()).padStart(2, "0");
133
- const ms = String(now.getMilliseconds()).padStart(3, "0");
134
- const micro = String(Math.floor(Math.random() * 1e3)).padStart(3, "0");
135
- return `${yyyy}${MM}${dd}${hh}${mm}${ss}${ms}${micro}`;
136
- }
137
- function sendData(text, image_url, dataId) {
138
- if (!storedConnectionData) {
139
- console.log("No connectionData found. Call connect() first.");
140
- return;
141
- }
142
- if (!socket || socket.readyState !== WebSocket.OPEN) {
143
- console.log("Socket not ready");
144
- return;
145
- }
146
- assert(storedConnectionData, "Call init() before sendData()");
147
- assert(isSocketOpen(), "WebSocket is not ready");
148
- assert(dataId, "dataId is required");
149
- assert(text || image_url, "Provide text or image_url");
150
- if (text)
151
- assert(text.trim().length >= 3, "Text must be at least 3 characters");
152
- const { app_id, user_id } = storedConnectionData;
153
- const timestamp = uniqueTimestamp();
154
- const jobId = `${app_id}_${user_id}_${dataId}_${timestamp}`;
155
- const data = {
156
- clientId: user_id,
157
- jobId,
158
- text,
159
- image_url
160
- };
161
- socket.send(JSON.stringify(data));
162
- return jobId;
163
- }
164
- function receiveData(data) {
165
- if (!storedConnectionData || data.clientId != storedConnectionData.user_id)
166
- return;
167
- for (const cb of Array.from(listeners)) {
168
- try {
169
- cb(data);
170
- } catch (err) {
171
- console.log("listener error", err);
172
- }
173
- }
174
- }
175
-
176
- // src/satyamark_process.ts
177
- var mergeText = (texts) => texts.join(", ");
178
- var isValidImageUrl = (url) => {
179
- return new Promise((resolve) => {
180
- const img = new Image();
181
- img.onload = () => resolve(true);
182
- img.onerror = () => resolve(false);
183
- img.src = url;
184
- });
185
- };
186
- var getFirstValidImage = async (urls) => {
187
- for (const url of urls) {
188
- if (await isValidImageUrl(url))
189
- return url;
190
- }
191
- return null;
192
- };
193
- var extractFromDiv = (root) => {
194
- var _a;
195
- const images = Array.from(root.querySelectorAll("img")).map((img) => img.src);
196
- const text = [];
197
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
198
- let node;
199
- while (node = walker.nextNode()) {
200
- const trimmed = (_a = node.textContent) == null ? void 0 : _a.trim();
201
- if (trimmed)
202
- text.push(trimmed);
203
- }
204
- return { text, images };
205
- };
206
- async function process(divRef, dataId) {
207
- if (!divRef) {
208
- throw new Error("Invalid root element");
209
- }
210
- if (!dataId) {
211
- throw new Error("dataId is required");
212
- }
213
- const { text, images } = extractFromDiv(divRef);
214
- const mergedText = mergeText(text);
215
- const validImage = await getFirstValidImage(images);
216
- if (!mergedText && !validImage) {
217
- throw new Error("No valid text or image found in the element");
218
- }
219
- if (mergedText && mergedText.length < 3) {
220
- throw new Error("Extracted text is too short");
221
- }
222
- const jobId = sendData(mergedText, validImage != null ? validImage : "", dataId);
223
- return jobId;
224
- }
225
-
226
- // src/satyamark_status_controller.ts
227
- var jobMap = {};
317
+ // src/core/status_controller.ts
228
318
  var DEFAULT_ICON_SIZE = 20;
229
319
  var satyamark_url = "https://satyamark.vercel.app/chat";
230
- function registerStatus(jobId, rootElement, options = {}) {
320
+ function updateIcon(containerRef, data) {
231
321
  var _a;
232
- jobMap[jobId] = {
233
- root: rootElement,
234
- iconSize: (_a = options.iconSize) != null ? _a : DEFAULT_ICON_SIZE
235
- };
236
- updateIcon(jobId, "pending", null);
237
- }
238
- function updateIcon(jobId, rawMark, data) {
239
- const entry = jobMap[jobId];
240
- if (!entry)
241
- return;
242
- const { root, iconSize } = entry;
243
- 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();
244
327
  ensureIconLoaded(mark);
245
328
  const container = root.querySelector("[data-satyamark-status-container]");
246
329
  if (!container)
@@ -296,18 +379,108 @@ function updateIcon(jobId, rawMark, data) {
296
379
  icon.onclick = null;
297
380
  }
298
381
  }
299
- onReceive((data) => {
300
- var _a, _b;
301
- 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)
444
+ return;
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)
302
469
  return;
303
- updateIcon(data.jobId, (_b = (_a = data.mark) == null ? void 0 : _a.toLowerCase()) != null ? _b : "pending", data);
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
+ }
304
481
  });
305
482
  export {
306
483
  init,
307
484
  onConnected,
308
- onReceive,
309
- process,
310
- receiveData,
311
- registerStatus,
312
- sendData
485
+ process
313
486
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "satyamark-react",
3
- "version": "0.0.12",
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",