@coderegtech/jsonify-ws 1.0.5

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 ADDED
@@ -0,0 +1,542 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ disableEditMode: () => disableEditMode,
24
+ enableEditMode: () => enableEditMode,
25
+ getByPath: () => getByPath,
26
+ injectEditToggle: () => injectEditToggle,
27
+ scanDataCopyElements: () => scanDataCopyElements,
28
+ setByPath: () => setByPath,
29
+ syncElementsFromData: () => syncElementsFromData,
30
+ useJsonify: () => useJsonify,
31
+ useJsonifyWs: () => useJsonifyWs
32
+ });
33
+ module.exports = __toCommonJS(src_exports);
34
+
35
+ // src/useJsonifyWs.ts
36
+ var import_react = require("react");
37
+ var import_socket = require("socket.io-client");
38
+ var import_meta = {};
39
+ function resolveUrl(optionsUrl) {
40
+ if (optionsUrl) return optionsUrl;
41
+ if (typeof import_meta !== "undefined" && import_meta.env?.WSL_URL) {
42
+ return import_meta.env.WSL_URL;
43
+ }
44
+ if (typeof process !== "undefined" && process.env?.WSL_URL) {
45
+ return process.env.WSL_URL;
46
+ }
47
+ return "http://localhost:4000";
48
+ }
49
+ function useJsonifyWs(options = {}) {
50
+ const {
51
+ url: optionsUrl,
52
+ autoConnect = false,
53
+ initialData = {},
54
+ reconnectionDelay = 3e3,
55
+ onSync,
56
+ onStatusChange,
57
+ onError
58
+ } = options;
59
+ const resolvedUrl = resolveUrl(optionsUrl);
60
+ const [data, setDataState] = (0, import_react.useState)(initialData);
61
+ const [status, setStatus] = (0, import_react.useState)("disconnected");
62
+ const socketRef = (0, import_react.useRef)(null);
63
+ const isRemoteUpdate = (0, import_react.useRef)(false);
64
+ const dataRef = (0, import_react.useRef)(data);
65
+ dataRef.current = data;
66
+ const updateStatus = (0, import_react.useCallback)(
67
+ (s) => {
68
+ setStatus(s);
69
+ onStatusChange?.(s);
70
+ },
71
+ [onStatusChange]
72
+ );
73
+ const cleanup = (0, import_react.useCallback)(() => {
74
+ if (socketRef.current) {
75
+ socketRef.current.disconnect();
76
+ socketRef.current = null;
77
+ }
78
+ updateStatus("disconnected");
79
+ }, [updateStatus]);
80
+ const connect = (0, import_react.useCallback)(
81
+ (overrideUrl) => {
82
+ cleanup();
83
+ const targetUrl = overrideUrl || resolvedUrl;
84
+ updateStatus("connecting");
85
+ const socket = (0, import_socket.io)(targetUrl, {
86
+ reconnection: true,
87
+ reconnectionDelay,
88
+ reconnectionAttempts: Infinity
89
+ });
90
+ socketRef.current = socket;
91
+ socket.on("connect", () => {
92
+ updateStatus("connected");
93
+ socket.emit("update", dataRef.current);
94
+ });
95
+ socket.on("sync", (syncData) => {
96
+ isRemoteUpdate.current = true;
97
+ setDataState(syncData);
98
+ onSync?.(syncData);
99
+ });
100
+ socket.on("disconnect", () => {
101
+ updateStatus("disconnected");
102
+ });
103
+ socket.on("connect_error", (err) => {
104
+ onError?.(err);
105
+ });
106
+ },
107
+ [cleanup, resolvedUrl, reconnectionDelay, onSync, onStatusChange, onError]
108
+ );
109
+ const disconnect = (0, import_react.useCallback)(() => {
110
+ cleanup();
111
+ }, [cleanup]);
112
+ const setData = (0, import_react.useCallback)((newData) => {
113
+ setDataState(newData);
114
+ }, []);
115
+ (0, import_react.useEffect)(() => {
116
+ if (isRemoteUpdate.current) {
117
+ isRemoteUpdate.current = false;
118
+ return;
119
+ }
120
+ if (socketRef.current?.connected) {
121
+ socketRef.current.emit("update", data);
122
+ }
123
+ }, [data]);
124
+ (0, import_react.useEffect)(() => {
125
+ if (autoConnect) {
126
+ connect();
127
+ }
128
+ return () => {
129
+ cleanup();
130
+ };
131
+ }, []);
132
+ return {
133
+ data,
134
+ setData,
135
+ status,
136
+ connect,
137
+ disconnect,
138
+ url: resolvedUrl
139
+ };
140
+ }
141
+
142
+ // src/useJsonify.ts
143
+ var import_react2 = require("react");
144
+ var import_socket2 = require("socket.io-client");
145
+
146
+ // src/data-copy.ts
147
+ function getByPath(obj, path) {
148
+ const keys = path.split(".");
149
+ let current = obj;
150
+ for (const key of keys) {
151
+ if (current === null || current === void 0 || typeof current !== "object" || Array.isArray(current)) {
152
+ return void 0;
153
+ }
154
+ current = current[key];
155
+ }
156
+ return current;
157
+ }
158
+ function setByPath(obj, path, value) {
159
+ const keys = path.split(".");
160
+ const result = { ...obj };
161
+ if (keys.length === 1) {
162
+ result[keys[0]] = value;
163
+ return result;
164
+ }
165
+ let current = result;
166
+ for (let i = 0; i < keys.length - 1; i++) {
167
+ const key = keys[i];
168
+ const next = current[key];
169
+ if (next && typeof next === "object" && !Array.isArray(next)) {
170
+ current[key] = { ...next };
171
+ current = current[key];
172
+ } else {
173
+ current[key] = {};
174
+ current = current[key];
175
+ }
176
+ }
177
+ current[keys[keys.length - 1]] = value;
178
+ return result;
179
+ }
180
+ function scanDataCopyElements(doc) {
181
+ const elements = doc.querySelectorAll("[data-copy]");
182
+ return Array.from(elements).map((el) => ({
183
+ element: el,
184
+ path: el.getAttribute("data-copy") || "",
185
+ originalValue: el.textContent || ""
186
+ }));
187
+ }
188
+ function enableEditMode(elements) {
189
+ for (const { element } of elements) {
190
+ element.contentEditable = "true";
191
+ element.style.outline = "2px dashed hsl(var(--primary, 210 100% 50%))";
192
+ element.style.outlineOffset = "2px";
193
+ element.style.cursor = "text";
194
+ element.style.borderRadius = "2px";
195
+ element.style.transition = "outline-color 0.2s";
196
+ element.setAttribute("data-copy-active", "true");
197
+ }
198
+ }
199
+ function disableEditMode(elements) {
200
+ for (const { element } of elements) {
201
+ element.contentEditable = "false";
202
+ element.style.outline = "";
203
+ element.style.outlineOffset = "";
204
+ element.style.cursor = "";
205
+ element.style.borderRadius = "";
206
+ element.style.transition = "";
207
+ element.removeAttribute("data-copy-active");
208
+ }
209
+ }
210
+ function syncElementsFromData(elements, data) {
211
+ for (const item of elements) {
212
+ const val = getByPath(data, item.path);
213
+ if (val !== void 0 && typeof val === "string") {
214
+ if (item.element.textContent !== val) {
215
+ item.element.textContent = val;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ function injectEditToggle(doc, onToggle) {
221
+ const btn = doc.createElement("button");
222
+ btn.id = "jsonify-edit-toggle";
223
+ btn.textContent = "\u270F\uFE0F Edit Mode";
224
+ btn.style.cssText = `
225
+ position: fixed;
226
+ bottom: 20px;
227
+ right: 20px;
228
+ z-index: 99999;
229
+ padding: 8px 16px;
230
+ border: none;
231
+ border-radius: 8px;
232
+ background: hsl(210, 100%, 50%);
233
+ color: white;
234
+ font-size: 13px;
235
+ font-weight: 600;
236
+ cursor: pointer;
237
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
238
+ transition: all 0.2s;
239
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
240
+ `;
241
+ let active = false;
242
+ btn.addEventListener("click", () => {
243
+ active = !active;
244
+ btn.textContent = active ? "\u2705 Editing" : "\u270F\uFE0F Edit Mode";
245
+ btn.style.background = active ? "hsl(140, 60%, 45%)" : "hsl(210, 100%, 50%)";
246
+ onToggle(active);
247
+ });
248
+ doc.body.appendChild(btn);
249
+ return () => {
250
+ btn.remove();
251
+ };
252
+ }
253
+ function injectWsStatusIndicator(doc) {
254
+ const indicator = doc.createElement("div");
255
+ indicator.id = "jsonify-ws-status";
256
+ indicator.style.cssText = `
257
+ position: fixed;
258
+ bottom: 20px;
259
+ left: 20px;
260
+ z-index: 99999;
261
+ display: flex;
262
+ align-items: center;
263
+ gap: 8px;
264
+ padding: 8px 14px;
265
+ border-radius: 20px;
266
+ background: hsl(0, 0%, 15%);
267
+ color: white;
268
+ font-size: 12px;
269
+ font-weight: 500;
270
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
271
+ box-shadow: 0 4px 12px rgba(0,0,0,0.2);
272
+ transition: all 0.3s ease;
273
+ user-select: none;
274
+ `;
275
+ const dot = doc.createElement("span");
276
+ dot.style.cssText = `
277
+ width: 8px;
278
+ height: 8px;
279
+ border-radius: 50%;
280
+ transition: all 0.3s ease;
281
+ `;
282
+ const label = doc.createElement("span");
283
+ label.textContent = "Disconnected";
284
+ indicator.appendChild(dot);
285
+ indicator.appendChild(label);
286
+ const statusConfig = {
287
+ disconnected: {
288
+ dotColor: "hsl(0, 70%, 55%)",
289
+ dotShadow: "0 0 6px hsl(0, 70%, 55%)",
290
+ label: "Disconnected"
291
+ },
292
+ connecting: {
293
+ dotColor: "hsl(45, 90%, 50%)",
294
+ dotShadow: "0 0 6px hsl(45, 90%, 50%)",
295
+ label: "Connecting..."
296
+ },
297
+ connected: {
298
+ dotColor: "hsl(140, 70%, 45%)",
299
+ dotShadow: "0 0 6px hsl(140, 70%, 45%)",
300
+ label: "Connected"
301
+ }
302
+ };
303
+ const updateStatus = (status) => {
304
+ const config = statusConfig[status];
305
+ dot.style.background = config.dotColor;
306
+ dot.style.boxShadow = config.dotShadow;
307
+ label.textContent = config.label;
308
+ if (status === "connecting") {
309
+ dot.style.animation = "jsonify-pulse 1.5s ease-in-out infinite";
310
+ } else {
311
+ dot.style.animation = "none";
312
+ }
313
+ };
314
+ if (!doc.getElementById("jsonify-ws-status-styles")) {
315
+ const style = doc.createElement("style");
316
+ style.id = "jsonify-ws-status-styles";
317
+ style.textContent = `
318
+ @keyframes jsonify-pulse {
319
+ 0%, 100% { opacity: 1; transform: scale(1); }
320
+ 50% { opacity: 0.5; transform: scale(1.2); }
321
+ }
322
+ `;
323
+ doc.head.appendChild(style);
324
+ }
325
+ updateStatus("disconnected");
326
+ doc.body.appendChild(indicator);
327
+ const cleanup = () => {
328
+ indicator.remove();
329
+ doc.getElementById("jsonify-ws-status-styles")?.remove();
330
+ };
331
+ return { cleanup, updateStatus };
332
+ }
333
+
334
+ // src/useJsonify.ts
335
+ var import_meta2 = {};
336
+ function resolveUrl2(optionsUrl) {
337
+ if (optionsUrl) return optionsUrl;
338
+ if (typeof import_meta2 !== "undefined" && import_meta2.env?.VITE_WSL_URL) {
339
+ return import_meta2.env.VITE_WSL_URL;
340
+ }
341
+ if (typeof import_meta2 !== "undefined" && import_meta2.env?.WSL_URL) {
342
+ return import_meta2.env.WSL_URL;
343
+ }
344
+ if (typeof process !== "undefined" && process.env?.REACT_APP_WSL_URL) {
345
+ return process.env.REACT_APP_WSL_URL;
346
+ }
347
+ if (typeof process !== "undefined" && process.env?.WSL_URL) {
348
+ return process.env.WSL_URL;
349
+ }
350
+ return "http://localhost:4000";
351
+ }
352
+ function useJsonify(options = {}) {
353
+ const {
354
+ url: optionsUrl,
355
+ autoConnect = false,
356
+ initialData = {},
357
+ reconnectionDelay = 3e3,
358
+ onSync,
359
+ onStatusChange,
360
+ onError,
361
+ targetDocument,
362
+ injectToggle = true,
363
+ injectStatusIndicator = false
364
+ } = options;
365
+ const resolvedUrl = resolveUrl2(optionsUrl);
366
+ const [data, setDataState] = (0, import_react2.useState)(initialData);
367
+ const [status, setStatus] = (0, import_react2.useState)("disconnected");
368
+ const [editMode, setEditModeState] = (0, import_react2.useState)(false);
369
+ const [elements, setElements] = (0, import_react2.useState)([]);
370
+ const socketRef = (0, import_react2.useRef)(null);
371
+ const isRemoteUpdate = (0, import_react2.useRef)(false);
372
+ const dataRef = (0, import_react2.useRef)(data);
373
+ const editModeRef = (0, import_react2.useRef)(editMode);
374
+ const elementsRef = (0, import_react2.useRef)(elements);
375
+ const cleanupToggleRef = (0, import_react2.useRef)(null);
376
+ const wsStatusRef = (0, import_react2.useRef)(null);
377
+ dataRef.current = data;
378
+ editModeRef.current = editMode;
379
+ elementsRef.current = elements;
380
+ const updateStatus = (0, import_react2.useCallback)(
381
+ (s) => {
382
+ setStatus(s);
383
+ onStatusChange?.(s);
384
+ wsStatusRef.current?.updateStatus(s);
385
+ },
386
+ [onStatusChange]
387
+ );
388
+ const cleanup = (0, import_react2.useCallback)(() => {
389
+ if (socketRef.current) {
390
+ socketRef.current.disconnect();
391
+ socketRef.current = null;
392
+ }
393
+ updateStatus("disconnected");
394
+ }, [updateStatus]);
395
+ const connect = (0, import_react2.useCallback)(
396
+ (overrideUrl) => {
397
+ cleanup();
398
+ const targetUrl = overrideUrl || resolvedUrl;
399
+ updateStatus("connecting");
400
+ const socket = (0, import_socket2.io)(targetUrl, {
401
+ reconnection: true,
402
+ reconnectionDelay,
403
+ reconnectionAttempts: Infinity
404
+ });
405
+ socketRef.current = socket;
406
+ socket.on("connect", () => {
407
+ updateStatus("connected");
408
+ socket.emit("update", dataRef.current);
409
+ });
410
+ socket.on("sync", (syncData) => {
411
+ isRemoteUpdate.current = true;
412
+ setDataState(syncData);
413
+ onSync?.(syncData);
414
+ if (editModeRef.current) {
415
+ syncElementsFromData(elementsRef.current, syncData);
416
+ }
417
+ });
418
+ socket.on("disconnect", () => {
419
+ updateStatus("disconnected");
420
+ });
421
+ socket.on("connect_error", (err) => {
422
+ onError?.(err);
423
+ });
424
+ },
425
+ [cleanup, resolvedUrl, reconnectionDelay, onSync, onStatusChange, onError]
426
+ );
427
+ const disconnect = (0, import_react2.useCallback)(() => {
428
+ cleanup();
429
+ }, [cleanup]);
430
+ const setData = (0, import_react2.useCallback)((newData) => {
431
+ setDataState(newData);
432
+ }, []);
433
+ const setPath = (0, import_react2.useCallback)((path, value) => {
434
+ setDataState((prev) => setByPath(prev, path, value));
435
+ }, []);
436
+ (0, import_react2.useEffect)(() => {
437
+ if (isRemoteUpdate.current) {
438
+ isRemoteUpdate.current = false;
439
+ return;
440
+ }
441
+ if (socketRef.current?.connected) {
442
+ socketRef.current.emit("update", data);
443
+ }
444
+ }, [data]);
445
+ (0, import_react2.useEffect)(() => {
446
+ if (autoConnect) {
447
+ connect();
448
+ }
449
+ return () => {
450
+ cleanup();
451
+ };
452
+ }, []);
453
+ const rescan = (0, import_react2.useCallback)(() => {
454
+ const doc = targetDocument || (typeof document !== "undefined" ? document : null);
455
+ if (!doc) return;
456
+ const found = scanDataCopyElements(doc);
457
+ setElements(found);
458
+ return found;
459
+ }, [targetDocument]);
460
+ const setEditMode = (0, import_react2.useCallback)(
461
+ (active) => {
462
+ setEditModeState(active);
463
+ const currentElements = elementsRef.current.length > 0 ? elementsRef.current : rescan() || [];
464
+ if (active) {
465
+ enableEditMode(currentElements);
466
+ syncElementsFromData(currentElements, dataRef.current);
467
+ for (const item of currentElements) {
468
+ const handler = () => {
469
+ const newVal = item.element.textContent || "";
470
+ setDataState((prev) => setByPath(prev, item.path, newVal));
471
+ };
472
+ item.element.addEventListener("input", handler);
473
+ item.element.__jsonifyHandler = handler;
474
+ }
475
+ } else {
476
+ for (const item of currentElements) {
477
+ if (item.element.__jsonifyHandler) {
478
+ item.element.removeEventListener("input", item.element.__jsonifyHandler);
479
+ delete item.element.__jsonifyHandler;
480
+ }
481
+ }
482
+ disableEditMode(currentElements);
483
+ }
484
+ },
485
+ [rescan]
486
+ );
487
+ const toggleEditMode = (0, import_react2.useCallback)(() => {
488
+ setEditMode(!editModeRef.current);
489
+ }, [setEditMode]);
490
+ (0, import_react2.useEffect)(() => {
491
+ if (!injectToggle) return;
492
+ const doc = targetDocument || (typeof document !== "undefined" ? document : null);
493
+ if (!doc) return;
494
+ cleanupToggleRef.current = injectEditToggle(doc, (active) => {
495
+ setEditMode(active);
496
+ });
497
+ return () => {
498
+ cleanupToggleRef.current?.();
499
+ };
500
+ }, [injectToggle, targetDocument, setEditMode]);
501
+ (0, import_react2.useEffect)(() => {
502
+ if (!injectStatusIndicator) return;
503
+ const doc = targetDocument || (typeof document !== "undefined" ? document : null);
504
+ if (!doc) return;
505
+ wsStatusRef.current = injectWsStatusIndicator(doc);
506
+ wsStatusRef.current.updateStatus(status);
507
+ return () => {
508
+ wsStatusRef.current?.cleanup();
509
+ wsStatusRef.current = null;
510
+ };
511
+ }, [injectStatusIndicator, targetDocument]);
512
+ (0, import_react2.useEffect)(() => {
513
+ rescan();
514
+ }, [rescan]);
515
+ return {
516
+ data,
517
+ setData,
518
+ setPath,
519
+ status,
520
+ connect,
521
+ disconnect,
522
+ url: resolvedUrl,
523
+ editMode,
524
+ toggleEditMode,
525
+ setEditMode,
526
+ elements,
527
+ rescan
528
+ };
529
+ }
530
+ // Annotate the CommonJS export names for ESM import in node:
531
+ 0 && (module.exports = {
532
+ disableEditMode,
533
+ enableEditMode,
534
+ getByPath,
535
+ injectEditToggle,
536
+ scanDataCopyElements,
537
+ setByPath,
538
+ syncElementsFromData,
539
+ useJsonify,
540
+ useJsonifyWs
541
+ });
542
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/useJsonifyWs.ts","../src/useJsonify.ts","../src/data-copy.ts"],"sourcesContent":["export { useJsonifyWs } from \"./useJsonifyWs\";\nexport { useJsonify } from \"./useJsonify\";\nexport type { UseJsonifyOptions, UseJsonifyReturn } from \"./useJsonify\";\nexport type { JsonifyWsOptions, JsonifyWsReturn, WsStatus, JsonValue } from \"./types\";\nexport {\n scanDataCopyElements,\n enableEditMode,\n disableEditMode,\n syncElementsFromData,\n getByPath,\n setByPath,\n injectEditToggle,\n type DataCopyElement,\n} from \"./data-copy\";\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { io, Socket } from \"socket.io-client\";\nimport type { JsonifyWsOptions, JsonifyWsReturn, JsonValue, WsStatus } from \"./types\";\n\n/**\n * Get the WebSocket URL from options or environment.\n * Priority: options.url > WSL_URL env > fallback\n */\nfunction resolveUrl(optionsUrl?: string): string {\n if (optionsUrl) return optionsUrl;\n\n // Support Vite env\n if (typeof import.meta !== \"undefined\" && (import.meta as any).env?.WSL_URL) {\n return (import.meta as any).env.WSL_URL;\n }\n\n // Support process.env (Node / CRA / Next.js)\n if (typeof process !== \"undefined\" && process.env?.WSL_URL) {\n return process.env.WSL_URL;\n }\n\n return \"http://localhost:4000\";\n}\n\n/**\n * React hook for real-time JSON synchronization over WebSocket.\n *\n * @example\n * ```tsx\n * import { useJsonifyWs } from \"jsonify-ws\";\n *\n * function App() {\n * const { data, setData, status, connect, disconnect } = useJsonifyWs({\n * autoConnect: true,\n * initialData: { hello: \"world\" },\n * });\n *\n * return <pre>{JSON.stringify(data, null, 2)}</pre>;\n * }\n * ```\n */\nexport function useJsonifyWs(options: JsonifyWsOptions = {}): JsonifyWsReturn {\n const {\n url: optionsUrl,\n autoConnect = false,\n initialData = {},\n reconnectionDelay = 3000,\n onSync,\n onStatusChange,\n onError,\n } = options;\n\n const resolvedUrl = resolveUrl(optionsUrl);\n const [data, setDataState] = useState<JsonValue>(initialData);\n const [status, setStatus] = useState<WsStatus>(\"disconnected\");\n const socketRef = useRef<Socket | null>(null);\n const isRemoteUpdate = useRef(false);\n const dataRef = useRef(data);\n\n // Keep dataRef in sync\n dataRef.current = data;\n\n // Notify status changes\n const updateStatus = useCallback(\n (s: WsStatus) => {\n setStatus(s);\n onStatusChange?.(s);\n },\n [onStatusChange],\n );\n\n const cleanup = useCallback(() => {\n if (socketRef.current) {\n socketRef.current.disconnect();\n socketRef.current = null;\n }\n updateStatus(\"disconnected\");\n }, [updateStatus]);\n\n const connect = useCallback(\n (overrideUrl?: string) => {\n cleanup();\n const targetUrl = overrideUrl || resolvedUrl;\n updateStatus(\"connecting\");\n\n const socket = io(targetUrl, {\n reconnection: true,\n reconnectionDelay,\n reconnectionAttempts: Infinity,\n });\n socketRef.current = socket;\n\n socket.on(\"connect\", () => {\n updateStatus(\"connected\");\n socket.emit(\"update\", dataRef.current);\n });\n\n socket.on(\"sync\", (syncData: JsonValue) => {\n isRemoteUpdate.current = true;\n setDataState(syncData);\n onSync?.(syncData);\n });\n\n socket.on(\"disconnect\", () => {\n updateStatus(\"disconnected\");\n });\n\n socket.on(\"connect_error\", (err) => {\n onError?.(err);\n });\n },\n [cleanup, resolvedUrl, reconnectionDelay, onSync, onStatusChange, onError],\n );\n\n const disconnect = useCallback(() => {\n cleanup();\n }, [cleanup]);\n\n // Public setData — updates local + broadcasts\n const setData = useCallback((newData: JsonValue) => {\n setDataState(newData);\n }, []);\n\n // Broadcast local changes\n useEffect(() => {\n if (isRemoteUpdate.current) {\n isRemoteUpdate.current = false;\n return;\n }\n if (socketRef.current?.connected) {\n socketRef.current.emit(\"update\", data);\n }\n }, [data]);\n\n // Auto-connect\n useEffect(() => {\n if (autoConnect) {\n connect();\n }\n return () => {\n cleanup();\n };\n }, []);\n\n return {\n data,\n setData,\n status,\n connect,\n disconnect,\n url: resolvedUrl,\n };\n}\n","import { useCallback, useEffect, useRef, useState } from \"react\";\nimport { io, Socket } from \"socket.io-client\";\nimport type { JsonValue, WsStatus } from \"./types\";\nimport {\n scanDataCopyElements,\n enableEditMode,\n disableEditMode,\n syncElementsFromData,\n setByPath,\n injectEditToggle,\n injectWsStatusIndicator,\n type DataCopyElement,\n} from \"./data-copy\";\n\nexport interface UseJsonifyOptions {\n /**\n * WebSocket server URL. Defaults to WSL_URL env variable.\n * Falls back to \"http://localhost:4000\" if not set.\n */\n url?: string;\n\n /**\n * Auto-connect on mount. Default: false\n */\n autoConnect?: boolean;\n\n /**\n * Initial JSON data. Default: {}\n */\n initialData?: Record<string, JsonValue>;\n\n /**\n * Reconnection delay in ms. Default: 3000\n */\n reconnectionDelay?: number;\n\n /**\n * Callback fired when remote data is received.\n */\n onSync?: (data: Record<string, JsonValue>) => void;\n\n /**\n * Callback fired when connection status changes.\n */\n onStatusChange?: (status: WsStatus) => void;\n\n /**\n * Callback fired on connection error.\n */\n onError?: (error: Error) => void;\n\n /**\n * Target document for data-copy scanning.\n * Defaults to window.document.\n * Pass an iframe's contentDocument to scan an iframe.\n */\n targetDocument?: Document;\n\n /**\n * Auto-inject the floating edit toggle button. Default: true\n */\n injectToggle?: boolean;\n\n /**\n * Auto-inject the WebSocket connection status indicator. Default: false\n */\n injectStatusIndicator?: boolean;\n}\n\nexport interface UseJsonifyReturn {\n /** Current JSON data state */\n data: Record<string, JsonValue>;\n /** Update the JSON data (will broadcast to peers) */\n setData: (data: Record<string, JsonValue>) => void;\n /** Update a single path in the data */\n setPath: (path: string, value: JsonValue) => void;\n /** Current connection status */\n status: WsStatus;\n /** Connect to the WebSocket server */\n connect: (url?: string) => void;\n /** Disconnect from the WebSocket server */\n disconnect: () => void;\n /** The resolved WebSocket URL */\n url: string;\n /** Whether edit mode is active */\n editMode: boolean;\n /** Toggle edit mode on/off */\n toggleEditMode: () => void;\n /** Set edit mode explicitly */\n setEditMode: (active: boolean) => void;\n /** Scanned data-copy elements */\n elements: DataCopyElement[];\n /** Re-scan the document for data-copy elements */\n rescan: () => void;\n}\n\nfunction resolveUrl(optionsUrl?: string): string {\n if (optionsUrl) return optionsUrl;\n\n // Support Vite env\n if (typeof import.meta !== \"undefined\" && (import.meta as any).env?.VITE_WSL_URL) {\n return (import.meta as any).env.VITE_WSL_URL;\n }\n if (typeof import.meta !== \"undefined\" && (import.meta as any).env?.WSL_URL) {\n return (import.meta as any).env.WSL_URL;\n }\n\n // Support process.env (Node / CRA / Next.js)\n if (typeof process !== \"undefined\" && process.env?.REACT_APP_WSL_URL) {\n return process.env.REACT_APP_WSL_URL;\n }\n if (typeof process !== \"undefined\" && process.env?.WSL_URL) {\n return process.env.WSL_URL;\n }\n\n return \"http://localhost:4000\";\n}\n\n/**\n * React hook for real-time JSON synchronization with data-copy attribute support.\n *\n * @example\n * ```tsx\n * import { useJsonify } from \"jsonify-ws\";\n *\n * function App() {\n * const j = useJsonify({\n * autoConnect: true,\n * initialData: { home: { title: \"Hello\" } },\n * });\n *\n * return (\n * <div>\n * <p>Status: {j.status}</p>\n * <button onClick={j.toggleEditMode}>\n * {j.editMode ? \"Stop Editing\" : \"Edit Mode\"}\n * </button>\n * <h1 data-copy=\"home.title\">{j.data.home?.title}</h1>\n * </div>\n * );\n * }\n * ```\n */\nexport function useJsonify(options: UseJsonifyOptions = {}): UseJsonifyReturn {\n const {\n url: optionsUrl,\n autoConnect = false,\n initialData = {},\n reconnectionDelay = 3000,\n onSync,\n onStatusChange,\n onError,\n targetDocument,\n injectToggle = true,\n injectStatusIndicator = false,\n } = options;\n\n const resolvedUrl = resolveUrl(optionsUrl);\n const [data, setDataState] = useState<Record<string, JsonValue>>(initialData);\n const [status, setStatus] = useState<WsStatus>(\"disconnected\");\n const [editMode, setEditModeState] = useState(false);\n const [elements, setElements] = useState<DataCopyElement[]>([]);\n\n const socketRef = useRef<Socket | null>(null);\n const isRemoteUpdate = useRef(false);\n const dataRef = useRef(data);\n const editModeRef = useRef(editMode);\n const elementsRef = useRef(elements);\n const cleanupToggleRef = useRef<(() => void) | null>(null);\n const wsStatusRef = useRef<{ cleanup: () => void; updateStatus: (status: WsStatus) => void } | null>(null);\n\n dataRef.current = data;\n editModeRef.current = editMode;\n elementsRef.current = elements;\n\n const updateStatus = useCallback(\n (s: WsStatus) => {\n setStatus(s);\n onStatusChange?.(s);\n // Update the visual status indicator if injected\n wsStatusRef.current?.updateStatus(s);\n },\n [onStatusChange],\n );\n\n const cleanup = useCallback(() => {\n if (socketRef.current) {\n socketRef.current.disconnect();\n socketRef.current = null;\n }\n updateStatus(\"disconnected\");\n }, [updateStatus]);\n\n const connect = useCallback(\n (overrideUrl?: string) => {\n cleanup();\n const targetUrl = overrideUrl || resolvedUrl;\n updateStatus(\"connecting\");\n\n const socket = io(targetUrl, {\n reconnection: true,\n reconnectionDelay,\n reconnectionAttempts: Infinity,\n });\n socketRef.current = socket;\n\n socket.on(\"connect\", () => {\n updateStatus(\"connected\");\n socket.emit(\"update\", dataRef.current);\n });\n\n socket.on(\"sync\", (syncData: Record<string, JsonValue>) => {\n isRemoteUpdate.current = true;\n setDataState(syncData);\n onSync?.(syncData);\n // Sync data-copy elements with new data\n if (editModeRef.current) {\n syncElementsFromData(elementsRef.current, syncData);\n }\n });\n\n socket.on(\"disconnect\", () => {\n updateStatus(\"disconnected\");\n });\n\n socket.on(\"connect_error\", (err) => {\n onError?.(err);\n });\n },\n [cleanup, resolvedUrl, reconnectionDelay, onSync, onStatusChange, onError],\n );\n\n const disconnect = useCallback(() => {\n cleanup();\n }, [cleanup]);\n\n const setData = useCallback((newData: Record<string, JsonValue>) => {\n setDataState(newData);\n }, []);\n\n const setPath = useCallback((path: string, value: JsonValue) => {\n setDataState((prev) => setByPath(prev, path, value));\n }, []);\n\n // Broadcast local changes\n useEffect(() => {\n if (isRemoteUpdate.current) {\n isRemoteUpdate.current = false;\n return;\n }\n if (socketRef.current?.connected) {\n socketRef.current.emit(\"update\", data);\n }\n }, [data]);\n\n // Auto-connect\n useEffect(() => {\n if (autoConnect) {\n connect();\n }\n return () => {\n cleanup();\n };\n }, []);\n\n // Scan for data-copy elements\n const rescan = useCallback(() => {\n const doc = targetDocument || (typeof document !== \"undefined\" ? document : null);\n if (!doc) return;\n const found = scanDataCopyElements(doc);\n setElements(found);\n return found;\n }, [targetDocument]);\n\n // Handle edit mode toggle\n const setEditMode = useCallback(\n (active: boolean) => {\n setEditModeState(active);\n const currentElements = elementsRef.current.length > 0 ? elementsRef.current : (rescan() || []);\n\n if (active) {\n enableEditMode(currentElements);\n syncElementsFromData(currentElements, dataRef.current);\n\n // Attach input listeners\n for (const item of currentElements) {\n const handler = () => {\n const newVal = item.element.textContent || \"\";\n setDataState((prev) => setByPath(prev, item.path, newVal));\n };\n item.element.addEventListener(\"input\", handler);\n (item.element as any).__jsonifyHandler = handler;\n }\n } else {\n // Remove input listeners\n for (const item of currentElements) {\n if ((item.element as any).__jsonifyHandler) {\n item.element.removeEventListener(\"input\", (item.element as any).__jsonifyHandler);\n delete (item.element as any).__jsonifyHandler;\n }\n }\n disableEditMode(currentElements);\n }\n },\n [rescan],\n );\n\n const toggleEditMode = useCallback(() => {\n setEditMode(!editModeRef.current);\n }, [setEditMode]);\n\n // Inject floating toggle button\n useEffect(() => {\n if (!injectToggle) return;\n const doc = targetDocument || (typeof document !== \"undefined\" ? document : null);\n if (!doc) return;\n\n cleanupToggleRef.current = injectEditToggle(doc, (active) => {\n setEditMode(active);\n });\n\n return () => {\n cleanupToggleRef.current?.();\n };\n }, [injectToggle, targetDocument, setEditMode]);\n\n // Inject WebSocket status indicator\n useEffect(() => {\n if (!injectStatusIndicator) return;\n const doc = targetDocument || (typeof document !== \"undefined\" ? document : null);\n if (!doc) return;\n\n wsStatusRef.current = injectWsStatusIndicator(doc);\n // Set initial status\n wsStatusRef.current.updateStatus(status);\n\n return () => {\n wsStatusRef.current?.cleanup();\n wsStatusRef.current = null;\n };\n }, [injectStatusIndicator, targetDocument]);\n\n // Initial scan\n useEffect(() => {\n rescan();\n }, [rescan]);\n\n return {\n data,\n setData,\n setPath,\n status,\n connect,\n disconnect,\n url: resolvedUrl,\n editMode,\n toggleEditMode,\n setEditMode,\n elements,\n rescan,\n };\n}\n","/**\n * data-copy attribute utilities.\n *\n * Scans a document (or iframe) for elements with `data-copy=\"path.to.key\"`\n * and enables contentEditable on them, syncing edits back to the JSON state.\n */\n\nimport type { JsonValue } from \"./types\";\n\nexport interface DataCopyElement {\n element: HTMLElement;\n path: string;\n originalValue: string;\n}\n\n/**\n * Get a value from a nested object by dot-path.\n * e.g. getByPath({ home: { title: \"Hi\" } }, \"home.title\") => \"Hi\"\n */\nexport function getByPath(obj: Record<string, JsonValue>, path: string): JsonValue | undefined {\n const keys = path.split(\".\");\n let current: JsonValue = obj;\n for (const key of keys) {\n if (current === null || current === undefined || typeof current !== \"object\" || Array.isArray(current)) {\n return undefined;\n }\n current = (current as Record<string, JsonValue>)[key];\n }\n return current;\n}\n\n/**\n * Set a value in a nested object by dot-path (immutable — returns new object).\n */\nexport function setByPath(obj: Record<string, JsonValue>, path: string, value: JsonValue): Record<string, JsonValue> {\n const keys = path.split(\".\");\n const result = { ...obj };\n\n if (keys.length === 1) {\n result[keys[0]] = value;\n return result;\n }\n\n let current: Record<string, JsonValue> = result;\n for (let i = 0; i < keys.length - 1; i++) {\n const key = keys[i];\n const next = current[key];\n if (next && typeof next === \"object\" && !Array.isArray(next)) {\n current[key] = { ...(next as Record<string, JsonValue>) };\n current = current[key] as Record<string, JsonValue>;\n } else {\n current[key] = {};\n current = current[key] as Record<string, JsonValue>;\n }\n }\n current[keys[keys.length - 1]] = value;\n return result;\n}\n\n/**\n * Scan a document for elements with `data-copy` attribute.\n */\nexport function scanDataCopyElements(doc: Document): DataCopyElement[] {\n const elements = doc.querySelectorAll<HTMLElement>(\"[data-copy]\");\n return Array.from(elements).map((el) => ({\n element: el,\n path: el.getAttribute(\"data-copy\") || \"\",\n originalValue: el.textContent || \"\",\n }));\n}\n\n/**\n * Apply contentEditable styling to data-copy elements.\n */\nexport function enableEditMode(elements: DataCopyElement[]): void {\n for (const { element } of elements) {\n element.contentEditable = \"true\";\n element.style.outline = \"2px dashed hsl(var(--primary, 210 100% 50%))\";\n element.style.outlineOffset = \"2px\";\n element.style.cursor = \"text\";\n element.style.borderRadius = \"2px\";\n element.style.transition = \"outline-color 0.2s\";\n element.setAttribute(\"data-copy-active\", \"true\");\n }\n}\n\n/**\n * Remove contentEditable styling from data-copy elements.\n */\nexport function disableEditMode(elements: DataCopyElement[]): void {\n for (const { element } of elements) {\n element.contentEditable = \"false\";\n element.style.outline = \"\";\n element.style.outlineOffset = \"\";\n element.style.cursor = \"\";\n element.style.borderRadius = \"\";\n element.style.transition = \"\";\n element.removeAttribute(\"data-copy-active\");\n }\n}\n\n/**\n * Update element text content from JSON data.\n */\nexport function syncElementsFromData(\n elements: DataCopyElement[],\n data: Record<string, JsonValue>,\n): void {\n for (const item of elements) {\n const val = getByPath(data, item.path);\n if (val !== undefined && typeof val === \"string\") {\n if (item.element.textContent !== val) {\n item.element.textContent = val;\n }\n }\n }\n}\n\n/**\n * Inject a floating \"Edit Mode\" toggle button into the page.\n * Returns a cleanup function.\n */\nexport function injectEditToggle(\n doc: Document,\n onToggle: (active: boolean) => void,\n): () => void {\n const btn = doc.createElement(\"button\");\n btn.id = \"jsonify-edit-toggle\";\n btn.textContent = \"✏️ Edit Mode\";\n btn.style.cssText = `\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 99999;\n padding: 8px 16px;\n border: none;\n border-radius: 8px;\n background: hsl(210, 100%, 50%);\n color: white;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 4px 12px rgba(0,0,0,0.15);\n transition: all 0.2s;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n `;\n\n let active = false;\n btn.addEventListener(\"click\", () => {\n active = !active;\n btn.textContent = active ? \"✅ Editing\" : \"✏️ Edit Mode\";\n btn.style.background = active ? \"hsl(140, 60%, 45%)\" : \"hsl(210, 100%, 50%)\";\n onToggle(active);\n });\n\n doc.body.appendChild(btn);\n\n return () => {\n btn.remove();\n };\n}\n\n/**\n * Inject a floating WebSocket connection status indicator into the page.\n * Shows real-time connection status: disconnected (red), connecting (yellow), connected (green).\n * Returns a cleanup function and a function to update the status.\n */\nexport function injectWsStatusIndicator(\n doc: Document,\n): { cleanup: () => void; updateStatus: (status: \"disconnected\" | \"connecting\" | \"connected\") => void } {\n const indicator = doc.createElement(\"div\");\n indicator.id = \"jsonify-ws-status\";\n indicator.style.cssText = `\n position: fixed;\n bottom: 20px;\n left: 20px;\n z-index: 99999;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-radius: 20px;\n background: hsl(0, 0%, 15%);\n color: white;\n font-size: 12px;\n font-weight: 500;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif;\n box-shadow: 0 4px 12px rgba(0,0,0,0.2);\n transition: all 0.3s ease;\n user-select: none;\n `;\n\n const dot = doc.createElement(\"span\");\n dot.style.cssText = `\n width: 8px;\n height: 8px;\n border-radius: 50%;\n transition: all 0.3s ease;\n `;\n\n const label = doc.createElement(\"span\");\n label.textContent = \"Disconnected\";\n\n indicator.appendChild(dot);\n indicator.appendChild(label);\n\n const statusConfig = {\n disconnected: {\n dotColor: \"hsl(0, 70%, 55%)\",\n dotShadow: \"0 0 6px hsl(0, 70%, 55%)\",\n label: \"Disconnected\",\n },\n connecting: {\n dotColor: \"hsl(45, 90%, 50%)\",\n dotShadow: \"0 0 6px hsl(45, 90%, 50%)\",\n label: \"Connecting...\",\n },\n connected: {\n dotColor: \"hsl(140, 70%, 45%)\",\n dotShadow: \"0 0 6px hsl(140, 70%, 45%)\",\n label: \"Connected\",\n },\n };\n\n const updateStatus = (status: \"disconnected\" | \"connecting\" | \"connected\") => {\n const config = statusConfig[status];\n dot.style.background = config.dotColor;\n dot.style.boxShadow = config.dotShadow;\n label.textContent = config.label;\n\n // Add pulse animation for connecting state\n if (status === \"connecting\") {\n dot.style.animation = \"jsonify-pulse 1.5s ease-in-out infinite\";\n } else {\n dot.style.animation = \"none\";\n }\n };\n\n // Add keyframe animation for pulse\n if (!doc.getElementById(\"jsonify-ws-status-styles\")) {\n const style = doc.createElement(\"style\");\n style.id = \"jsonify-ws-status-styles\";\n style.textContent = `\n @keyframes jsonify-pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(1.2); }\n }\n `;\n doc.head.appendChild(style);\n }\n\n // Set initial state\n updateStatus(\"disconnected\");\n\n doc.body.appendChild(indicator);\n\n const cleanup = () => {\n indicator.remove();\n doc.getElementById(\"jsonify-ws-status-styles\")?.remove();\n };\n\n return { cleanup, updateStatus };\n}\n"],"mappings":"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","names":["import_react","import_socket","import_meta","resolveUrl"]}