@symbo.ls/sync 3.2.3 → 3.2.8
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/Inspect.js +1 -1
- package/client.js +131 -0
- package/dist/cjs/Inspect.js +1 -1
- package/dist/cjs/client.js +130 -0
- package/dist/cjs/index.js +184 -50
- package/dist/cjs/server.js +115 -0
- package/dist/esm/Inspect.js +2 -17
- package/dist/esm/client.js +101 -0
- package/dist/esm/index.js +185 -50
- package/dist/esm/server.js +85 -0
- package/dist/iife/index.js +495 -0
- package/index.js +221 -54
- package/package.json +44 -18
- package/server.js +102 -0
- package/dist/cjs/package.json +0 -4
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as utils from "@domql/utils";
|
|
2
|
+
import io from "socket.io-client";
|
|
3
|
+
const { window, isFunction, isArray } = utils.default || utils;
|
|
4
|
+
const defautlOpts = {};
|
|
5
|
+
let CONNECT_ATTEPT = 0;
|
|
6
|
+
const CONNECT_ATTEPT_MAX_ALLOWED = 1;
|
|
7
|
+
const getIsDev = (options) => {
|
|
8
|
+
return options.development || window && window.location && window.location.host.includes("local") || ENV === "testing" || ENV === "development";
|
|
9
|
+
};
|
|
10
|
+
const getSocketUrl = (options, isDev) => {
|
|
11
|
+
const SOCKET_BACKEND_URL = isDev ? "http://localhost:8080/" : "https://api.symbols.app/";
|
|
12
|
+
const socketUrls = isArray(options.socketUrl) ? options.socketUrl : [options.socketUrl || SOCKET_BACKEND_URL];
|
|
13
|
+
const primaryUrl = socketUrls[0];
|
|
14
|
+
const secondaryUrl = socketUrls[1] || "api.symbols.app";
|
|
15
|
+
return {
|
|
16
|
+
primaryUrl: primaryUrl || SOCKET_BACKEND_URL,
|
|
17
|
+
secondaryUrl
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
const connect = (key, options = {}) => {
|
|
21
|
+
const isDev = getIsDev(options);
|
|
22
|
+
const { primaryUrl, secondaryUrl } = getSocketUrl(options, isDev);
|
|
23
|
+
const socket = io(primaryUrl || secondaryUrl, {
|
|
24
|
+
// withCredentials: true
|
|
25
|
+
});
|
|
26
|
+
socket.on("connect", () => {
|
|
27
|
+
if (isDev) {
|
|
28
|
+
console.warn(
|
|
29
|
+
`Connected to %c${primaryUrl} %c${key} %c${socket.id}`,
|
|
30
|
+
"font-weight: bold; color: green;",
|
|
31
|
+
"font-weight: bold;",
|
|
32
|
+
""
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
socket.emit("initConnect", { key, ...options });
|
|
36
|
+
try {
|
|
37
|
+
if (isFunction(options.onConnect)) {
|
|
38
|
+
options.onConnect(socket.id, socket);
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
console.error(e);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
socket.on("connect_error", (err) => {
|
|
45
|
+
console.log(`event: connect_error | reason: ${err.message}`);
|
|
46
|
+
try {
|
|
47
|
+
if (isFunction(options.onError)) {
|
|
48
|
+
options.onError(err, socket);
|
|
49
|
+
}
|
|
50
|
+
if (CONNECT_ATTEPT < CONNECT_ATTEPT_MAX_ALLOWED) {
|
|
51
|
+
CONNECT_ATTEPT++;
|
|
52
|
+
socket.disconnect();
|
|
53
|
+
if (utils.isNotProduction()) {
|
|
54
|
+
console.log(
|
|
55
|
+
`Could not connect to %c${primaryUrl}%c, reconnecting to %c${secondaryUrl}`,
|
|
56
|
+
"font-weight: bold; color: red;",
|
|
57
|
+
"",
|
|
58
|
+
"font-weight: bold; color: green;"
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
connect(key, { ...options, socketUrl: secondaryUrl });
|
|
62
|
+
}
|
|
63
|
+
} catch (e) {
|
|
64
|
+
console.error(e);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
socket.on("disconnect", (reason) => {
|
|
68
|
+
console.log(`event: disconnect | reason: ${reason}`);
|
|
69
|
+
try {
|
|
70
|
+
if (isFunction(options.onDisconnect)) {
|
|
71
|
+
options.onDisconnect(reason, socket);
|
|
72
|
+
}
|
|
73
|
+
} catch (e) {
|
|
74
|
+
console.error(e);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
socket.onAny((event, ...args) => {
|
|
78
|
+
if (event === "connect") {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
if (isFunction(options.onChange)) {
|
|
83
|
+
options.onChange(event, args[0], socket);
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error(e);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
return socket;
|
|
90
|
+
};
|
|
91
|
+
function send(event = "change", changes, options) {
|
|
92
|
+
this.emit(event, changes, { ...options, ...defautlOpts });
|
|
93
|
+
}
|
|
94
|
+
function disconnect() {
|
|
95
|
+
this.disconnect();
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
connect,
|
|
99
|
+
disconnect,
|
|
100
|
+
send
|
|
101
|
+
};
|
package/dist/esm/index.js
CHANGED
|
@@ -1,64 +1,199 @@
|
|
|
1
1
|
import { router } from "@domql/router";
|
|
2
|
-
import { init } from "
|
|
3
|
-
import {
|
|
4
|
-
import { window, overwriteShallow } from "@domql/utils";
|
|
2
|
+
import { init } from "smbls";
|
|
3
|
+
import { io } from "socket.io-client";
|
|
4
|
+
import { window, overwriteShallow, overwriteDeep } from "@domql/utils";
|
|
5
5
|
import { connectedToSymbols, Notifications } from "./SyncNotifications";
|
|
6
6
|
import { Inspect } from "./Inspect";
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const onChange = (el, s, ctx) => {
|
|
17
|
-
return (event, data) => {
|
|
18
|
-
if (event === "change") {
|
|
19
|
-
const obj = JSON.parse(data);
|
|
20
|
-
if (!(obj == null ? void 0 : obj.DATA)) return;
|
|
21
|
-
const { state, designSystem, pages, components, snippets, functions } = obj.DATA;
|
|
22
|
-
const { utils } = ctx;
|
|
23
|
-
if (pages) {
|
|
24
|
-
overwriteShallow(ctx.pages, pages);
|
|
25
|
-
}
|
|
26
|
-
if (components) {
|
|
27
|
-
overwriteShallow(ctx.components, components);
|
|
7
|
+
const isLocal = process.env.NODE_ENV === "local";
|
|
8
|
+
const deletePath = (obj, path) => {
|
|
9
|
+
if (!obj || !Array.isArray(path)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
path.reduce((acc, v, i, arr) => {
|
|
13
|
+
if (acc && v in acc) {
|
|
14
|
+
if (i !== arr.length - 1) {
|
|
15
|
+
return acc[v];
|
|
28
16
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
17
|
+
delete acc[v];
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}, obj);
|
|
21
|
+
};
|
|
22
|
+
const setPath = (obj, path, value, createNestedObjects = false) => {
|
|
23
|
+
if (!obj || !Array.isArray(path)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
path.reduce((acc, v, i, arr) => {
|
|
27
|
+
if (!acc) {
|
|
28
|
+
return void 0;
|
|
29
|
+
}
|
|
30
|
+
if (i !== arr.length - 1) {
|
|
31
|
+
if (!acc[v] && createNestedObjects) {
|
|
32
|
+
acc[v] = {};
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
34
|
+
return acc[v];
|
|
35
|
+
}
|
|
36
|
+
acc[v] = value;
|
|
37
|
+
return void 0;
|
|
38
|
+
}, obj);
|
|
39
|
+
};
|
|
40
|
+
const applyOpsToCtx = (ctx, changes) => {
|
|
41
|
+
const topLevelChanged = /* @__PURE__ */ new Set();
|
|
42
|
+
if (!Array.isArray(changes)) {
|
|
43
|
+
return topLevelChanged;
|
|
44
|
+
}
|
|
45
|
+
for (const [action, path, change] of changes) {
|
|
46
|
+
if (!Array.isArray(path) || !path.length) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
topLevelChanged.add(path[0]);
|
|
50
|
+
switch (action) {
|
|
51
|
+
case "delete":
|
|
52
|
+
deletePath(ctx, path);
|
|
53
|
+
break;
|
|
54
|
+
case "update":
|
|
55
|
+
case "set":
|
|
56
|
+
setPath(ctx, path, change, true);
|
|
57
|
+
break;
|
|
58
|
+
default:
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return topLevelChanged;
|
|
63
|
+
};
|
|
64
|
+
const fetchServiceToken = async () => {
|
|
65
|
+
try {
|
|
66
|
+
const urlBase = isLocal ? "http://localhost:8080" : "https://api.symbols.app";
|
|
67
|
+
const res = await window.fetch(`${urlBase}/service-token`, {
|
|
68
|
+
method: "GET"
|
|
69
|
+
});
|
|
70
|
+
let txt;
|
|
71
|
+
try {
|
|
72
|
+
const json = await res.clone().json();
|
|
73
|
+
if (json && typeof json.token === "string") {
|
|
74
|
+
return json.token.trim();
|
|
41
75
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
76
|
+
txt = await res.text();
|
|
77
|
+
} catch {
|
|
78
|
+
txt = await res.text();
|
|
79
|
+
}
|
|
80
|
+
return (txt || "").replace(/\s+/gu, "") || void 0;
|
|
81
|
+
} catch (e) {
|
|
82
|
+
console.error("[sync] Failed to fetch service-token", e);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const onSnapshot = (el, s, ctx) => (payload = {}) => {
|
|
86
|
+
let { data } = payload;
|
|
87
|
+
const { schema } = payload;
|
|
88
|
+
if (!data) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
data = el.call(
|
|
92
|
+
"deepDestringifyFunctions",
|
|
93
|
+
data,
|
|
94
|
+
Array.isArray(data) ? [] : {}
|
|
95
|
+
);
|
|
96
|
+
Object.entries(data).forEach(([key, val]) => {
|
|
97
|
+
if (ctx[key] && typeof ctx[key] === "object") {
|
|
98
|
+
if (key === "designSystem") {
|
|
99
|
+
init(val);
|
|
100
|
+
} else {
|
|
101
|
+
overwriteShallow(ctx[key], val);
|
|
45
102
|
}
|
|
46
|
-
|
|
103
|
+
} else {
|
|
104
|
+
ctx[key] = val;
|
|
47
105
|
}
|
|
48
|
-
|
|
49
|
-
|
|
106
|
+
});
|
|
107
|
+
if (schema) {
|
|
108
|
+
ctx.schema = schema;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const onOps = (el, s, ctx) => (payload = {}) => {
|
|
112
|
+
let { changes } = payload;
|
|
113
|
+
if (!changes || !Array.isArray(changes) || !changes.length) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
changes = el.call(
|
|
117
|
+
"deepDestringifyFunctions",
|
|
118
|
+
changes,
|
|
119
|
+
Array.isArray(changes) ? [] : {}
|
|
120
|
+
);
|
|
121
|
+
const changed = applyOpsToCtx(ctx, changes);
|
|
122
|
+
if (changed.has("state")) {
|
|
123
|
+
const route = ctx.state?.route;
|
|
124
|
+
if (route) {
|
|
125
|
+
el.call(
|
|
126
|
+
"router",
|
|
127
|
+
route.replace("/state", "") || "/",
|
|
128
|
+
el.__ref.root,
|
|
129
|
+
{},
|
|
130
|
+
{ scrollToTop: false }
|
|
131
|
+
);
|
|
132
|
+
} else {
|
|
133
|
+
s.update(ctx.state);
|
|
50
134
|
}
|
|
51
|
-
}
|
|
135
|
+
}
|
|
136
|
+
if (["pages", "components", "snippets", "functions"].some(
|
|
137
|
+
(k) => changed.has(k)
|
|
138
|
+
)) {
|
|
139
|
+
const { pathname, search, hash } = ctx.window.location;
|
|
140
|
+
el.call(
|
|
141
|
+
"router",
|
|
142
|
+
pathname + search + hash,
|
|
143
|
+
el.__ref.root,
|
|
144
|
+
{},
|
|
145
|
+
{ scrollToTop: false }
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
if (changed.has("designSystem")) {
|
|
149
|
+
init(ctx.designSystem);
|
|
150
|
+
}
|
|
52
151
|
};
|
|
53
|
-
const connectToSocket = (el, s, ctx) => {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
152
|
+
const connectToSocket = async (el, s, ctx) => {
|
|
153
|
+
const token = await fetchServiceToken();
|
|
154
|
+
if (!token) {
|
|
155
|
+
console.warn("[sync] No service token \u2013 live collaboration disabled");
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
const projectKey = ctx.key;
|
|
159
|
+
if (!projectKey) {
|
|
160
|
+
console.warn(
|
|
161
|
+
"[sync] ctx.key missing \u2013 cannot establish collaborative connection"
|
|
162
|
+
);
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const socketBaseUrl = isLocal ? "http://localhost:8080" : "https://api.symbols.app";
|
|
166
|
+
const socket = io(socketBaseUrl, {
|
|
167
|
+
path: "/collab-socket",
|
|
168
|
+
transports: ["websocket"],
|
|
169
|
+
auth: {
|
|
170
|
+
token,
|
|
171
|
+
projectKey,
|
|
172
|
+
branch: "main",
|
|
173
|
+
live: true,
|
|
174
|
+
clientType: "platform"
|
|
175
|
+
},
|
|
176
|
+
reconnectionAttempts: Infinity,
|
|
177
|
+
reconnectionDelayMax: 4e3
|
|
178
|
+
});
|
|
179
|
+
socket.on("connect", () => {
|
|
180
|
+
if (ctx.editor?.verbose) {
|
|
181
|
+
console.info("[sync] Connected to collab socket");
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
socket.on("snapshot", onSnapshot(el, s, ctx));
|
|
185
|
+
socket.on("ops", onOps(el, s, ctx));
|
|
186
|
+
socket.on("clients", (data) => {
|
|
187
|
+
if (ctx.editor?.verbose) {
|
|
188
|
+
connectedToSymbols(data, el, s);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
socket.on("disconnect", (reason) => {
|
|
192
|
+
if (ctx.editor?.verbose) {
|
|
193
|
+
console.info("[sync] Disconnected from collab socket", reason);
|
|
194
|
+
}
|
|
61
195
|
});
|
|
196
|
+
return socket;
|
|
62
197
|
};
|
|
63
198
|
const SyncComponent = {
|
|
64
199
|
onInitSync: connectToSocket
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import express from "express";
|
|
5
|
+
import http from "http";
|
|
6
|
+
import { Server } from "socket.io";
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import * as utils from "@domql/utils";
|
|
9
|
+
const { overwriteDeep } = utils.default || utils;
|
|
10
|
+
const require2 = createRequire(import.meta.url);
|
|
11
|
+
const DES_SYS_DEFAULT_FILE = require2("./dynamic.json");
|
|
12
|
+
const app = express();
|
|
13
|
+
let io;
|
|
14
|
+
const debugMsg = chalk.dim(
|
|
15
|
+
"Use --verbose to debug the error or open the issue at https://github.com/symbo-ls/smbls"
|
|
16
|
+
);
|
|
17
|
+
const updateDynamycFile = (changes, options = {}) => {
|
|
18
|
+
const { verbose, prettify, verboseCode } = options;
|
|
19
|
+
const file = require2("./dynamic.json");
|
|
20
|
+
const newMerge = overwriteDeep(file, changes);
|
|
21
|
+
const mergeStr = JSON.stringify(newMerge, null, 2);
|
|
22
|
+
const initPath = `${process.cwd()}/node_modules/@symbo.ls/init/dynamic.json`;
|
|
23
|
+
console.log(chalk.dim("\n----------------\n"));
|
|
24
|
+
console.log(chalk.dim("Received update:"));
|
|
25
|
+
console.log(Object.keys(changes).join(", "));
|
|
26
|
+
if (verboseCode)
|
|
27
|
+
console.log(chalk.dim(JSON.stringify(changes, null, prettify ?? 2)));
|
|
28
|
+
try {
|
|
29
|
+
fs.writeFileSync(initPath, mergeStr);
|
|
30
|
+
if (verbose) {
|
|
31
|
+
console.log(chalk.bold.green("\nChanges wrote to the file"));
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.log("");
|
|
35
|
+
console.log(chalk.bold.red("Error writing file"));
|
|
36
|
+
if (verbose) {
|
|
37
|
+
console.error(e);
|
|
38
|
+
} else {
|
|
39
|
+
console.log(debugMsg);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const sync = (desSysFile = DES_SYS_DEFAULT_FILE, opts = {}) => {
|
|
44
|
+
const server = http.createServer(app);
|
|
45
|
+
const { key } = opts;
|
|
46
|
+
io = new Server(server, {
|
|
47
|
+
transports: ["websocket", "polling", "flashsocket"],
|
|
48
|
+
cors: {
|
|
49
|
+
origin: "*"
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
app.get("/", (req, res) => {
|
|
53
|
+
res.end("open");
|
|
54
|
+
});
|
|
55
|
+
io.on("connection", (socket) => {
|
|
56
|
+
socket.join(key);
|
|
57
|
+
let source;
|
|
58
|
+
socket.on("initConnect", (options) => {
|
|
59
|
+
const { clientsCount } = io.engine;
|
|
60
|
+
socket.to(key).emit("clientsCount", clientsCount);
|
|
61
|
+
source = options.source;
|
|
62
|
+
console.log("Connected", key, source);
|
|
63
|
+
console.log("from", options.location);
|
|
64
|
+
});
|
|
65
|
+
socket.on("components", (data, options) => {
|
|
66
|
+
io.to(key).emit("change", data, options);
|
|
67
|
+
});
|
|
68
|
+
socket.on("route", (data, options) => {
|
|
69
|
+
io.to(key).emit("route", data, options);
|
|
70
|
+
});
|
|
71
|
+
socket.on("change", updateDynamycFile);
|
|
72
|
+
socket.on("disconnect", (changes, options) => {
|
|
73
|
+
const { clientsCount } = io.engine;
|
|
74
|
+
socket.to(key).emit("clientsCount", clientsCount);
|
|
75
|
+
console.log("Disconnected", key, source);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
server.listen(13336, () => {
|
|
79
|
+
console.log("listening on *:13336");
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
export {
|
|
83
|
+
sync,
|
|
84
|
+
updateDynamycFile
|
|
85
|
+
};
|