@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
package/index.js
CHANGED
|
@@ -1,83 +1,250 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
import { router } from '@domql/router'
|
|
4
|
-
import { init } from '
|
|
5
|
-
import {
|
|
6
|
-
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'
|
|
7
5
|
import { connectedToSymbols, Notifications } from './SyncNotifications'
|
|
8
6
|
import { Inspect } from './Inspect'
|
|
9
7
|
export { Inspect, Notifications }
|
|
10
8
|
|
|
11
|
-
const
|
|
12
|
-
|
|
9
|
+
const isLocal = process.env.NODE_ENV === 'local'
|
|
10
|
+
|
|
11
|
+
// ---------------------------------------------
|
|
12
|
+
// Utility helpers to apply ops
|
|
13
13
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const deletePath = (obj, path) => {
|
|
15
|
+
if (!obj || !Array.isArray(path)) {
|
|
16
|
+
return
|
|
17
17
|
}
|
|
18
|
+
path.reduce((acc, v, i, arr) => {
|
|
19
|
+
if (acc && v in acc) {
|
|
20
|
+
if (i !== arr.length - 1) {
|
|
21
|
+
return acc[v]
|
|
22
|
+
}
|
|
23
|
+
delete acc[v]
|
|
24
|
+
}
|
|
25
|
+
return void 0
|
|
26
|
+
}, obj)
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
const
|
|
21
|
-
|
|
29
|
+
const setPath = (obj, path, value, createNestedObjects = false) => {
|
|
30
|
+
if (!obj || !Array.isArray(path)) {
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
path.reduce((acc, v, i, arr) => {
|
|
34
|
+
if (!acc) {
|
|
35
|
+
return void 0
|
|
36
|
+
}
|
|
37
|
+
if (i !== arr.length - 1) {
|
|
38
|
+
if (!acc[v] && createNestedObjects) {
|
|
39
|
+
acc[v] = {}
|
|
40
|
+
}
|
|
41
|
+
return acc[v]
|
|
42
|
+
}
|
|
43
|
+
acc[v] = value
|
|
44
|
+
return void 0
|
|
45
|
+
}, obj)
|
|
22
46
|
}
|
|
23
47
|
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
const applyOpsToCtx = (ctx, changes) => {
|
|
49
|
+
const topLevelChanged = new Set()
|
|
50
|
+
if (!Array.isArray(changes)) {
|
|
51
|
+
return topLevelChanged
|
|
52
|
+
}
|
|
53
|
+
for (const [action, path, change] of changes) {
|
|
54
|
+
if (!Array.isArray(path) || !path.length) {
|
|
55
|
+
continue
|
|
56
|
+
}
|
|
57
|
+
topLevelChanged.add(path[0])
|
|
58
|
+
switch (action) {
|
|
59
|
+
case 'delete':
|
|
60
|
+
deletePath(ctx, path)
|
|
61
|
+
break
|
|
62
|
+
case 'update':
|
|
63
|
+
case 'set':
|
|
64
|
+
setPath(ctx, path, change, true)
|
|
65
|
+
break
|
|
66
|
+
default:
|
|
67
|
+
// Unsupported action – ignore
|
|
68
|
+
break
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return topLevelChanged
|
|
72
|
+
}
|
|
37
73
|
|
|
38
|
-
|
|
39
|
-
overwriteShallow(ctx.components, components)
|
|
40
|
-
}
|
|
74
|
+
// ---------------------------------------------
|
|
41
75
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
76
|
+
const fetchServiceToken = async () => {
|
|
77
|
+
try {
|
|
78
|
+
const urlBase = isLocal
|
|
79
|
+
? 'http://localhost:8080'
|
|
80
|
+
: 'https://api.symbols.app'
|
|
81
|
+
const res = await window.fetch(`${urlBase}/service-token`, {
|
|
82
|
+
method: 'GET'
|
|
83
|
+
})
|
|
45
84
|
|
|
46
|
-
|
|
47
|
-
|
|
85
|
+
// Attempt to parse JSON first – recent versions return `{ token: "..." }`
|
|
86
|
+
// Fall back to treating the response as raw text for backward-compatibility.
|
|
87
|
+
let txt
|
|
88
|
+
try {
|
|
89
|
+
const json = await res.clone().json()
|
|
90
|
+
if (json && typeof json.token === 'string') {
|
|
91
|
+
return json.token.trim()
|
|
48
92
|
}
|
|
93
|
+
// If json parsing succeeds but no token field, fall back to text below.
|
|
94
|
+
txt = await res.text()
|
|
95
|
+
} catch {
|
|
96
|
+
// Response is not JSON – treat as plain text token.
|
|
97
|
+
txt = await res.text()
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (txt || '').replace(/\s+/gu, '') || void 0
|
|
101
|
+
} catch (e) {
|
|
102
|
+
console.error('[sync] Failed to fetch service-token', e)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
49
105
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
106
|
+
const onSnapshot =
|
|
107
|
+
(el, s, ctx) =>
|
|
108
|
+
(payload = {}) => {
|
|
109
|
+
let { data } = payload
|
|
110
|
+
const { schema } = payload
|
|
111
|
+
if (!data) {
|
|
112
|
+
return
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
data = el.call(
|
|
116
|
+
'deepDestringifyFunctions',
|
|
117
|
+
data,
|
|
118
|
+
Array.isArray(data) ? [] : {}
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
// Overwrite high-level objects shallowly so references are preserved
|
|
122
|
+
Object.entries(data).forEach(([key, val]) => {
|
|
123
|
+
if (ctx[key] && typeof ctx[key] === 'object') {
|
|
124
|
+
if (key === 'designSystem') {
|
|
125
|
+
init(val)
|
|
126
|
+
} else {
|
|
127
|
+
overwriteShallow(ctx[key], val)
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
ctx[key] = val
|
|
56
131
|
}
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Optionally make schema available on ctx
|
|
135
|
+
if (schema) {
|
|
136
|
+
ctx.schema = schema
|
|
137
|
+
}
|
|
138
|
+
}
|
|
57
139
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
140
|
+
const onOps =
|
|
141
|
+
(el, s, ctx) =>
|
|
142
|
+
(payload = {}) => {
|
|
143
|
+
let { changes } = payload
|
|
144
|
+
if (!changes || !Array.isArray(changes) || !changes.length) {
|
|
145
|
+
return
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
changes = el.call(
|
|
149
|
+
'deepDestringifyFunctions',
|
|
150
|
+
changes,
|
|
151
|
+
Array.isArray(changes) ? [] : {}
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
const changed = applyOpsToCtx(ctx, changes)
|
|
156
|
+
|
|
157
|
+
// React to specific top-level changes
|
|
158
|
+
if (changed.has('state')) {
|
|
159
|
+
const route = ctx.state?.route
|
|
160
|
+
if (route) {
|
|
161
|
+
el.call(
|
|
162
|
+
'router',
|
|
163
|
+
route.replace('/state', '') || '/',
|
|
164
|
+
el.__ref.root,
|
|
165
|
+
{},
|
|
166
|
+
{ scrollToTop: false }
|
|
167
|
+
)
|
|
168
|
+
} else {
|
|
169
|
+
s.update(ctx.state)
|
|
61
170
|
}
|
|
171
|
+
}
|
|
62
172
|
|
|
63
|
-
|
|
173
|
+
if (
|
|
174
|
+
['pages', 'components', 'snippets', 'functions'].some((k) =>
|
|
175
|
+
changed.has(k)
|
|
176
|
+
)
|
|
177
|
+
) {
|
|
178
|
+
const { pathname, search, hash } = ctx.window.location
|
|
179
|
+
el.call(
|
|
180
|
+
'router',
|
|
181
|
+
pathname + search + hash,
|
|
182
|
+
el.__ref.root,
|
|
183
|
+
{},
|
|
184
|
+
{ scrollToTop: false }
|
|
185
|
+
)
|
|
64
186
|
}
|
|
65
187
|
|
|
66
|
-
if (
|
|
67
|
-
|
|
188
|
+
if (changed.has('designSystem')) {
|
|
189
|
+
init(ctx.designSystem)
|
|
68
190
|
}
|
|
69
191
|
}
|
|
70
|
-
}
|
|
71
192
|
|
|
72
|
-
export const connectToSocket = (el, s, ctx) => {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
193
|
+
export const connectToSocket = async (el, s, ctx) => {
|
|
194
|
+
const token = await fetchServiceToken()
|
|
195
|
+
if (!token) {
|
|
196
|
+
console.warn('[sync] No service token – live collaboration disabled')
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const projectKey = ctx.key
|
|
201
|
+
if (!projectKey) {
|
|
202
|
+
console.warn(
|
|
203
|
+
'[sync] ctx.key missing – cannot establish collaborative connection'
|
|
204
|
+
)
|
|
205
|
+
return null
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const socketBaseUrl = isLocal
|
|
209
|
+
? 'http://localhost:8080'
|
|
210
|
+
: 'https://api.symbols.app'
|
|
211
|
+
|
|
212
|
+
const socket = io(socketBaseUrl, {
|
|
213
|
+
path: '/collab-socket',
|
|
214
|
+
transports: ['websocket'],
|
|
215
|
+
auth: {
|
|
216
|
+
token,
|
|
217
|
+
projectKey,
|
|
218
|
+
branch: 'main',
|
|
219
|
+
live: true,
|
|
220
|
+
clientType: 'platform'
|
|
221
|
+
},
|
|
222
|
+
reconnectionAttempts: Infinity,
|
|
223
|
+
reconnectionDelayMax: 4000
|
|
80
224
|
})
|
|
225
|
+
|
|
226
|
+
socket.on('connect', () => {
|
|
227
|
+
if (ctx.editor?.verbose) {
|
|
228
|
+
console.info('[sync] Connected to collab socket')
|
|
229
|
+
}
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
socket.on('snapshot', onSnapshot(el, s, ctx))
|
|
233
|
+
socket.on('ops', onOps(el, s, ctx))
|
|
234
|
+
|
|
235
|
+
socket.on('clients', (data) => {
|
|
236
|
+
if (ctx.editor?.verbose) {
|
|
237
|
+
connectedToSymbols(data, el, s)
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
socket.on('disconnect', (reason) => {
|
|
242
|
+
if (ctx.editor?.verbose) {
|
|
243
|
+
console.info('[sync] Disconnected from collab socket', reason)
|
|
244
|
+
}
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
return socket
|
|
81
248
|
}
|
|
82
249
|
|
|
83
250
|
export const SyncComponent = {
|
package/package.json
CHANGED
|
@@ -1,42 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/sync",
|
|
3
|
-
"version": "3.2.
|
|
4
|
-
"main": "index.js",
|
|
5
|
-
"module": "index.js",
|
|
3
|
+
"version": "3.2.8",
|
|
4
|
+
"main": "./index.js",
|
|
5
|
+
"module": "./index.js",
|
|
6
6
|
"gitHead": "9fc1b79b41cdc725ca6b24aec64920a599634681",
|
|
7
7
|
"files": [
|
|
8
|
-
"
|
|
9
|
-
"
|
|
8
|
+
"dist",
|
|
9
|
+
"*.js"
|
|
10
10
|
],
|
|
11
11
|
"repository": "https://github.com/symbo-ls/scratch",
|
|
12
12
|
"type": "module",
|
|
13
|
-
"unpkg": "dist/iife/index.js",
|
|
14
|
-
"jsdelivr": "dist/iife/index.js",
|
|
13
|
+
"unpkg": "./dist/iife/index.js",
|
|
14
|
+
"jsdelivr": "./dist/iife/index.js",
|
|
15
15
|
"exports": {
|
|
16
16
|
".": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
17
|
+
"import": "./index.js",
|
|
18
|
+
"require": "./index.js",
|
|
19
|
+
"browser": "./index.js",
|
|
20
|
+
"default": "./index.js"
|
|
21
|
+
},
|
|
22
|
+
"./client": {
|
|
23
|
+
"import": "./client.js",
|
|
24
|
+
"require": "./client.js",
|
|
25
|
+
"default": "./client.js"
|
|
26
|
+
},
|
|
27
|
+
"./server": {
|
|
28
|
+
"import": "./server.js",
|
|
29
|
+
"default": "./server.js"
|
|
30
|
+
},
|
|
31
|
+
"./*.js": {
|
|
32
|
+
"import": "./dist/esm/*.js",
|
|
33
|
+
"require": "./dist/cjs/*.js",
|
|
34
|
+
"default": "./dist/esm/*.js"
|
|
35
|
+
},
|
|
36
|
+
"./*": {
|
|
37
|
+
"import": "./dist/esm/*.js",
|
|
38
|
+
"require": "./dist/cjs/*.js",
|
|
39
|
+
"default": "./dist/esm/*.js"
|
|
19
40
|
}
|
|
20
41
|
},
|
|
21
42
|
"source": "index.js",
|
|
22
43
|
"publishConfig": {},
|
|
23
44
|
"scripts": {
|
|
24
45
|
"copy:package:cjs": "cp ../../build/package-cjs.json dist/cjs/package.json",
|
|
25
|
-
"build:esm": "
|
|
26
|
-
"build:cjs": "
|
|
27
|
-
"build:iife": "
|
|
28
|
-
"build": "
|
|
29
|
-
"prepublish": "
|
|
46
|
+
"build:esm": "cross-env NODE_ENV=$NODE_ENV esbuild *.js --target=es2020 --format=esm --outdir=dist/esm --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
47
|
+
"build:cjs": "cross-env NODE_ENV=$NODE_ENV esbuild *.js --target=node18 --format=cjs --outdir=dist/cjs --define:process.env.NODE_ENV=process.env.NODE_ENV",
|
|
48
|
+
"build:iife": "cross-env NODE_ENV=$NODE_ENV esbuild index.js --bundle --target=es2020 --format=iife --global-name=SmblsSync --outfile=dist/iife/index.js --define:process.env.NODE_ENV=process.env.NODE_ENV --external:smbls --external:@domql/* --external:@symbo.ls/* --external:socket.io-client --external:socket.io --external:express --external:css-in-props",
|
|
49
|
+
"build": "node ../../build/build.js",
|
|
50
|
+
"prepublish": "npm run build && npm run copy:package:cjs"
|
|
30
51
|
},
|
|
31
52
|
"dependencies": {
|
|
32
53
|
"@domql/router": "^3.2.3",
|
|
33
54
|
"@domql/utils": "^3.2.3",
|
|
34
|
-
"@symbo.ls/init": "^3.2.3",
|
|
35
55
|
"@symbo.ls/scratch": "^3.2.3",
|
|
36
|
-
"@symbo.ls/
|
|
37
|
-
"
|
|
56
|
+
"@symbo.ls/uikit": "^3.2.3",
|
|
57
|
+
"chalk": "^5.4.1",
|
|
58
|
+
"express": "^4.21.2",
|
|
59
|
+
"http": "^0.0.1-security",
|
|
60
|
+
"socket.io": "^4.8.1",
|
|
61
|
+
"socket.io-client": "^4.8.1"
|
|
38
62
|
},
|
|
39
63
|
"devDependencies": {
|
|
40
64
|
"@babel/core": "^7.26.0"
|
|
41
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"browser": "./dist/iife/index.js",
|
|
67
|
+
"sideEffects": false
|
|
42
68
|
}
|
package/server.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import express from 'express'
|
|
6
|
+
import http from 'http'
|
|
7
|
+
import { Server } from 'socket.io'
|
|
8
|
+
import { createRequire } from 'module'
|
|
9
|
+
import * as utils from '@domql/utils'
|
|
10
|
+
const { overwriteDeep } = utils.default || utils
|
|
11
|
+
|
|
12
|
+
const require = createRequire(import.meta.url) // construct the require method
|
|
13
|
+
const DES_SYS_DEFAULT_FILE = require('./dynamic.json') // Bring in the ability to create the 'require' method
|
|
14
|
+
|
|
15
|
+
const app = express()
|
|
16
|
+
let io
|
|
17
|
+
|
|
18
|
+
const debugMsg = chalk.dim(
|
|
19
|
+
'Use --verbose to debug the error or open the issue at https://github.com/symbo-ls/smbls'
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export const updateDynamycFile = (changes, options = {}) => {
|
|
23
|
+
const { verbose, prettify, verboseCode } = options
|
|
24
|
+
const file = require('./dynamic.json')
|
|
25
|
+
|
|
26
|
+
const newMerge = overwriteDeep(file, changes)
|
|
27
|
+
const mergeStr = JSON.stringify(newMerge, null, 2)
|
|
28
|
+
const initPath = `${process.cwd()}/node_modules/@symbo.ls/init/dynamic.json`
|
|
29
|
+
|
|
30
|
+
console.log(chalk.dim('\n----------------\n'))
|
|
31
|
+
|
|
32
|
+
console.log(chalk.dim('Received update:'))
|
|
33
|
+
console.log(Object.keys(changes).join(', '))
|
|
34
|
+
if (verboseCode)
|
|
35
|
+
console.log(chalk.dim(JSON.stringify(changes, null, prettify ?? 2)))
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
fs.writeFileSync(initPath, mergeStr)
|
|
39
|
+
if (verbose) {
|
|
40
|
+
console.log(chalk.bold.green('\nChanges wrote to the file'))
|
|
41
|
+
}
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.log('')
|
|
44
|
+
console.log(chalk.bold.red('Error writing file'))
|
|
45
|
+
if (verbose) {
|
|
46
|
+
console.error(e)
|
|
47
|
+
} else {
|
|
48
|
+
console.log(debugMsg)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line no-unused-vars
|
|
54
|
+
export const sync = (desSysFile = DES_SYS_DEFAULT_FILE, opts = {}) => {
|
|
55
|
+
const server = http.createServer(app)
|
|
56
|
+
const { key } = opts
|
|
57
|
+
|
|
58
|
+
io = new Server(server, {
|
|
59
|
+
transports: ['websocket', 'polling', 'flashsocket'],
|
|
60
|
+
cors: {
|
|
61
|
+
origin: '*'
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
app.get('/', (req, res) => {
|
|
66
|
+
res.end('open')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
io.on('connection', socket => {
|
|
70
|
+
socket.join(key)
|
|
71
|
+
let source
|
|
72
|
+
|
|
73
|
+
socket.on('initConnect', options => {
|
|
74
|
+
const { clientsCount } = io.engine
|
|
75
|
+
socket.to(key).emit('clientsCount', clientsCount)
|
|
76
|
+
source = options.source
|
|
77
|
+
console.log('Connected', key, source)
|
|
78
|
+
console.log('from', options.location)
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
socket.on('components', (data, options) => {
|
|
82
|
+
io.to(key).emit('change', data, options)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
socket.on('route', (data, options) => {
|
|
86
|
+
io.to(key).emit('route', data, options)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
socket.on('change', updateDynamycFile)
|
|
90
|
+
|
|
91
|
+
// eslint-disable-next-line no-unused-vars
|
|
92
|
+
socket.on('disconnect', (changes, options) => {
|
|
93
|
+
const { clientsCount } = io.engine
|
|
94
|
+
socket.to(key).emit('clientsCount', clientsCount)
|
|
95
|
+
console.log('Disconnected', key, source)
|
|
96
|
+
})
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
server.listen(13336, () => {
|
|
100
|
+
console.log('listening on *:13336')
|
|
101
|
+
})
|
|
102
|
+
}
|
package/dist/cjs/package.json
DELETED