@recordtimelabel/core 0.1.4 → 0.1.7
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/package.json +1 -1
- package/src/index.js +137 -25
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const REQUIRED_FOLDERS = [
|
|
|
5
5
|
{ id: DEFAULT_FOLDER_ID, name: 'Uncategorized' }
|
|
6
6
|
];
|
|
7
7
|
|
|
8
|
-
export const RECORD_TIMELABEL_CORE_VERSION = '0.1.
|
|
8
|
+
export const RECORD_TIMELABEL_CORE_VERSION = '0.1.6';
|
|
9
9
|
|
|
10
10
|
export const OPERATION_TYPES = Object.freeze({
|
|
11
11
|
RECORD_CREATE: 'record.create',
|
|
@@ -21,6 +21,13 @@ export const OPERATION_TYPES = Object.freeze({
|
|
|
21
21
|
EXPANDED_GROUPS_UPDATE: 'expandedGroups.update',
|
|
22
22
|
SETTINGS_UPDATE: 'settings.update'
|
|
23
23
|
});
|
|
24
|
+
|
|
25
|
+
const ORDER_OPERATION_TYPES = new Set([
|
|
26
|
+
OPERATION_TYPES.RECORD_REORDER,
|
|
27
|
+
OPERATION_TYPES.FOLDER_REORDER,
|
|
28
|
+
OPERATION_TYPES.GROUP_REORDER,
|
|
29
|
+
OPERATION_TYPES.EXPANDED_GROUPS_UPDATE
|
|
30
|
+
]);
|
|
24
31
|
|
|
25
32
|
export const RECORD_TIMELABEL_SYNC_MODES = Object.freeze({
|
|
26
33
|
FULL: 'full',
|
|
@@ -514,15 +521,57 @@ export const preserveRecordOrderFromLocal = (records = {}, localRecords = {}) =>
|
|
|
514
521
|
return result;
|
|
515
522
|
};
|
|
516
523
|
|
|
517
|
-
export const createOperation = (type, payload = {}, options = {}) => ({
|
|
518
|
-
id: options.id || `${type}:${options.clientId || 'client'}:${options.now || Date.now()}:${Math.random().toString(36).slice(2, 10)}`,
|
|
519
|
-
type,
|
|
520
|
-
payload,
|
|
521
|
-
clientId: options.clientId || null,
|
|
522
|
-
createdAt: options.now || Date.now()
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
|
|
524
|
+
export const createOperation = (type, payload = {}, options = {}) => ({
|
|
525
|
+
id: options.id || `${type}:${options.clientId || 'client'}:${options.now || Date.now()}:${Math.random().toString(36).slice(2, 10)}`,
|
|
526
|
+
type,
|
|
527
|
+
payload,
|
|
528
|
+
clientId: options.clientId || null,
|
|
529
|
+
createdAt: options.now || Date.now()
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
const getOperationClientInstanceId = (operation = {}, payload = {}) => (
|
|
533
|
+
operation.clientInstanceId ||
|
|
534
|
+
operation.instanceId ||
|
|
535
|
+
payload.clientInstanceId ||
|
|
536
|
+
payload.instanceId ||
|
|
537
|
+
null
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
const buildOperationSyncMeta = (previousSyncMeta = {}, operation = {}, payload = {}, operationTime) => {
|
|
541
|
+
const nextSyncMeta = {
|
|
542
|
+
...(previousSyncMeta || {}),
|
|
543
|
+
lastOperationId: operation.id || previousSyncMeta?.lastOperationId || null,
|
|
544
|
+
lastOperationType: operation.type || previousSyncMeta?.lastOperationType || null,
|
|
545
|
+
lastClientId: operation.clientId || previousSyncMeta?.lastClientId || null,
|
|
546
|
+
lastOperationAt: operationTime
|
|
547
|
+
};
|
|
548
|
+
const nextClientInstanceId = getOperationClientInstanceId(operation, payload);
|
|
549
|
+
|
|
550
|
+
if (nextClientInstanceId) {
|
|
551
|
+
nextSyncMeta.lastClientInstanceId = nextClientInstanceId;
|
|
552
|
+
} else if (operation.clientId) {
|
|
553
|
+
delete nextSyncMeta.lastClientInstanceId;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
if (ORDER_OPERATION_TYPES.has(operation.type)) {
|
|
557
|
+
nextSyncMeta.lastOrderOperationAtByType = {
|
|
558
|
+
...(previousSyncMeta?.lastOrderOperationAtByType || {}),
|
|
559
|
+
[operation.type]: operationTime
|
|
560
|
+
};
|
|
561
|
+
nextSyncMeta.lastOrderOperationIdByType = {
|
|
562
|
+
...(previousSyncMeta?.lastOrderOperationIdByType || {}),
|
|
563
|
+
[operation.type]: operation.id || null
|
|
564
|
+
};
|
|
565
|
+
nextSyncMeta.lastOrderClientIdByType = {
|
|
566
|
+
...(previousSyncMeta?.lastOrderClientIdByType || {}),
|
|
567
|
+
[operation.type]: operation.clientId || previousSyncMeta?.lastClientId || null
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return nextSyncMeta;
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
export const applyOperation = (state = {}, operation = {}) => {
|
|
526
575
|
const normalized = normalizeState(state);
|
|
527
576
|
const type = operation.type;
|
|
528
577
|
const payload = operation.payload || {};
|
|
@@ -723,16 +772,15 @@ export const applyOperation = (state = {}, operation = {}) => {
|
|
|
723
772
|
|
|
724
773
|
default:
|
|
725
774
|
return normalized;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
nextState.lastModified = Math.max(nextState.lastModified || 0, operationTime);
|
|
729
|
-
nextState.rtlSyncMeta =
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
nextState.lastModified = Math.max(nextState.lastModified || 0, operationTime);
|
|
778
|
+
nextState.rtlSyncMeta = buildOperationSyncMeta(
|
|
779
|
+
nextState.rtlSyncMeta,
|
|
780
|
+
operation,
|
|
781
|
+
payload,
|
|
782
|
+
operationTime
|
|
783
|
+
);
|
|
736
784
|
|
|
737
785
|
return normalizeState(nextState);
|
|
738
786
|
};
|
|
@@ -1334,6 +1382,12 @@ const getLastLocalOperationType = (data = {}) => String(
|
|
|
1334
1382
|
''
|
|
1335
1383
|
);
|
|
1336
1384
|
|
|
1385
|
+
const getLastRemoteOperationType = (data = {}) => String(
|
|
1386
|
+
data?.rtlSyncMeta?.lastOperationType ||
|
|
1387
|
+
data?.syncMeta?.lastRemoteOperationType ||
|
|
1388
|
+
''
|
|
1389
|
+
);
|
|
1390
|
+
|
|
1337
1391
|
const getLastLocalOperationAt = (data = {}) => toFiniteTimestamp(
|
|
1338
1392
|
data?.rtlSyncMeta?.lastOperationAt ||
|
|
1339
1393
|
data?.rtlSyncMeta?.lastLocalOperationAt ||
|
|
@@ -1347,23 +1401,81 @@ const getLastRemoteOperationAt = (data = {}) => toFiniteTimestamp(
|
|
|
1347
1401
|
data?.lastModified
|
|
1348
1402
|
);
|
|
1349
1403
|
|
|
1404
|
+
const getLastClientId = (data = {}) => String(
|
|
1405
|
+
data?.rtlSyncMeta?.lastClientId ||
|
|
1406
|
+
data?.syncMeta?.lastClientId ||
|
|
1407
|
+
data?.syncMeta?.lastMergedClientId ||
|
|
1408
|
+
''
|
|
1409
|
+
);
|
|
1410
|
+
|
|
1411
|
+
const getOrderOperationAtByType = (data = {}, operationType) => toFiniteTimestamp(
|
|
1412
|
+
data?.rtlSyncMeta?.lastOrderOperationAtByType?.[operationType] ||
|
|
1413
|
+
data?.syncMeta?.lastOrderOperationAtByType?.[operationType]
|
|
1414
|
+
);
|
|
1415
|
+
|
|
1416
|
+
const getOrderClientIdByType = (data = {}, operationType) => String(
|
|
1417
|
+
data?.rtlSyncMeta?.lastOrderClientIdByType?.[operationType] ||
|
|
1418
|
+
data?.syncMeta?.lastOrderClientIdByType?.[operationType] ||
|
|
1419
|
+
''
|
|
1420
|
+
);
|
|
1421
|
+
|
|
1422
|
+
const hasOperationMarker = (data = {}, operationType, operationTypeResolver = getLastRemoteOperationType) => {
|
|
1423
|
+
const lastOperationId = String(data?.rtlSyncMeta?.lastOperationId || '');
|
|
1424
|
+
const lastOperationType = operationTypeResolver(data);
|
|
1425
|
+
return lastOperationId.startsWith(`${operationType}:`) || lastOperationType === operationType;
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
const getMarkedOrderOperationAt = (data = {}, operationType, {
|
|
1429
|
+
operationTypeResolver = getLastRemoteOperationType,
|
|
1430
|
+
operationAtResolver = getLastRemoteOperationAt
|
|
1431
|
+
} = {}) => {
|
|
1432
|
+
const operationAtByType = getOrderOperationAtByType(data, operationType);
|
|
1433
|
+
if (operationAtByType) return operationAtByType;
|
|
1434
|
+
return hasOperationMarker(data, operationType, operationTypeResolver)
|
|
1435
|
+
? operationAtResolver(data)
|
|
1436
|
+
: 0;
|
|
1437
|
+
};
|
|
1438
|
+
|
|
1350
1439
|
const hasRecentLocalOperation = (localData = {}, remoteData = {}, operationType) => {
|
|
1351
|
-
const
|
|
1352
|
-
|
|
1353
|
-
|
|
1440
|
+
const localOperationAt = getMarkedOrderOperationAt(localData, operationType, {
|
|
1441
|
+
operationTypeResolver: getLastLocalOperationType,
|
|
1442
|
+
operationAtResolver: getLastLocalOperationAt
|
|
1443
|
+
});
|
|
1444
|
+
if (!localOperationAt) {
|
|
1354
1445
|
return false;
|
|
1355
1446
|
}
|
|
1356
1447
|
|
|
1357
|
-
const
|
|
1358
|
-
|
|
1448
|
+
const remoteOrderOperationAt = getMarkedOrderOperationAt(remoteData, operationType, {
|
|
1449
|
+
operationTypeResolver: getLastRemoteOperationType,
|
|
1450
|
+
operationAtResolver: getLastRemoteOperationAt
|
|
1451
|
+
});
|
|
1452
|
+
const remoteOperationAt = remoteOrderOperationAt || getLastRemoteOperationAt(remoteData);
|
|
1359
1453
|
return !localOperationAt || !remoteOperationAt || localOperationAt >= remoteOperationAt;
|
|
1360
1454
|
};
|
|
1361
1455
|
|
|
1456
|
+
const hasRemoteOrderOperationFromDifferentClient = (localData = {}, remoteData = {}, operationType) => {
|
|
1457
|
+
const remoteOrderOperationAt = getMarkedOrderOperationAt(remoteData, operationType, {
|
|
1458
|
+
operationTypeResolver: getLastRemoteOperationType,
|
|
1459
|
+
operationAtResolver: getLastRemoteOperationAt
|
|
1460
|
+
});
|
|
1461
|
+
if (!remoteOrderOperationAt) {
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
const remoteClientId = getOrderClientIdByType(remoteData, operationType) || getLastClientId(remoteData);
|
|
1466
|
+
const localClientId = getLastClientId(localData);
|
|
1467
|
+
return Boolean(remoteClientId) && (!localClientId || remoteClientId !== localClientId);
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1362
1470
|
const shouldPreferRecentLocalOrder = (localData = {}, remoteData = {}, operationType) => {
|
|
1363
1471
|
if (hasRecentLocalOperation(localData, remoteData, operationType)) {
|
|
1364
1472
|
return true;
|
|
1365
1473
|
}
|
|
1366
1474
|
|
|
1475
|
+
if (hasRemoteOrderOperationFromDifferentClient(localData, remoteData, operationType)) {
|
|
1476
|
+
return false;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1367
1479
|
const localOperationAt = getLastLocalOperationAt(localData);
|
|
1368
1480
|
const remoteOperationAt = getLastRemoteOperationAt(remoteData);
|
|
1369
1481
|
return Boolean(localOperationAt) && (!remoteOperationAt || localOperationAt >= remoteOperationAt);
|