@symbo.ls/sdk 2.32.22 → 2.32.26
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/services/AuthService.js +6 -0
- package/dist/cjs/services/BaseService.js +1 -1
- package/dist/cjs/services/CollabService.js +24 -6
- package/dist/cjs/utils/changePreprocessor.js +58 -2
- package/dist/cjs/utils/services.js +1 -0
- package/dist/esm/index.js +90 -9
- package/dist/esm/services/AdminService.js +1 -1
- package/dist/esm/services/AuthService.js +7 -1
- package/dist/esm/services/BaseService.js +1 -1
- package/dist/esm/services/BranchService.js +1 -1
- package/dist/esm/services/CollabService.js +83 -9
- package/dist/esm/services/DnsService.js +1 -1
- package/dist/esm/services/FileService.js +1 -1
- package/dist/esm/services/PaymentService.js +1 -1
- package/dist/esm/services/PlanService.js +1 -1
- package/dist/esm/services/ProjectService.js +59 -3
- package/dist/esm/services/PullRequestService.js +1 -1
- package/dist/esm/services/ScreenshotService.js +1 -1
- package/dist/esm/services/SubscriptionService.js +1 -1
- package/dist/esm/services/TrackingService.js +1 -1
- package/dist/esm/services/index.js +89 -9
- package/dist/esm/utils/changePreprocessor.js +58 -2
- package/dist/esm/utils/services.js +1 -0
- package/dist/node/services/AuthService.js +6 -0
- package/dist/node/services/BaseService.js +1 -1
- package/dist/node/services/CollabService.js +24 -6
- package/dist/node/utils/changePreprocessor.js +58 -2
- package/dist/node/utils/services.js +1 -0
- package/package.json +6 -6
- package/src/services/AuthService.js +7 -0
- package/src/services/BaseService.js +1 -1
- package/src/services/CollabService.js +27 -6
- package/src/utils/changePreprocessor.js +76 -2
- package/src/utils/services.js +1 -0
|
@@ -17588,7 +17588,7 @@ var BaseService = class {
|
|
|
17588
17588
|
}
|
|
17589
17589
|
}
|
|
17590
17590
|
_requireAuth() {
|
|
17591
|
-
if (!this.
|
|
17591
|
+
if (!this.getAuthToken()) {
|
|
17592
17592
|
throw new Error("Authentication required");
|
|
17593
17593
|
}
|
|
17594
17594
|
}
|
|
@@ -31640,7 +31640,7 @@ var BaseService = class {
|
|
|
31640
31640
|
}
|
|
31641
31641
|
}
|
|
31642
31642
|
_requireAuth() {
|
|
31643
|
-
if (!this.
|
|
31643
|
+
if (!this.getAuthToken()) {
|
|
31644
31644
|
throw new Error("Authentication required");
|
|
31645
31645
|
}
|
|
31646
31646
|
}
|
|
@@ -32190,6 +32190,12 @@ var AuthService = class extends BaseService {
|
|
|
32190
32190
|
throw new Error(`Failed to get user profile: ${error.message}`, { cause: error });
|
|
32191
32191
|
}
|
|
32192
32192
|
}
|
|
32193
|
+
getAuthToken() {
|
|
32194
|
+
if (!this._tokenManager) {
|
|
32195
|
+
return null;
|
|
32196
|
+
}
|
|
32197
|
+
return this._tokenManager.getAccessToken();
|
|
32198
|
+
}
|
|
32193
32199
|
/**
|
|
32194
32200
|
* Get stored authentication state (backward compatibility method)
|
|
32195
32201
|
* Replaces AuthService.getStoredAuthState()
|
|
@@ -42896,6 +42902,48 @@ function getByPathSafe(root, path) {
|
|
|
42896
42902
|
return null;
|
|
42897
42903
|
}
|
|
42898
42904
|
}
|
|
42905
|
+
function resolveNextValueFromTuples(tuples, path) {
|
|
42906
|
+
if (!Array.isArray(tuples) || !Array.isArray(path)) {
|
|
42907
|
+
return null;
|
|
42908
|
+
}
|
|
42909
|
+
for (let i3 = tuples.length - 1; i3 >= 0; i3--) {
|
|
42910
|
+
const t4 = tuples[i3];
|
|
42911
|
+
if (!Array.isArray(t4) || t4.length < 3) {
|
|
42912
|
+
continue;
|
|
42913
|
+
}
|
|
42914
|
+
const [action, tuplePath, tupleValue] = t4;
|
|
42915
|
+
if (action !== "update" && action !== "set" || !Array.isArray(tuplePath)) {
|
|
42916
|
+
continue;
|
|
42917
|
+
}
|
|
42918
|
+
if (tuplePath.length > path.length) {
|
|
42919
|
+
continue;
|
|
42920
|
+
}
|
|
42921
|
+
let isPrefix = true;
|
|
42922
|
+
for (let j3 = 0; j3 < tuplePath.length; j3++) {
|
|
42923
|
+
if (tuplePath[j3] !== path[j3]) {
|
|
42924
|
+
isPrefix = false;
|
|
42925
|
+
break;
|
|
42926
|
+
}
|
|
42927
|
+
}
|
|
42928
|
+
if (!isPrefix) {
|
|
42929
|
+
continue;
|
|
42930
|
+
}
|
|
42931
|
+
if (tuplePath.length === path.length) {
|
|
42932
|
+
return tupleValue;
|
|
42933
|
+
}
|
|
42934
|
+
let current2 = tupleValue;
|
|
42935
|
+
for (let j3 = tuplePath.length; j3 < path.length; j3++) {
|
|
42936
|
+
if (current2 == null) {
|
|
42937
|
+
return null;
|
|
42938
|
+
}
|
|
42939
|
+
current2 = current2[path[j3]];
|
|
42940
|
+
}
|
|
42941
|
+
if (current2 !== null) {
|
|
42942
|
+
return current2;
|
|
42943
|
+
}
|
|
42944
|
+
}
|
|
42945
|
+
return null;
|
|
42946
|
+
}
|
|
42899
42947
|
function preprocessChanges(root, tuples = [], options = {}) {
|
|
42900
42948
|
const expandTuple = (t4) => {
|
|
42901
42949
|
const [action, path, value2] = t4 || [];
|
|
@@ -42970,7 +43018,21 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
42970
43018
|
return Array.isArray(tuples) ? tuples.slice() : [];
|
|
42971
43019
|
}
|
|
42972
43020
|
})();
|
|
42973
|
-
const
|
|
43021
|
+
const hydratedGranularChanges = granularChanges.map((t4) => {
|
|
43022
|
+
if (!Array.isArray(t4) || t4.length < 3) {
|
|
43023
|
+
return t4;
|
|
43024
|
+
}
|
|
43025
|
+
const [action, path] = t4;
|
|
43026
|
+
if (action !== "update" && action !== "set" || !Array.isArray(path)) {
|
|
43027
|
+
return t4;
|
|
43028
|
+
}
|
|
43029
|
+
const nextValue = resolveNextValueFromTuples(tuples, path);
|
|
43030
|
+
if (nextValue === null) {
|
|
43031
|
+
return t4;
|
|
43032
|
+
}
|
|
43033
|
+
return [action, path, nextValue];
|
|
43034
|
+
});
|
|
43035
|
+
const baseOrders = computeOrdersForTuples(root, hydratedGranularChanges);
|
|
42974
43036
|
const preferOrdersMap = /* @__PURE__ */ new Map();
|
|
42975
43037
|
for (let i3 = 0; i3 < tuples.length; i3++) {
|
|
42976
43038
|
const t4 = tuples[i3];
|
|
@@ -43000,7 +43062,7 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
43000
43062
|
mergedOrders.push(v3);
|
|
43001
43063
|
}
|
|
43002
43064
|
}
|
|
43003
|
-
return { granularChanges, orders: mergedOrders };
|
|
43065
|
+
return { granularChanges: hydratedGranularChanges, orders: mergedOrders };
|
|
43004
43066
|
}
|
|
43005
43067
|
|
|
43006
43068
|
// src/services/CollabService.js
|
|
@@ -43220,8 +43282,19 @@ var CollabService = class extends BaseService {
|
|
|
43220
43282
|
console.log(
|
|
43221
43283
|
`[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
|
|
43222
43284
|
);
|
|
43223
|
-
this._pendingOps.forEach(({ changes, granularChanges, orders }) => {
|
|
43224
|
-
|
|
43285
|
+
this._pendingOps.forEach(({ changes, granularChanges, orders, options: opOptions }) => {
|
|
43286
|
+
const { message } = opOptions || {};
|
|
43287
|
+
const ts = Date.now();
|
|
43288
|
+
const payload = {
|
|
43289
|
+
changes,
|
|
43290
|
+
granularChanges,
|
|
43291
|
+
orders,
|
|
43292
|
+
ts
|
|
43293
|
+
};
|
|
43294
|
+
if (message) {
|
|
43295
|
+
payload.message = message;
|
|
43296
|
+
}
|
|
43297
|
+
this.socket.emit("ops", payload);
|
|
43225
43298
|
});
|
|
43226
43299
|
this._pendingOps.length = 0;
|
|
43227
43300
|
}
|
|
@@ -43332,6 +43405,7 @@ var CollabService = class extends BaseService {
|
|
|
43332
43405
|
tuples,
|
|
43333
43406
|
Array.isArray(tuples) ? [] : {}
|
|
43334
43407
|
) : deepStringifyFunctions(tuples, Array.isArray(tuples) ? [] : {});
|
|
43408
|
+
const { message } = options;
|
|
43335
43409
|
if (!this.isConnected()) {
|
|
43336
43410
|
console.warn("[CollabService] Not connected, queuing real-time update");
|
|
43337
43411
|
this._pendingOps.push({
|
|
@@ -43343,18 +43417,24 @@ var CollabService = class extends BaseService {
|
|
|
43343
43417
|
return;
|
|
43344
43418
|
}
|
|
43345
43419
|
if ((_c = this.socket) == null ? void 0 : _c.connected) {
|
|
43420
|
+
const ts = Date.now();
|
|
43346
43421
|
console.log("[CollabService] Sending operations to the backend", {
|
|
43347
43422
|
changes: stringifiedTuples,
|
|
43348
43423
|
granularChanges: stringifiedGranularTuples,
|
|
43349
43424
|
orders,
|
|
43350
|
-
ts
|
|
43425
|
+
ts,
|
|
43426
|
+
message
|
|
43351
43427
|
});
|
|
43352
|
-
|
|
43428
|
+
const payload = {
|
|
43353
43429
|
changes: stringifiedTuples,
|
|
43354
43430
|
granularChanges: stringifiedGranularTuples,
|
|
43355
43431
|
orders,
|
|
43356
|
-
ts
|
|
43357
|
-
}
|
|
43432
|
+
ts
|
|
43433
|
+
};
|
|
43434
|
+
if (message) {
|
|
43435
|
+
payload.message = message;
|
|
43436
|
+
}
|
|
43437
|
+
this.socket.emit("ops", payload);
|
|
43358
43438
|
}
|
|
43359
43439
|
return { success: true };
|
|
43360
43440
|
}
|
|
@@ -340,6 +340,48 @@ function getByPathSafe(root, path) {
|
|
|
340
340
|
return null;
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
|
+
function resolveNextValueFromTuples(tuples, path) {
|
|
344
|
+
if (!Array.isArray(tuples) || !Array.isArray(path)) {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
for (let i = tuples.length - 1; i >= 0; i--) {
|
|
348
|
+
const t = tuples[i];
|
|
349
|
+
if (!Array.isArray(t) || t.length < 3) {
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
const [action, tuplePath, tupleValue] = t;
|
|
353
|
+
if (action !== "update" && action !== "set" || !Array.isArray(tuplePath)) {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (tuplePath.length > path.length) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
let isPrefix = true;
|
|
360
|
+
for (let j = 0; j < tuplePath.length; j++) {
|
|
361
|
+
if (tuplePath[j] !== path[j]) {
|
|
362
|
+
isPrefix = false;
|
|
363
|
+
break;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
if (!isPrefix) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
if (tuplePath.length === path.length) {
|
|
370
|
+
return tupleValue;
|
|
371
|
+
}
|
|
372
|
+
let current = tupleValue;
|
|
373
|
+
for (let j = tuplePath.length; j < path.length; j++) {
|
|
374
|
+
if (current == null) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
current = current[path[j]];
|
|
378
|
+
}
|
|
379
|
+
if (current !== null) {
|
|
380
|
+
return current;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
343
385
|
function preprocessChanges(root, tuples = [], options = {}) {
|
|
344
386
|
const expandTuple = (t) => {
|
|
345
387
|
const [action, path, value] = t || [];
|
|
@@ -414,7 +456,21 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
414
456
|
return Array.isArray(tuples) ? tuples.slice() : [];
|
|
415
457
|
}
|
|
416
458
|
})();
|
|
417
|
-
const
|
|
459
|
+
const hydratedGranularChanges = granularChanges.map((t) => {
|
|
460
|
+
if (!Array.isArray(t) || t.length < 3) {
|
|
461
|
+
return t;
|
|
462
|
+
}
|
|
463
|
+
const [action, path] = t;
|
|
464
|
+
if (action !== "update" && action !== "set" || !Array.isArray(path)) {
|
|
465
|
+
return t;
|
|
466
|
+
}
|
|
467
|
+
const nextValue = resolveNextValueFromTuples(tuples, path);
|
|
468
|
+
if (nextValue === null) {
|
|
469
|
+
return t;
|
|
470
|
+
}
|
|
471
|
+
return [action, path, nextValue];
|
|
472
|
+
});
|
|
473
|
+
const baseOrders = computeOrdersForTuples(root, hydratedGranularChanges);
|
|
418
474
|
const preferOrdersMap = /* @__PURE__ */ new Map();
|
|
419
475
|
for (let i = 0; i < tuples.length; i++) {
|
|
420
476
|
const t = tuples[i];
|
|
@@ -444,7 +500,7 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
444
500
|
mergedOrders.push(v);
|
|
445
501
|
}
|
|
446
502
|
}
|
|
447
|
-
return { granularChanges, orders: mergedOrders };
|
|
503
|
+
return { granularChanges: hydratedGranularChanges, orders: mergedOrders };
|
|
448
504
|
}
|
|
449
505
|
export {
|
|
450
506
|
preprocessChanges
|
|
@@ -319,6 +319,12 @@ class AuthService extends BaseService {
|
|
|
319
319
|
throw new Error(`Failed to get user profile: ${error.message}`, { cause: error });
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
|
+
getAuthToken() {
|
|
323
|
+
if (!this._tokenManager) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return this._tokenManager.getAccessToken();
|
|
327
|
+
}
|
|
322
328
|
/**
|
|
323
329
|
* Get stored authentication state (backward compatibility method)
|
|
324
330
|
* Replaces AuthService.getStoredAuthState()
|
|
@@ -221,8 +221,19 @@ class CollabService extends BaseService {
|
|
|
221
221
|
console.log(
|
|
222
222
|
`[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
|
|
223
223
|
);
|
|
224
|
-
this._pendingOps.forEach(({ changes, granularChanges, orders }) => {
|
|
225
|
-
|
|
224
|
+
this._pendingOps.forEach(({ changes, granularChanges, orders, options: opOptions }) => {
|
|
225
|
+
const { message } = opOptions || {};
|
|
226
|
+
const ts = Date.now();
|
|
227
|
+
const payload = {
|
|
228
|
+
changes,
|
|
229
|
+
granularChanges,
|
|
230
|
+
orders,
|
|
231
|
+
ts
|
|
232
|
+
};
|
|
233
|
+
if (message) {
|
|
234
|
+
payload.message = message;
|
|
235
|
+
}
|
|
236
|
+
this.socket.emit("ops", payload);
|
|
226
237
|
});
|
|
227
238
|
this._pendingOps.length = 0;
|
|
228
239
|
}
|
|
@@ -333,6 +344,7 @@ class CollabService extends BaseService {
|
|
|
333
344
|
tuples,
|
|
334
345
|
Array.isArray(tuples) ? [] : {}
|
|
335
346
|
) : deepStringifyFunctions(tuples, Array.isArray(tuples) ? [] : {});
|
|
347
|
+
const { message } = options;
|
|
336
348
|
if (!this.isConnected()) {
|
|
337
349
|
console.warn("[CollabService] Not connected, queuing real-time update");
|
|
338
350
|
this._pendingOps.push({
|
|
@@ -344,18 +356,24 @@ class CollabService extends BaseService {
|
|
|
344
356
|
return;
|
|
345
357
|
}
|
|
346
358
|
if ((_c = this.socket) == null ? void 0 : _c.connected) {
|
|
359
|
+
const ts = Date.now();
|
|
347
360
|
console.log("[CollabService] Sending operations to the backend", {
|
|
348
361
|
changes: stringifiedTuples,
|
|
349
362
|
granularChanges: stringifiedGranularTuples,
|
|
350
363
|
orders,
|
|
351
|
-
ts
|
|
364
|
+
ts,
|
|
365
|
+
message
|
|
352
366
|
});
|
|
353
|
-
|
|
367
|
+
const payload = {
|
|
354
368
|
changes: stringifiedTuples,
|
|
355
369
|
granularChanges: stringifiedGranularTuples,
|
|
356
370
|
orders,
|
|
357
|
-
ts
|
|
358
|
-
}
|
|
371
|
+
ts
|
|
372
|
+
};
|
|
373
|
+
if (message) {
|
|
374
|
+
payload.message = message;
|
|
375
|
+
}
|
|
376
|
+
this.socket.emit("ops", payload);
|
|
359
377
|
}
|
|
360
378
|
return { success: true };
|
|
361
379
|
}
|
|
@@ -13,6 +13,48 @@ function getByPathSafe(root, path) {
|
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
|
+
function resolveNextValueFromTuples(tuples, path) {
|
|
17
|
+
if (!Array.isArray(tuples) || !Array.isArray(path)) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
for (let i = tuples.length - 1; i >= 0; i--) {
|
|
21
|
+
const t = tuples[i];
|
|
22
|
+
if (!Array.isArray(t) || t.length < 3) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const [action, tuplePath, tupleValue] = t;
|
|
26
|
+
if (action !== "update" && action !== "set" || !Array.isArray(tuplePath)) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (tuplePath.length > path.length) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
let isPrefix = true;
|
|
33
|
+
for (let j = 0; j < tuplePath.length; j++) {
|
|
34
|
+
if (tuplePath[j] !== path[j]) {
|
|
35
|
+
isPrefix = false;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!isPrefix) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (tuplePath.length === path.length) {
|
|
43
|
+
return tupleValue;
|
|
44
|
+
}
|
|
45
|
+
let current = tupleValue;
|
|
46
|
+
for (let j = tuplePath.length; j < path.length; j++) {
|
|
47
|
+
if (current == null) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
current = current[path[j]];
|
|
51
|
+
}
|
|
52
|
+
if (current !== null) {
|
|
53
|
+
return current;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
16
58
|
function preprocessChanges(root, tuples = [], options = {}) {
|
|
17
59
|
const expandTuple = (t) => {
|
|
18
60
|
const [action, path, value] = t || [];
|
|
@@ -87,7 +129,21 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
87
129
|
return Array.isArray(tuples) ? tuples.slice() : [];
|
|
88
130
|
}
|
|
89
131
|
})();
|
|
90
|
-
const
|
|
132
|
+
const hydratedGranularChanges = granularChanges.map((t) => {
|
|
133
|
+
if (!Array.isArray(t) || t.length < 3) {
|
|
134
|
+
return t;
|
|
135
|
+
}
|
|
136
|
+
const [action, path] = t;
|
|
137
|
+
if (action !== "update" && action !== "set" || !Array.isArray(path)) {
|
|
138
|
+
return t;
|
|
139
|
+
}
|
|
140
|
+
const nextValue = resolveNextValueFromTuples(tuples, path);
|
|
141
|
+
if (nextValue === null) {
|
|
142
|
+
return t;
|
|
143
|
+
}
|
|
144
|
+
return [action, path, nextValue];
|
|
145
|
+
});
|
|
146
|
+
const baseOrders = computeOrdersForTuples(root, hydratedGranularChanges);
|
|
91
147
|
const preferOrdersMap = /* @__PURE__ */ new Map();
|
|
92
148
|
for (let i = 0; i < tuples.length; i++) {
|
|
93
149
|
const t = tuples[i];
|
|
@@ -117,7 +173,7 @@ function preprocessChanges(root, tuples = [], options = {}) {
|
|
|
117
173
|
mergedOrders.push(v);
|
|
118
174
|
}
|
|
119
175
|
}
|
|
120
|
-
return { granularChanges, orders: mergedOrders };
|
|
176
|
+
return { granularChanges: hydratedGranularChanges, orders: mergedOrders };
|
|
121
177
|
}
|
|
122
178
|
export {
|
|
123
179
|
preprocessChanges
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbo.ls/sdk",
|
|
3
|
-
"version": "2.32.
|
|
3
|
+
"version": "2.32.26",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"test:user": "cross-env NODE_ENV=$NODE_ENV npx tape integration-tests/index.js integration-tests/user/*.test.js | tap-spec"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@domql/element": "^2.32.
|
|
50
|
-
"@domql/utils": "^2.32.
|
|
49
|
+
"@domql/element": "^2.32.26",
|
|
50
|
+
"@domql/utils": "^2.32.26",
|
|
51
51
|
"@grafana/faro-web-sdk": "^1.19.0",
|
|
52
52
|
"@grafana/faro-web-tracing": "^1.19.0",
|
|
53
|
-
"@symbo.ls/router": "^2.32.
|
|
54
|
-
"@symbo.ls/socket": "^2.32.
|
|
53
|
+
"@symbo.ls/router": "^2.32.26",
|
|
54
|
+
"@symbo.ls/socket": "^2.32.26",
|
|
55
55
|
"acorn": "^8.14.0",
|
|
56
56
|
"acorn-walk": "^8.3.4",
|
|
57
57
|
"dexie": "^4.0.11",
|
|
@@ -73,5 +73,5 @@
|
|
|
73
73
|
"tap-spec": "^5.0.0",
|
|
74
74
|
"tape": "^5.9.0"
|
|
75
75
|
},
|
|
76
|
-
"gitHead": "
|
|
76
|
+
"gitHead": "66d87154b447b4b96fdc5030ab372fbfeb4547f3"
|
|
77
77
|
}
|
|
@@ -370,6 +370,13 @@ export class AuthService extends BaseService {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
getAuthToken() {
|
|
374
|
+
if (!this._tokenManager) {
|
|
375
|
+
return null
|
|
376
|
+
}
|
|
377
|
+
return this._tokenManager.getAccessToken()
|
|
378
|
+
}
|
|
379
|
+
|
|
373
380
|
/**
|
|
374
381
|
* Get stored authentication state (backward compatibility method)
|
|
375
382
|
* Replaces AuthService.getStoredAuthState()
|
|
@@ -290,8 +290,19 @@ export class CollabService extends BaseService {
|
|
|
290
290
|
console.log(
|
|
291
291
|
`[CollabService] Flushing ${this._pendingOps.length} offline operation batch(es)`
|
|
292
292
|
)
|
|
293
|
-
this._pendingOps.forEach(({ changes, granularChanges, orders }) => {
|
|
294
|
-
|
|
293
|
+
this._pendingOps.forEach(({ changes, granularChanges, orders, options: opOptions }) => {
|
|
294
|
+
const { message } = opOptions || {}
|
|
295
|
+
const ts = Date.now()
|
|
296
|
+
const payload = {
|
|
297
|
+
changes,
|
|
298
|
+
granularChanges,
|
|
299
|
+
orders,
|
|
300
|
+
ts
|
|
301
|
+
}
|
|
302
|
+
if (message) {
|
|
303
|
+
payload.message = message
|
|
304
|
+
}
|
|
305
|
+
this.socket.emit('ops', payload)
|
|
295
306
|
})
|
|
296
307
|
this._pendingOps.length = 0
|
|
297
308
|
}
|
|
@@ -423,6 +434,8 @@ export class CollabService extends BaseService {
|
|
|
423
434
|
)
|
|
424
435
|
: deepStringifyFunctions(tuples, Array.isArray(tuples) ? [] : {})
|
|
425
436
|
|
|
437
|
+
const { message } = options
|
|
438
|
+
|
|
426
439
|
// If not connected yet, queue the operations for later synchronisation.
|
|
427
440
|
if (!this.isConnected()) {
|
|
428
441
|
console.warn('[CollabService] Not connected, queuing real-time update')
|
|
@@ -437,18 +450,26 @@ export class CollabService extends BaseService {
|
|
|
437
450
|
|
|
438
451
|
// When connected, send the operations to the backend.
|
|
439
452
|
if (this.socket?.connected) {
|
|
453
|
+
const ts = Date.now()
|
|
440
454
|
console.log('[CollabService] Sending operations to the backend', {
|
|
441
455
|
changes: stringifiedTuples,
|
|
442
456
|
granularChanges: stringifiedGranularTuples,
|
|
443
457
|
orders,
|
|
444
|
-
ts
|
|
458
|
+
ts,
|
|
459
|
+
message
|
|
445
460
|
})
|
|
446
|
-
|
|
461
|
+
const payload = {
|
|
447
462
|
changes: stringifiedTuples,
|
|
448
463
|
granularChanges: stringifiedGranularTuples,
|
|
449
464
|
orders,
|
|
450
|
-
ts
|
|
451
|
-
}
|
|
465
|
+
ts
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (message) {
|
|
469
|
+
payload.message = message
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
this.socket.emit('ops', payload)
|
|
452
473
|
}
|
|
453
474
|
|
|
454
475
|
return { success: true }
|
|
@@ -11,6 +11,65 @@ function getByPathSafe (root, path) {
|
|
|
11
11
|
try { return root.getByPath(path) } catch { return null }
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
// Given the original high-level tuples and a fully qualified path, resolve the
|
|
15
|
+
// "next" value that the change set intends to write at that path.
|
|
16
|
+
// This walks tuples from last to first so that later changes win, and supports
|
|
17
|
+
// nested paths where the tuple only targets a parent container, e.g.:
|
|
18
|
+
// ['update', ['components', 'CanvasLogoDropdown'], { ... }]
|
|
19
|
+
// for a granular path like:
|
|
20
|
+
// ['components', 'CanvasLogoDropdown', 'ProjectNav', 'ListInDropdown', 'children']
|
|
21
|
+
function resolveNextValueFromTuples (tuples, path) {
|
|
22
|
+
if (!Array.isArray(tuples) || !Array.isArray(path)) { return null }
|
|
23
|
+
|
|
24
|
+
// Walk from the end to honour the latest change
|
|
25
|
+
for (let i = tuples.length - 1; i >= 0; i--) {
|
|
26
|
+
const t = tuples[i]
|
|
27
|
+
if (!Array.isArray(t) || t.length < 3) {
|
|
28
|
+
// eslint-disable-next-line no-continue
|
|
29
|
+
continue
|
|
30
|
+
}
|
|
31
|
+
const [action, tuplePath, tupleValue] = t
|
|
32
|
+
if ((action !== 'update' && action !== 'set') || !Array.isArray(tuplePath)) {
|
|
33
|
+
// eslint-disable-next-line no-continue
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
if (tuplePath.length > path.length) {
|
|
37
|
+
// eslint-disable-next-line no-continue
|
|
38
|
+
continue
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Ensure tuplePath is a prefix of the requested path
|
|
42
|
+
let isPrefix = true
|
|
43
|
+
for (let j = 0; j < tuplePath.length; j++) {
|
|
44
|
+
if (tuplePath[j] !== path[j]) {
|
|
45
|
+
isPrefix = false
|
|
46
|
+
break
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!isPrefix) {
|
|
50
|
+
// eslint-disable-next-line no-continue
|
|
51
|
+
continue
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Direct match: the tuple already targets the exact path
|
|
55
|
+
if (tuplePath.length === path.length) {
|
|
56
|
+
return tupleValue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Nested match: drill into the tuple value using the remaining segments
|
|
60
|
+
let current = tupleValue
|
|
61
|
+
for (let j = tuplePath.length; j < path.length; j++) {
|
|
62
|
+
if (current == null) { return null }
|
|
63
|
+
current = current[path[j]]
|
|
64
|
+
}
|
|
65
|
+
if (current !== null) {
|
|
66
|
+
return current
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return null
|
|
71
|
+
}
|
|
72
|
+
|
|
14
73
|
/**
|
|
15
74
|
* Preprocess broad project changes into granular changes and ordering metadata.
|
|
16
75
|
* - Expands top-level object updates (e.g. ['update', ['components'], {...}])
|
|
@@ -121,8 +180,23 @@ export function preprocessChanges (root, tuples = [], options = {}) {
|
|
|
121
180
|
}
|
|
122
181
|
})()
|
|
123
182
|
|
|
183
|
+
const hydratedGranularChanges = granularChanges.map(t => {
|
|
184
|
+
if (!Array.isArray(t) || t.length < 3) { return t }
|
|
185
|
+
const [action, path] = t
|
|
186
|
+
if ((action !== 'update' && action !== 'set') || !Array.isArray(path)) {
|
|
187
|
+
return t
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const nextValue = resolveNextValueFromTuples(tuples, path)
|
|
191
|
+
if (nextValue === null) {
|
|
192
|
+
return t
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return [action, path, nextValue]
|
|
196
|
+
})
|
|
197
|
+
|
|
124
198
|
// Base orders from granular changes/state
|
|
125
|
-
const baseOrders = computeOrdersForTuples(root,
|
|
199
|
+
const baseOrders = computeOrdersForTuples(root, hydratedGranularChanges)
|
|
126
200
|
|
|
127
201
|
// Prefer explicit order for containers updated via ['update', [type], value] or ['update', [type, key], value]
|
|
128
202
|
const preferOrdersMap = new Map()
|
|
@@ -161,5 +235,5 @@ export function preprocessChanges (root, tuples = [], options = {}) {
|
|
|
161
235
|
if (!seen.has(k)) { seen.add(k); mergedOrders.push(v) }
|
|
162
236
|
}
|
|
163
237
|
|
|
164
|
-
return { granularChanges, orders: mergedOrders }
|
|
238
|
+
return { granularChanges: hydratedGranularChanges, orders: mergedOrders }
|
|
165
239
|
}
|
package/src/utils/services.js
CHANGED