@symbo.ls/sync 2.29.51 → 2.29.53
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/cjs/index.js +135 -48
- package/dist/esm/index.js +135 -48
- package/index.js +160 -55
- package/package.json +11 -10
package/dist/cjs/index.js
CHANGED
|
@@ -27,64 +27,151 @@ __export(index_exports, {
|
|
|
27
27
|
module.exports = __toCommonJS(index_exports);
|
|
28
28
|
var import_router = require("@domql/router");
|
|
29
29
|
var import_init = require("@symbo.ls/init");
|
|
30
|
-
var
|
|
30
|
+
var import_socket = require("socket.io-client");
|
|
31
31
|
var import_utils = require("@domql/utils");
|
|
32
32
|
var import_SyncNotifications = require("./SyncNotifications");
|
|
33
33
|
var import_Inspect = require("./Inspect");
|
|
34
34
|
const isLocalhost = import_utils.window && import_utils.window.location && import_utils.window.location.host.includes("local");
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const deletePath = (obj, path) => {
|
|
36
|
+
if (!obj || !Array.isArray(path)) return;
|
|
37
|
+
path.reduce((acc, v, i, arr) => {
|
|
38
|
+
if (acc && v in acc) {
|
|
39
|
+
if (i !== arr.length - 1) return acc[v];
|
|
40
|
+
delete acc[v];
|
|
41
|
+
}
|
|
42
|
+
return void 0;
|
|
43
|
+
}, obj);
|
|
38
44
|
};
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
const setPath = (obj, path, value, createNestedObjects = false) => {
|
|
46
|
+
if (!obj || !Array.isArray(path)) return;
|
|
47
|
+
path.reduce((acc, v, i, arr) => {
|
|
48
|
+
if (!acc) return void 0;
|
|
49
|
+
if (i !== arr.length - 1) {
|
|
50
|
+
if (!acc[v] && createNestedObjects) acc[v] = {};
|
|
51
|
+
return acc[v];
|
|
52
|
+
}
|
|
53
|
+
acc[v] = value;
|
|
54
|
+
return void 0;
|
|
55
|
+
}, obj);
|
|
42
56
|
};
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (snippets) {
|
|
60
|
-
(0, import_utils.overwriteShallow)(ctx.snippets, snippets);
|
|
61
|
-
}
|
|
62
|
-
if (state) {
|
|
63
|
-
const route = state.route;
|
|
64
|
-
if (route) el.call("router", route.replace("/state", "") || "/", el, {});
|
|
65
|
-
else if (!(snippets && functions && components && pages))
|
|
66
|
-
s.update(state);
|
|
67
|
-
}
|
|
68
|
-
if (snippets || functions || components || pages) {
|
|
69
|
-
const { pathname, search, hash } = ctx.window.location;
|
|
70
|
-
el.call("router", pathname + search + hash, el, {});
|
|
71
|
-
}
|
|
72
|
-
if (designSystem) (0, import_init.init)(designSystem);
|
|
57
|
+
const applyOpsToCtx = (ctx, changes) => {
|
|
58
|
+
const topLevelChanged = /* @__PURE__ */ new Set();
|
|
59
|
+
if (!Array.isArray(changes)) return topLevelChanged;
|
|
60
|
+
for (const [action, path, change] of changes) {
|
|
61
|
+
if (!Array.isArray(path) || !path.length) continue;
|
|
62
|
+
topLevelChanged.add(path[0]);
|
|
63
|
+
switch (action) {
|
|
64
|
+
case "delete":
|
|
65
|
+
deletePath(ctx, path);
|
|
66
|
+
break;
|
|
67
|
+
case "update":
|
|
68
|
+
case "set":
|
|
69
|
+
setPath(ctx, path, change, true);
|
|
70
|
+
break;
|
|
71
|
+
default:
|
|
72
|
+
break;
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
}
|
|
75
|
+
return topLevelChanged;
|
|
76
|
+
};
|
|
77
|
+
const fetchServiceToken = async () => {
|
|
78
|
+
try {
|
|
79
|
+
const urlBase = isLocalhost ? "http://localhost:8080" : "https://api.symbols.app";
|
|
80
|
+
const res = await import_utils.window.fetch(`${urlBase}/service-token`, { method: "GET" });
|
|
81
|
+
let txt;
|
|
82
|
+
try {
|
|
83
|
+
const json = await res.clone().json();
|
|
84
|
+
if (json && typeof json.token === "string") return json.token.trim();
|
|
85
|
+
txt = await res.text();
|
|
86
|
+
} catch {
|
|
87
|
+
txt = await res.text();
|
|
76
88
|
}
|
|
77
|
-
|
|
89
|
+
return (txt || "").replace(/\s+/g, "") || void 0;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error("[sync] Failed to fetch service-token", e);
|
|
92
|
+
return void 0;
|
|
93
|
+
}
|
|
78
94
|
};
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
95
|
+
const onSnapshot = (el, s, ctx) => (payload = {}) => {
|
|
96
|
+
var _a;
|
|
97
|
+
const { data, schema } = payload;
|
|
98
|
+
if (!data) return;
|
|
99
|
+
Object.entries(data).forEach(([key, val]) => {
|
|
100
|
+
if (ctx[key] && typeof ctx[key] === "object") {
|
|
101
|
+
(0, import_utils.overwriteShallow)(ctx[key], val);
|
|
102
|
+
} else {
|
|
103
|
+
ctx[key] = val;
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
if (schema) ctx.schema = schema;
|
|
107
|
+
const { pathname, search, hash } = ctx.window.location;
|
|
108
|
+
(((_a = ctx.utils) == null ? void 0 : _a.router) || import_router.router)(pathname + search + hash, el, {});
|
|
109
|
+
};
|
|
110
|
+
const onOps = (el, s, ctx) => (payload = {}) => {
|
|
111
|
+
var _a, _b, _c;
|
|
112
|
+
console.log("onOps", payload);
|
|
113
|
+
const { changes } = payload;
|
|
114
|
+
if (!changes || !Array.isArray(changes) || !changes.length) return;
|
|
115
|
+
const changed = applyOpsToCtx(ctx, changes);
|
|
116
|
+
if (changed.has("state")) {
|
|
117
|
+
const route = (_a = ctx.state) == null ? void 0 : _a.route;
|
|
118
|
+
if (route) {
|
|
119
|
+
;
|
|
120
|
+
(((_b = ctx.utils) == null ? void 0 : _b.router) || import_router.router)(route.replace("/state", "") || "/", el, {});
|
|
121
|
+
} else {
|
|
122
|
+
s.update(ctx.state);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (["pages", "components", "snippets", "functions"].some((k) => changed.has(k))) {
|
|
126
|
+
const { pathname, search, hash } = ctx.window.location;
|
|
127
|
+
(((_c = ctx.utils) == null ? void 0 : _c.router) || import_router.router)(pathname + search + hash, el, {});
|
|
128
|
+
}
|
|
129
|
+
if (changed.has("designSystem")) {
|
|
130
|
+
(0, import_init.init)(ctx.designSystem);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const connectToSocket = async (el, s, ctx) => {
|
|
134
|
+
const token = await fetchServiceToken();
|
|
135
|
+
if (!token) {
|
|
136
|
+
console.warn("[sync] No service token \u2013 live collaboration disabled");
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
const projectKey = ctx.key;
|
|
140
|
+
if (!projectKey) {
|
|
141
|
+
console.warn("[sync] ctx.key missing \u2013 cannot establish collaborative connection");
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
const socketBaseUrl = isLocalhost ? "http://localhost:8080" : "https://api.symbols.app";
|
|
145
|
+
const socket = (0, import_socket.io)(socketBaseUrl, {
|
|
146
|
+
path: "/collab-socket",
|
|
147
|
+
transports: ["websocket"],
|
|
148
|
+
auth: {
|
|
149
|
+
token,
|
|
150
|
+
projectKey,
|
|
151
|
+
branch: "main",
|
|
152
|
+
live: true,
|
|
153
|
+
clientType: "platform"
|
|
154
|
+
},
|
|
155
|
+
reconnectionAttempts: Infinity,
|
|
156
|
+
reconnectionDelayMax: 4e3
|
|
157
|
+
});
|
|
158
|
+
socket.on("connect", () => {
|
|
159
|
+
var _a;
|
|
160
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) console.info("[sync] Connected to collab socket");
|
|
161
|
+
});
|
|
162
|
+
socket.on("snapshot", onSnapshot(el, s, ctx));
|
|
163
|
+
socket.on("ops", onOps(el, s, ctx));
|
|
164
|
+
socket.on("clients", (data) => {
|
|
165
|
+
var _a;
|
|
166
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) {
|
|
167
|
+
(0, import_SyncNotifications.connectedToSymbols)(data, el, s);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
socket.on("disconnect", (reason) => {
|
|
171
|
+
var _a;
|
|
172
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) console.info("[sync] Disconnected from collab socket", reason);
|
|
87
173
|
});
|
|
174
|
+
return socket;
|
|
88
175
|
};
|
|
89
176
|
const SyncComponent = {
|
|
90
177
|
on: {
|
package/dist/esm/index.js
CHANGED
|
@@ -1,63 +1,150 @@
|
|
|
1
1
|
import { router } from "@domql/router";
|
|
2
2
|
import { init } from "@symbo.ls/init";
|
|
3
|
-
import {
|
|
3
|
+
import { io } from "socket.io-client";
|
|
4
4
|
import { window, overwriteShallow } from "@domql/utils";
|
|
5
5
|
import { connectedToSymbols, Notifications } from "./SyncNotifications";
|
|
6
6
|
import { Inspect } from "./Inspect";
|
|
7
7
|
const isLocalhost = window && window.location && window.location.host.includes("local");
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const deletePath = (obj, path) => {
|
|
9
|
+
if (!obj || !Array.isArray(path)) return;
|
|
10
|
+
path.reduce((acc, v, i, arr) => {
|
|
11
|
+
if (acc && v in acc) {
|
|
12
|
+
if (i !== arr.length - 1) return acc[v];
|
|
13
|
+
delete acc[v];
|
|
14
|
+
}
|
|
15
|
+
return void 0;
|
|
16
|
+
}, obj);
|
|
11
17
|
};
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
const setPath = (obj, path, value, createNestedObjects = false) => {
|
|
19
|
+
if (!obj || !Array.isArray(path)) return;
|
|
20
|
+
path.reduce((acc, v, i, arr) => {
|
|
21
|
+
if (!acc) return void 0;
|
|
22
|
+
if (i !== arr.length - 1) {
|
|
23
|
+
if (!acc[v] && createNestedObjects) acc[v] = {};
|
|
24
|
+
return acc[v];
|
|
25
|
+
}
|
|
26
|
+
acc[v] = value;
|
|
27
|
+
return void 0;
|
|
28
|
+
}, obj);
|
|
15
29
|
};
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (snippets) {
|
|
33
|
-
overwriteShallow(ctx.snippets, snippets);
|
|
34
|
-
}
|
|
35
|
-
if (state) {
|
|
36
|
-
const route = state.route;
|
|
37
|
-
if (route) el.call("router", route.replace("/state", "") || "/", el, {});
|
|
38
|
-
else if (!(snippets && functions && components && pages))
|
|
39
|
-
s.update(state);
|
|
40
|
-
}
|
|
41
|
-
if (snippets || functions || components || pages) {
|
|
42
|
-
const { pathname, search, hash } = ctx.window.location;
|
|
43
|
-
el.call("router", pathname + search + hash, el, {});
|
|
44
|
-
}
|
|
45
|
-
if (designSystem) init(designSystem);
|
|
30
|
+
const applyOpsToCtx = (ctx, changes) => {
|
|
31
|
+
const topLevelChanged = /* @__PURE__ */ new Set();
|
|
32
|
+
if (!Array.isArray(changes)) return topLevelChanged;
|
|
33
|
+
for (const [action, path, change] of changes) {
|
|
34
|
+
if (!Array.isArray(path) || !path.length) continue;
|
|
35
|
+
topLevelChanged.add(path[0]);
|
|
36
|
+
switch (action) {
|
|
37
|
+
case "delete":
|
|
38
|
+
deletePath(ctx, path);
|
|
39
|
+
break;
|
|
40
|
+
case "update":
|
|
41
|
+
case "set":
|
|
42
|
+
setPath(ctx, path, change, true);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
break;
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
}
|
|
48
|
+
return topLevelChanged;
|
|
49
|
+
};
|
|
50
|
+
const fetchServiceToken = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const urlBase = isLocalhost ? "http://localhost:8080" : "https://api.symbols.app";
|
|
53
|
+
const res = await window.fetch(`${urlBase}/service-token`, { method: "GET" });
|
|
54
|
+
let txt;
|
|
55
|
+
try {
|
|
56
|
+
const json = await res.clone().json();
|
|
57
|
+
if (json && typeof json.token === "string") return json.token.trim();
|
|
58
|
+
txt = await res.text();
|
|
59
|
+
} catch (e) {
|
|
60
|
+
txt = await res.text();
|
|
49
61
|
}
|
|
50
|
-
|
|
62
|
+
return (txt || "").replace(/\s+/g, "") || void 0;
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error("[sync] Failed to fetch service-token", e);
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
51
67
|
};
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
68
|
+
const onSnapshot = (el, s, ctx) => (payload = {}) => {
|
|
69
|
+
var _a;
|
|
70
|
+
const { data, schema } = payload;
|
|
71
|
+
if (!data) return;
|
|
72
|
+
Object.entries(data).forEach(([key, val]) => {
|
|
73
|
+
if (ctx[key] && typeof ctx[key] === "object") {
|
|
74
|
+
overwriteShallow(ctx[key], val);
|
|
75
|
+
} else {
|
|
76
|
+
ctx[key] = val;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
if (schema) ctx.schema = schema;
|
|
80
|
+
const { pathname, search, hash } = ctx.window.location;
|
|
81
|
+
(((_a = ctx.utils) == null ? void 0 : _a.router) || router)(pathname + search + hash, el, {});
|
|
82
|
+
};
|
|
83
|
+
const onOps = (el, s, ctx) => (payload = {}) => {
|
|
84
|
+
var _a, _b, _c;
|
|
85
|
+
console.log("onOps", payload);
|
|
86
|
+
const { changes } = payload;
|
|
87
|
+
if (!changes || !Array.isArray(changes) || !changes.length) return;
|
|
88
|
+
const changed = applyOpsToCtx(ctx, changes);
|
|
89
|
+
if (changed.has("state")) {
|
|
90
|
+
const route = (_a = ctx.state) == null ? void 0 : _a.route;
|
|
91
|
+
if (route) {
|
|
92
|
+
;
|
|
93
|
+
(((_b = ctx.utils) == null ? void 0 : _b.router) || router)(route.replace("/state", "") || "/", el, {});
|
|
94
|
+
} else {
|
|
95
|
+
s.update(ctx.state);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (["pages", "components", "snippets", "functions"].some((k) => changed.has(k))) {
|
|
99
|
+
const { pathname, search, hash } = ctx.window.location;
|
|
100
|
+
(((_c = ctx.utils) == null ? void 0 : _c.router) || router)(pathname + search + hash, el, {});
|
|
101
|
+
}
|
|
102
|
+
if (changed.has("designSystem")) {
|
|
103
|
+
init(ctx.designSystem);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const connectToSocket = async (el, s, ctx) => {
|
|
107
|
+
const token = await fetchServiceToken();
|
|
108
|
+
if (!token) {
|
|
109
|
+
console.warn("[sync] No service token \u2013 live collaboration disabled");
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const projectKey = ctx.key;
|
|
113
|
+
if (!projectKey) {
|
|
114
|
+
console.warn("[sync] ctx.key missing \u2013 cannot establish collaborative connection");
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const socketBaseUrl = isLocalhost ? "http://localhost:8080" : "https://api.symbols.app";
|
|
118
|
+
const socket = io(socketBaseUrl, {
|
|
119
|
+
path: "/collab-socket",
|
|
120
|
+
transports: ["websocket"],
|
|
121
|
+
auth: {
|
|
122
|
+
token,
|
|
123
|
+
projectKey,
|
|
124
|
+
branch: "main",
|
|
125
|
+
live: true,
|
|
126
|
+
clientType: "platform"
|
|
127
|
+
},
|
|
128
|
+
reconnectionAttempts: Infinity,
|
|
129
|
+
reconnectionDelayMax: 4e3
|
|
130
|
+
});
|
|
131
|
+
socket.on("connect", () => {
|
|
132
|
+
var _a;
|
|
133
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) console.info("[sync] Connected to collab socket");
|
|
134
|
+
});
|
|
135
|
+
socket.on("snapshot", onSnapshot(el, s, ctx));
|
|
136
|
+
socket.on("ops", onOps(el, s, ctx));
|
|
137
|
+
socket.on("clients", (data) => {
|
|
138
|
+
var _a;
|
|
139
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) {
|
|
140
|
+
connectedToSymbols(data, el, s);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
socket.on("disconnect", (reason) => {
|
|
144
|
+
var _a;
|
|
145
|
+
if ((_a = ctx.editor) == null ? void 0 : _a.verbose) console.info("[sync] Disconnected from collab socket", reason);
|
|
60
146
|
});
|
|
147
|
+
return socket;
|
|
61
148
|
};
|
|
62
149
|
const SyncComponent = {
|
|
63
150
|
on: {
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { router } from '@domql/router'
|
|
4
4
|
import { init } from '@symbo.ls/init'
|
|
5
|
-
import {
|
|
5
|
+
import { io } from 'socket.io-client'
|
|
6
6
|
import { window, overwriteShallow } from '@domql/utils'
|
|
7
7
|
import { connectedToSymbols, Notifications } from './SyncNotifications'
|
|
8
8
|
import { Inspect } from './Inspect'
|
|
@@ -11,72 +11,177 @@ export { Inspect, Notifications }
|
|
|
11
11
|
const isLocalhost =
|
|
12
12
|
window && window.location && window.location.host.includes('local')
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
// ---------------------------------------------
|
|
15
|
+
// Utility helpers to apply ops
|
|
16
|
+
|
|
17
|
+
const deletePath = (obj, path) => {
|
|
18
|
+
if (!obj || !Array.isArray(path)) return
|
|
19
|
+
path.reduce((acc, v, i, arr) => {
|
|
20
|
+
if (acc && v in acc) {
|
|
21
|
+
if (i !== arr.length - 1) return acc[v]
|
|
22
|
+
delete acc[v]
|
|
23
|
+
}
|
|
24
|
+
return undefined
|
|
25
|
+
}, obj)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const setPath = (obj, path, value, createNestedObjects = false) => {
|
|
29
|
+
if (!obj || !Array.isArray(path)) return
|
|
30
|
+
path.reduce((acc, v, i, arr) => {
|
|
31
|
+
if (!acc) return undefined
|
|
32
|
+
if (i !== arr.length - 1) {
|
|
33
|
+
if (!acc[v] && createNestedObjects) acc[v] = {}
|
|
34
|
+
return acc[v]
|
|
35
|
+
}
|
|
36
|
+
acc[v] = value
|
|
37
|
+
return undefined
|
|
38
|
+
}, obj)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const applyOpsToCtx = (ctx, changes) => {
|
|
42
|
+
const topLevelChanged = new Set()
|
|
43
|
+
if (!Array.isArray(changes)) return topLevelChanged
|
|
44
|
+
for (const [action, path, change] of changes) {
|
|
45
|
+
if (!Array.isArray(path) || !path.length) continue
|
|
46
|
+
topLevelChanged.add(path[0])
|
|
47
|
+
switch (action) {
|
|
48
|
+
case 'delete':
|
|
49
|
+
deletePath(ctx, path)
|
|
50
|
+
break
|
|
51
|
+
case 'update':
|
|
52
|
+
case 'set':
|
|
53
|
+
setPath(ctx, path, change, true)
|
|
54
|
+
break
|
|
55
|
+
default:
|
|
56
|
+
// Unsupported action – ignore
|
|
57
|
+
break
|
|
58
|
+
}
|
|
17
59
|
}
|
|
60
|
+
return topLevelChanged
|
|
18
61
|
}
|
|
19
62
|
|
|
20
|
-
|
|
21
|
-
|
|
63
|
+
// ---------------------------------------------
|
|
64
|
+
|
|
65
|
+
const fetchServiceToken = async () => {
|
|
66
|
+
try {
|
|
67
|
+
const urlBase = isLocalhost ? 'http://localhost:8080' : 'https://api.symbols.app'
|
|
68
|
+
const res = await window.fetch(`${urlBase}/service-token`, { method: 'GET' })
|
|
69
|
+
|
|
70
|
+
// Attempt to parse JSON first – recent versions return `{ token: "..." }`
|
|
71
|
+
// Fall back to treating the response as raw text for backward-compatibility.
|
|
72
|
+
let txt
|
|
73
|
+
try {
|
|
74
|
+
const json = await res.clone().json()
|
|
75
|
+
if (json && typeof json.token === 'string') return json.token.trim()
|
|
76
|
+
// If json parsing succeeds but no token field, fall back to text below.
|
|
77
|
+
txt = await res.text()
|
|
78
|
+
} catch {
|
|
79
|
+
// Response is not JSON – treat as plain text token.
|
|
80
|
+
txt = await res.text()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return (txt || '').replace(/\s+/g, '') || undefined
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.error('[sync] Failed to fetch service-token', e)
|
|
86
|
+
return undefined
|
|
87
|
+
}
|
|
22
88
|
}
|
|
23
89
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// overwriteShallow(ctx.pages, pages)
|
|
35
|
-
overwriteShallow(ctx.pages, pages)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
if (components) {
|
|
39
|
-
overwriteShallow(ctx.components, components)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (functions) {
|
|
43
|
-
overwriteShallow(ctx.functions, functions)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (snippets) {
|
|
47
|
-
overwriteShallow(ctx.snippets, snippets)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (state) {
|
|
51
|
-
const route = state.route
|
|
52
|
-
if (route) el.call('router', route.replace('/state', '') || '/', el, {})
|
|
53
|
-
else if (!(snippets && functions && components && pages))
|
|
54
|
-
s.update(state)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (snippets || functions || components || pages) {
|
|
58
|
-
const { pathname, search, hash } = ctx.window.location
|
|
59
|
-
el.call('router', pathname + search + hash, el, {})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (designSystem) init(designSystem)
|
|
90
|
+
const onSnapshot = (el, s, ctx) => (payload = {}) => {
|
|
91
|
+
const { data, schema } = payload
|
|
92
|
+
if (!data) return
|
|
93
|
+
|
|
94
|
+
// Overwrite high-level objects shallowly so references are preserved
|
|
95
|
+
Object.entries(data).forEach(([key, val]) => {
|
|
96
|
+
if (ctx[key] && typeof ctx[key] === 'object') {
|
|
97
|
+
overwriteShallow(ctx[key], val)
|
|
98
|
+
} else {
|
|
99
|
+
ctx[key] = val
|
|
63
100
|
}
|
|
101
|
+
})
|
|
64
102
|
|
|
65
|
-
|
|
66
|
-
|
|
103
|
+
// Optionally make schema available on ctx
|
|
104
|
+
if (schema) ctx.schema = schema
|
|
105
|
+
|
|
106
|
+
// Trigger routing so UI reflects latest data
|
|
107
|
+
const { pathname, search, hash } = ctx.window.location
|
|
108
|
+
;(ctx.utils?.router || router)(pathname + search + hash, el, {})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const onOps = (el, s, ctx) => (payload = {}) => {
|
|
112
|
+
console.log('onOps', payload)
|
|
113
|
+
const { changes } = payload
|
|
114
|
+
if (!changes || !Array.isArray(changes) || !changes.length) return
|
|
115
|
+
|
|
116
|
+
const changed = applyOpsToCtx(ctx, changes)
|
|
117
|
+
|
|
118
|
+
// React to specific top-level changes
|
|
119
|
+
if (changed.has('state')) {
|
|
120
|
+
const route = ctx.state?.route
|
|
121
|
+
if (route) {
|
|
122
|
+
;(ctx.utils?.router || router)(route.replace('/state', '') || '/', el, {})
|
|
123
|
+
} else {
|
|
124
|
+
s.update(ctx.state)
|
|
67
125
|
}
|
|
68
126
|
}
|
|
127
|
+
|
|
128
|
+
if (['pages', 'components', 'snippets', 'functions'].some(k => changed.has(k))) {
|
|
129
|
+
const { pathname, search, hash } = ctx.window.location
|
|
130
|
+
;(ctx.utils?.router || router)(pathname + search + hash, el, {})
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (changed.has('designSystem')) {
|
|
134
|
+
init(ctx.designSystem)
|
|
135
|
+
}
|
|
69
136
|
}
|
|
70
137
|
|
|
71
|
-
export const connectToSocket = (el, s, ctx) => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
138
|
+
export const connectToSocket = async (el, s, ctx) => {
|
|
139
|
+
const token = await fetchServiceToken()
|
|
140
|
+
if (!token) {
|
|
141
|
+
console.warn('[sync] No service token – live collaboration disabled')
|
|
142
|
+
return null
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const projectKey = ctx.key
|
|
146
|
+
if (!projectKey) {
|
|
147
|
+
console.warn('[sync] ctx.key missing – cannot establish collaborative connection')
|
|
148
|
+
return null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const socketBaseUrl = isLocalhost ? 'http://localhost:8080' : 'https://api.symbols.app'
|
|
152
|
+
|
|
153
|
+
const socket = io(socketBaseUrl, {
|
|
154
|
+
path: '/collab-socket',
|
|
155
|
+
transports: ['websocket'],
|
|
156
|
+
auth: {
|
|
157
|
+
token,
|
|
158
|
+
projectKey,
|
|
159
|
+
branch: 'main',
|
|
160
|
+
live: true,
|
|
161
|
+
clientType: 'platform'
|
|
162
|
+
},
|
|
163
|
+
reconnectionAttempts: Infinity,
|
|
164
|
+
reconnectionDelayMax: 4000
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
socket.on('connect', () => {
|
|
168
|
+
if (ctx.editor?.verbose) console.info('[sync] Connected to collab socket')
|
|
79
169
|
})
|
|
170
|
+
|
|
171
|
+
socket.on('snapshot', onSnapshot(el, s, ctx))
|
|
172
|
+
socket.on('ops', onOps(el, s, ctx))
|
|
173
|
+
|
|
174
|
+
socket.on('clients', (data) => {
|
|
175
|
+
if (ctx.editor?.verbose) {
|
|
176
|
+
connectedToSymbols(data, el, s)
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
socket.on('disconnect', (reason) => {
|
|
181
|
+
if (ctx.editor?.verbose) console.info('[sync] Disconnected from collab socket', reason)
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
return socket
|
|
80
185
|
}
|
|
81
186
|
|
|
82
187
|
export const SyncComponent = {
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/sync",
|
|
3
|
-
"version": "2.29.
|
|
3
|
+
"version": "2.29.53",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"module": "index.js",
|
|
6
|
-
"gitHead": "
|
|
6
|
+
"gitHead": "87f9120f580203116585f21335aa97cc7dbfde6d",
|
|
7
7
|
"files": [
|
|
8
8
|
"*.js",
|
|
9
9
|
"dist"
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"jsdelivr": "dist/iife/index.js",
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"import": "./index.js",
|
|
18
|
+
"require": "./dist/cjs/index.js"
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"source": "index.js",
|
|
@@ -29,12 +29,13 @@
|
|
|
29
29
|
"prepublish": "rimraf -I dist && npm run build && npm run copy:package:cjs"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@domql/router": "^2.29.
|
|
33
|
-
"@domql/utils": "^2.29.
|
|
34
|
-
"@symbo.ls/init": "^2.29.
|
|
35
|
-
"@symbo.ls/scratch": "^2.29.
|
|
36
|
-
"@symbo.ls/socket": "^2.29.
|
|
37
|
-
"@symbo.ls/uikit": "^2.29.
|
|
32
|
+
"@domql/router": "^2.29.53",
|
|
33
|
+
"@domql/utils": "^2.29.53",
|
|
34
|
+
"@symbo.ls/init": "^2.29.53",
|
|
35
|
+
"@symbo.ls/scratch": "^2.29.53",
|
|
36
|
+
"@symbo.ls/socket": "^2.29.53",
|
|
37
|
+
"@symbo.ls/uikit": "^2.29.53",
|
|
38
|
+
"socket.io-client": "^4.8.1"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"@babel/core": "^7.27.1"
|