@modelnex/sdk 0.5.7 → 0.5.9

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.d.mts CHANGED
@@ -315,7 +315,8 @@ interface TagData {
315
315
  interface TagStore {
316
316
  tags: Map<string, TagData>;
317
317
  getTag: (fingerprint: string) => TagData | undefined;
318
- setTag: (fingerprint: string, description: string, category?: TagData['category'], metadata?: Record<string, unknown>, selector?: string, patternId?: string, behavior?: string, sourcePage?: string, displayContext?: string) => void;
318
+ setTag: (fingerprint: string, description: string, category?: TagData['category'], metadata?: Record<string, unknown>, selector?: string, patternId?: string, behavior?: string, sourcePage?: string, displayContext?: string, skipRemoteSync?: boolean) => void;
319
+ setTagsBatch: (tags: TagData[], skipRemoteSync?: boolean) => void;
319
320
  deleteTag: (fingerprint: string) => void;
320
321
  getAllTags: () => TagData[];
321
322
  /** Export all tags as a JSON string (for debugging / backup) */
package/dist/index.d.ts CHANGED
@@ -315,7 +315,8 @@ interface TagData {
315
315
  interface TagStore {
316
316
  tags: Map<string, TagData>;
317
317
  getTag: (fingerprint: string) => TagData | undefined;
318
- setTag: (fingerprint: string, description: string, category?: TagData['category'], metadata?: Record<string, unknown>, selector?: string, patternId?: string, behavior?: string, sourcePage?: string, displayContext?: string) => void;
318
+ setTag: (fingerprint: string, description: string, category?: TagData['category'], metadata?: Record<string, unknown>, selector?: string, patternId?: string, behavior?: string, sourcePage?: string, displayContext?: string, skipRemoteSync?: boolean) => void;
319
+ setTagsBatch: (tags: TagData[], skipRemoteSync?: boolean) => void;
319
320
  deleteTag: (fingerprint: string) => void;
320
321
  getAllTags: () => TagData[];
321
322
  /** Export all tags as a JSON string (for debugging / backup) */
package/dist/index.js CHANGED
@@ -222,6 +222,18 @@ var ModelNexContext = (0, import_react.createContext)(null);
222
222
  var import_react3 = require("react");
223
223
  var import_socket = require("socket.io-client");
224
224
 
225
+ // src/utils/socket-io-transports.ts
226
+ function resolveSocketIoTransports(serverUrl, order) {
227
+ try {
228
+ const host = new URL(serverUrl).hostname.toLowerCase();
229
+ if (host.endsWith("awsapprunner.com")) {
230
+ return ["polling"];
231
+ }
232
+ } catch {
233
+ }
234
+ return order === "websocket-first" ? ["websocket", "polling"] : ["polling", "websocket"];
235
+ }
236
+
225
237
  // src/auto-extract.ts
226
238
  var import_react2 = require("react");
227
239
  function simpleHash(str) {
@@ -839,7 +851,10 @@ function useModelNexSocket({
839
851
  onSocketId?.(null);
840
852
  return;
841
853
  }
842
- const socket = (0, import_socket.io)(serverUrl);
854
+ const socket = (0, import_socket.io)(serverUrl, {
855
+ path: "/socket.io",
856
+ transports: resolveSocketIoTransports(serverUrl, "polling-first")
857
+ });
843
858
  socketRef.current = socket;
844
859
  const buildSyncPayload = () => ({
845
860
  actions: serializeActions(actionsRef.current),
@@ -1272,7 +1287,7 @@ function useTagStore(options) {
1272
1287
  const getTag = (0, import_react6.useCallback)((fingerprint) => {
1273
1288
  return tags.get(fingerprint);
1274
1289
  }, [tags]);
1275
- const setTag = (0, import_react6.useCallback)((fingerprint, description, category, metadata, selector, patternId, behavior, sourcePage, displayContext) => {
1290
+ const setTag = (0, import_react6.useCallback)((fingerprint, description, category, metadata, selector, patternId, behavior, sourcePage, displayContext, skipRemoteSync) => {
1276
1291
  setTags((prev) => {
1277
1292
  const next = new Map(prev);
1278
1293
  const key = selector ? `selector:${selector}` : fingerprint;
@@ -1292,7 +1307,7 @@ function useTagStore(options) {
1292
1307
  updatedAt: now
1293
1308
  };
1294
1309
  next.set(key, tagObj);
1295
- if (apiUrl) {
1310
+ if (apiUrl && !skipRemoteSync) {
1296
1311
  const payload = { tags: [tagObj] };
1297
1312
  if (websiteId) payload.websiteId = websiteId;
1298
1313
  fetch(apiUrl, {
@@ -1304,6 +1319,34 @@ function useTagStore(options) {
1304
1319
  return next;
1305
1320
  });
1306
1321
  }, [apiUrl, websiteId]);
1322
+ const setTagsBatch = (0, import_react6.useCallback)((newTags, skipRemoteSync) => {
1323
+ setTags((prev) => {
1324
+ const next = new Map(prev);
1325
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1326
+ const updatedTags = [];
1327
+ for (const t of newTags) {
1328
+ const key = t.selector ? `selector:${t.selector}` : t.fingerprint;
1329
+ const existing = prev.get(key);
1330
+ const tagObj = {
1331
+ ...t,
1332
+ createdAt: existing?.createdAt ?? t.createdAt ?? now,
1333
+ updatedAt: now
1334
+ };
1335
+ next.set(key, tagObj);
1336
+ updatedTags.push(tagObj);
1337
+ }
1338
+ if (apiUrl && !skipRemoteSync && updatedTags.length > 0) {
1339
+ const payload = { tags: updatedTags };
1340
+ if (websiteId) payload.websiteId = websiteId;
1341
+ fetch(apiUrl, {
1342
+ method: "POST",
1343
+ headers: { "Content-Type": "application/json" },
1344
+ body: JSON.stringify(payload)
1345
+ }).catch((err) => console.warn("[ModelNex] Failed to save remote tags batch:", err));
1346
+ }
1347
+ return next;
1348
+ });
1349
+ }, [apiUrl, websiteId]);
1307
1350
  const deleteTag = (0, import_react6.useCallback)((fingerprint) => {
1308
1351
  setTags((prev) => {
1309
1352
  const next = new Map(prev);
@@ -1333,7 +1376,7 @@ function useTagStore(options) {
1333
1376
  console.warn("[ModelNex] Failed to import tags:", err);
1334
1377
  }
1335
1378
  }, []);
1336
- return { tags, getTag, setTag, deleteTag, getAllTags, exportTags, importTags };
1379
+ return { tags, getTag, setTag, setTagsBatch, deleteTag, getAllTags, exportTags, importTags };
1337
1380
  }
1338
1381
 
1339
1382
  // src/studio-mode.tsx
@@ -3152,7 +3195,7 @@ function useTourPlayback({
3152
3195
  const socket = io2(serverUrl, {
3153
3196
  path: "/socket.io",
3154
3197
  // standard
3155
- transports: ["websocket", "polling"]
3198
+ transports: resolveSocketIoTransports(serverUrl, "websocket-first")
3156
3199
  });
3157
3200
  socketRef.current = socket;
3158
3201
  socket.on("connect", () => {
@@ -5407,7 +5450,7 @@ function useVoice(serverUrl) {
5407
5450
  mediaRecorderRef.current = recorder;
5408
5451
  const socket = io2(serverUrl, {
5409
5452
  path: "/socket.io",
5410
- transports: ["websocket", "polling"]
5453
+ transports: resolveSocketIoTransports(serverUrl, "websocket-first")
5411
5454
  });
5412
5455
  sttSocketRef.current = socket;
5413
5456
  recorder.ondataavailable = (e) => {
@@ -8480,21 +8523,7 @@ function ModelNexChatBubble({
8480
8523
  });
8481
8524
  const data = await resp.json();
8482
8525
  if (data.success && Array.isArray(data.tags)) {
8483
- for (const tag of data.tags) {
8484
- if (tag.patternId && tag.description && tag.selector) {
8485
- tagStore.setTag(
8486
- "",
8487
- tag.description,
8488
- tag.category || "other",
8489
- { matchedFingerprints: tag.matchedFingerprints },
8490
- tag.selector,
8491
- tag.patternId,
8492
- tag.behavior || void 0,
8493
- tag.sourcePage || void 0,
8494
- tag.displayContext || void 0
8495
- );
8496
- }
8497
- }
8526
+ tagStore.setTagsBatch(data.tags, true);
8498
8527
  lastAutoTaggedUrlRef.current = currentUrl;
8499
8528
  localStorage.setItem(storageKey, "true");
8500
8529
  console.log(`[ModelNex] Auto-tagged ${data.tags.length} elements for ${currentUrl}`);
package/dist/index.mjs CHANGED
@@ -13,6 +13,18 @@ var ModelNexContext = createContext(null);
13
13
  import { useEffect as useEffect3, useRef as useRef3 } from "react";
14
14
  import { io } from "socket.io-client";
15
15
 
16
+ // src/utils/socket-io-transports.ts
17
+ function resolveSocketIoTransports(serverUrl, order) {
18
+ try {
19
+ const host = new URL(serverUrl).hostname.toLowerCase();
20
+ if (host.endsWith("awsapprunner.com")) {
21
+ return ["polling"];
22
+ }
23
+ } catch {
24
+ }
25
+ return order === "websocket-first" ? ["websocket", "polling"] : ["polling", "websocket"];
26
+ }
27
+
16
28
  // src/auto-extract.ts
17
29
  import { useState, useEffect as useEffect2, useRef as useRef2, useCallback as useCallback2 } from "react";
18
30
  function simpleHash(str) {
@@ -630,7 +642,10 @@ function useModelNexSocket({
630
642
  onSocketId?.(null);
631
643
  return;
632
644
  }
633
- const socket = io(serverUrl);
645
+ const socket = io(serverUrl, {
646
+ path: "/socket.io",
647
+ transports: resolveSocketIoTransports(serverUrl, "polling-first")
648
+ });
634
649
  socketRef.current = socket;
635
650
  const buildSyncPayload = () => ({
636
651
  actions: serializeActions(actionsRef.current),
@@ -1063,7 +1078,7 @@ function useTagStore(options) {
1063
1078
  const getTag = useCallback3((fingerprint) => {
1064
1079
  return tags.get(fingerprint);
1065
1080
  }, [tags]);
1066
- const setTag = useCallback3((fingerprint, description, category, metadata, selector, patternId, behavior, sourcePage, displayContext) => {
1081
+ const setTag = useCallback3((fingerprint, description, category, metadata, selector, patternId, behavior, sourcePage, displayContext, skipRemoteSync) => {
1067
1082
  setTags((prev) => {
1068
1083
  const next = new Map(prev);
1069
1084
  const key = selector ? `selector:${selector}` : fingerprint;
@@ -1083,7 +1098,7 @@ function useTagStore(options) {
1083
1098
  updatedAt: now
1084
1099
  };
1085
1100
  next.set(key, tagObj);
1086
- if (apiUrl) {
1101
+ if (apiUrl && !skipRemoteSync) {
1087
1102
  const payload = { tags: [tagObj] };
1088
1103
  if (websiteId) payload.websiteId = websiteId;
1089
1104
  fetch(apiUrl, {
@@ -1095,6 +1110,34 @@ function useTagStore(options) {
1095
1110
  return next;
1096
1111
  });
1097
1112
  }, [apiUrl, websiteId]);
1113
+ const setTagsBatch = useCallback3((newTags, skipRemoteSync) => {
1114
+ setTags((prev) => {
1115
+ const next = new Map(prev);
1116
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1117
+ const updatedTags = [];
1118
+ for (const t of newTags) {
1119
+ const key = t.selector ? `selector:${t.selector}` : t.fingerprint;
1120
+ const existing = prev.get(key);
1121
+ const tagObj = {
1122
+ ...t,
1123
+ createdAt: existing?.createdAt ?? t.createdAt ?? now,
1124
+ updatedAt: now
1125
+ };
1126
+ next.set(key, tagObj);
1127
+ updatedTags.push(tagObj);
1128
+ }
1129
+ if (apiUrl && !skipRemoteSync && updatedTags.length > 0) {
1130
+ const payload = { tags: updatedTags };
1131
+ if (websiteId) payload.websiteId = websiteId;
1132
+ fetch(apiUrl, {
1133
+ method: "POST",
1134
+ headers: { "Content-Type": "application/json" },
1135
+ body: JSON.stringify(payload)
1136
+ }).catch((err) => console.warn("[ModelNex] Failed to save remote tags batch:", err));
1137
+ }
1138
+ return next;
1139
+ });
1140
+ }, [apiUrl, websiteId]);
1098
1141
  const deleteTag = useCallback3((fingerprint) => {
1099
1142
  setTags((prev) => {
1100
1143
  const next = new Map(prev);
@@ -1124,7 +1167,7 @@ function useTagStore(options) {
1124
1167
  console.warn("[ModelNex] Failed to import tags:", err);
1125
1168
  }
1126
1169
  }, []);
1127
- return { tags, getTag, setTag, deleteTag, getAllTags, exportTags, importTags };
1170
+ return { tags, getTag, setTag, setTagsBatch, deleteTag, getAllTags, exportTags, importTags };
1128
1171
  }
1129
1172
 
1130
1173
  // src/studio-mode.tsx
@@ -2943,7 +2986,7 @@ function useTourPlayback({
2943
2986
  const socket = io2(serverUrl, {
2944
2987
  path: "/socket.io",
2945
2988
  // standard
2946
- transports: ["websocket", "polling"]
2989
+ transports: resolveSocketIoTransports(serverUrl, "websocket-first")
2947
2990
  });
2948
2991
  socketRef.current = socket;
2949
2992
  socket.on("connect", () => {
@@ -5198,7 +5241,7 @@ function useVoice(serverUrl) {
5198
5241
  mediaRecorderRef.current = recorder;
5199
5242
  const socket = io2(serverUrl, {
5200
5243
  path: "/socket.io",
5201
- transports: ["websocket", "polling"]
5244
+ transports: resolveSocketIoTransports(serverUrl, "websocket-first")
5202
5245
  });
5203
5246
  sttSocketRef.current = socket;
5204
5247
  recorder.ondataavailable = (e) => {
@@ -8270,21 +8313,7 @@ function ModelNexChatBubble({
8270
8313
  });
8271
8314
  const data = await resp.json();
8272
8315
  if (data.success && Array.isArray(data.tags)) {
8273
- for (const tag of data.tags) {
8274
- if (tag.patternId && tag.description && tag.selector) {
8275
- tagStore.setTag(
8276
- "",
8277
- tag.description,
8278
- tag.category || "other",
8279
- { matchedFingerprints: tag.matchedFingerprints },
8280
- tag.selector,
8281
- tag.patternId,
8282
- tag.behavior || void 0,
8283
- tag.sourcePage || void 0,
8284
- tag.displayContext || void 0
8285
- );
8286
- }
8287
- }
8316
+ tagStore.setTagsBatch(data.tags, true);
8288
8317
  lastAutoTaggedUrlRef.current = currentUrl;
8289
8318
  localStorage.setItem(storageKey, "true");
8290
8319
  console.log(`[ModelNex] Auto-tagged ${data.tags.length} elements for ${currentUrl}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modelnex/sdk",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "React SDK for natural language control of web apps via AI agents",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",