@claudian-collab/protocol 4.1.3 → 4.1.4
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/README.md
CHANGED
|
@@ -64,7 +64,7 @@ part of the public surface.
|
|
|
64
64
|
|
|
65
65
|
Package SemVer, canonical wire version, and Cloud binding version are independent concepts.
|
|
66
66
|
|
|
67
|
-
- **Package version** (this `package.json`): `4.1.
|
|
67
|
+
- **Package version** (this `package.json`): `4.1.4`. Behavior-preserving implementation refactors and reviewed compatible defect corrections may use a patch release when public declarations, runtime exports, and accepted wire/binding semantics remain unchanged. Minor releases may add backward-compatible API. Removing or incompatibly changing an existing declaration, export, runtime behavior, codec, error, limit, ref rule, operation, or compatibility rule requires a major release. Package API SemVer is classified independently from the wire and Cloud binding contracts, but a compatible package addition cannot exempt a changed wire contract from a wire-version increase or a changed Cloud binding contract from a binding-version increase. The package version never signals wire compatibility by itself.
|
|
68
68
|
- **Wire version** (`COLLAB_PROTOCOL_VERSION`): currently `8`. The supported range is exactly `[8, 8]`. This is independent from Cloud binding version `4` and the application-owned LAN bindings. Any change to an existing envelope, DTO, operation payload shape, or operation definition requires a new wire-protocol version. This includes additive operations: every wire-contract change increases the canonical wire version.
|
|
69
69
|
- **Cloud binding version** (`COLLAB_CLOUD_BINDING_VERSION`): currently `4`. It versions Cloud routes, bounded transfer streams, and capabilities independently from package and wire versions. Every Cloud binding-contract change increases this version. Bindings before v4 and wire versions before v8 are unsupported rather than translated or dual-interpreted.
|
|
70
70
|
|
|
@@ -1520,7 +1520,10 @@ function validateContinuity(records) {
|
|
|
1520
1520
|
const recoveryEvidenceInvalid = direction === null
|
|
1521
1521
|
|| (direction === 'lan-to-cloud' && recovery.value.sourceEvidence === null)
|
|
1522
1522
|
|| (targetEvidenceRequired && recovery.value.targetEvidence === null)
|
|
1523
|
-
|| (
|
|
1523
|
+
|| (isCancellation
|
|
1524
|
+
? (lifecycle.value.batchRevision !== null)
|
|
1525
|
+
!== (recovery.value.stageSha256 !== null)
|
|
1526
|
+
: stageRequired !== (recovery.value.stageSha256 !== null))
|
|
1524
1527
|
|| (direction === 'lan-to-cloud'
|
|
1525
1528
|
&& recovery.value.stageSha256 !== null
|
|
1526
1529
|
&& recovery.value.stageSha256 !== lifecycle.value.checkpointSha256)
|
|
@@ -1543,33 +1546,51 @@ function validateContinuity(records) {
|
|
|
1543
1546
|
if (projectAuthorityGeneration !== expectedProjectAuthorityGeneration) {
|
|
1544
1547
|
throw invalidPayload('records');
|
|
1545
1548
|
}
|
|
1549
|
+
const hostMemberId = direction === 'lan-to-cloud'
|
|
1550
|
+
? recovery.value.sourceHostMemberId
|
|
1551
|
+
: recovery.value.targetHostMemberId;
|
|
1552
|
+
const transferMemberIds = [...memberRecords.values()]
|
|
1553
|
+
.filter(item => (item.value.status === 'active'
|
|
1554
|
+
&& item.value.memberId !== hostMemberId
|
|
1555
|
+
&& Date.parse(item.value.createdAt) <= Date.parse(recovery.value.createdAt)))
|
|
1556
|
+
.map(item => item.value.memberId);
|
|
1546
1557
|
if (!isCancellation) {
|
|
1547
|
-
const hostMemberId = direction === 'lan-to-cloud'
|
|
1548
|
-
? recovery.value.sourceHostMemberId
|
|
1549
|
-
: recovery.value.targetHostMemberId;
|
|
1550
|
-
const eligibleMemberIds = [...memberRecords.values()]
|
|
1551
|
-
.filter(item => item.value.status === 'active' && item.value.memberId !== hostMemberId)
|
|
1552
|
-
.map(item => item.value.memberId);
|
|
1553
|
-
const memberCustodyRequired = normalPhaseIndex >= 3;
|
|
1554
1558
|
const batchReceiptRequired = direction === 'cloud-to-lan'
|
|
1555
1559
|
? normalPhaseIndex >= 3
|
|
1556
1560
|
: normalPhaseIndex >= 4;
|
|
1557
1561
|
if ((batchReceipt !== undefined) !== batchReceiptRequired) {
|
|
1558
1562
|
throw invalidPayload('records');
|
|
1559
1563
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1564
|
+
}
|
|
1565
|
+
const memberCustody = transferMemberIds.map((memberId) => {
|
|
1566
|
+
const identity = `${recovery.value.transferId}:${memberId}`;
|
|
1567
|
+
const claimPresent = claims.has(identity);
|
|
1568
|
+
const envelopePresent = protectedEnvelopes.has(identity);
|
|
1569
|
+
const redemptionPresent = redemptionReceipts.has(identity);
|
|
1570
|
+
return {
|
|
1571
|
+
absent: !claimPresent && !envelopePresent && !redemptionPresent,
|
|
1572
|
+
exact: direction === 'lan-to-cloud'
|
|
1566
1573
|
? claimPresent && !envelopePresent
|
|
1567
|
-
: !claimPresent && envelopePresent !== redemptionPresent
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1574
|
+
: !claimPresent && envelopePresent !== redemptionPresent,
|
|
1575
|
+
};
|
|
1576
|
+
});
|
|
1577
|
+
const cleanupInProgress = isCancellation
|
|
1578
|
+
&& cancellationIndex === 1
|
|
1579
|
+
&& lifecycle.value.batchRevision !== null;
|
|
1580
|
+
if (cleanupInProgress) {
|
|
1581
|
+
const allAbsent = memberCustody.every(item => item.absent);
|
|
1582
|
+
const allExact = memberCustody.every(item => item.exact);
|
|
1583
|
+
if (!allAbsent && !allExact) {
|
|
1584
|
+
throw invalidPayload('records');
|
|
1571
1585
|
}
|
|
1572
1586
|
}
|
|
1587
|
+
else {
|
|
1588
|
+
const memberCustodyRequired = isCancellation
|
|
1589
|
+
? !cleanupCompleted && lifecycle.value.batchRevision !== null
|
|
1590
|
+
: normalPhaseIndex >= 3;
|
|
1591
|
+
if (memberCustody.some(item => (memberCustodyRequired ? !item.exact : !item.absent)))
|
|
1592
|
+
throw invalidPayload('records');
|
|
1593
|
+
}
|
|
1573
1594
|
if (recovery.value.targetEvidence !== null && (recovery.value.targetHostMemberId === null
|
|
1574
1595
|
|| principalBindings.get(recovery.value.targetHostMemberId)
|
|
1575
1596
|
!== recovery.value.targetEvidence.principalId))
|
|
@@ -1585,6 +1606,7 @@ function validateContinuity(records) {
|
|
|
1585
1606
|
? 'cloud-to-lan'
|
|
1586
1607
|
: 'lan-to-cloud')
|
|
1587
1608
|
|| lifecycle.value.expectedAuthorityGeneration !== recovery.value.sourceAuthority.generation
|
|
1609
|
+
|| lifecycle.value.createdAt !== recovery.value.createdAt
|
|
1588
1610
|
|| lifecycle.value.scheduledAt !== recovery.value.expiresAt
|
|
1589
1611
|
|| (recovery.value.sourceHostMemberId !== null
|
|
1590
1612
|
&& !members.has(recovery.value.sourceHostMemberId))
|
|
@@ -1640,6 +1662,7 @@ function validateContinuity(records) {
|
|
|
1640
1662
|
for (const claim of claims.values()) {
|
|
1641
1663
|
const lifecycle = lifecycles.get(claim.value.transferId);
|
|
1642
1664
|
const recovery = recoveries.get(claim.value.transferId);
|
|
1665
|
+
const member = memberRecords.get(claim.value.memberId);
|
|
1643
1666
|
const redemptionReceipt = redemptionReceipts.get(`${claim.value.transferId}:${claim.value.memberId}`);
|
|
1644
1667
|
const redeemedOverride = redemptionReceipt === undefined
|
|
1645
1668
|
? undefined
|
|
@@ -1648,8 +1671,9 @@ function validateContinuity(records) {
|
|
|
1648
1671
|
&& item.value.state === 'redeemed'
|
|
1649
1672
|
&& item.value.claimSha256 === redemptionReceipt.value.receipt.claimSha256
|
|
1650
1673
|
&& item.value.redemptionReceiptId === redemptionReceipt.value.receipt.receiptId));
|
|
1651
|
-
if (
|
|
1674
|
+
if (member?.value.status !== 'active'
|
|
1652
1675
|
|| recovery === undefined
|
|
1676
|
+
|| Date.parse(member.value.createdAt) > Date.parse(recovery.value.createdAt)
|
|
1653
1677
|
|| recovery.value.sourceAuthority.kind !== 'lan'
|
|
1654
1678
|
|| claim.value.memberId === recovery.value.sourceHostMemberId
|
|
1655
1679
|
|| lifecycle?.value.batchRevision !== claim.value.batchRevision
|
|
@@ -1832,10 +1856,12 @@ function validateContinuity(records) {
|
|
|
1832
1856
|
const identity = `${item.value.transferId}:${item.value.memberId}`;
|
|
1833
1857
|
const recovery = recoveries.get(item.value.transferId);
|
|
1834
1858
|
const lifecycle = lifecycles.get(item.value.transferId);
|
|
1859
|
+
const member = memberRecords.get(item.value.memberId);
|
|
1835
1860
|
if (item.recordId !== identity
|
|
1836
1861
|
|| recovery === undefined
|
|
1837
1862
|
|| lifecycle === undefined
|
|
1838
|
-
||
|
|
1863
|
+
|| member?.value.status !== 'active'
|
|
1864
|
+
|| Date.parse(member.value.createdAt) > Date.parse(recovery.value.createdAt)
|
|
1839
1865
|
|| item.value.memberId === recovery.value.targetHostMemberId
|
|
1840
1866
|
|| item.value.associatedData.authorityGeneration
|
|
1841
1867
|
!== recovery.value.sourceAuthority.generation
|
|
@@ -1510,7 +1510,10 @@ function validateContinuity(records) {
|
|
|
1510
1510
|
const recoveryEvidenceInvalid = direction === null
|
|
1511
1511
|
|| (direction === 'lan-to-cloud' && recovery.value.sourceEvidence === null)
|
|
1512
1512
|
|| (targetEvidenceRequired && recovery.value.targetEvidence === null)
|
|
1513
|
-
|| (
|
|
1513
|
+
|| (isCancellation
|
|
1514
|
+
? (lifecycle.value.batchRevision !== null)
|
|
1515
|
+
!== (recovery.value.stageSha256 !== null)
|
|
1516
|
+
: stageRequired !== (recovery.value.stageSha256 !== null))
|
|
1514
1517
|
|| (direction === 'lan-to-cloud'
|
|
1515
1518
|
&& recovery.value.stageSha256 !== null
|
|
1516
1519
|
&& recovery.value.stageSha256 !== lifecycle.value.checkpointSha256)
|
|
@@ -1533,33 +1536,51 @@ function validateContinuity(records) {
|
|
|
1533
1536
|
if (projectAuthorityGeneration !== expectedProjectAuthorityGeneration) {
|
|
1534
1537
|
throw invalidPayload('records');
|
|
1535
1538
|
}
|
|
1539
|
+
const hostMemberId = direction === 'lan-to-cloud'
|
|
1540
|
+
? recovery.value.sourceHostMemberId
|
|
1541
|
+
: recovery.value.targetHostMemberId;
|
|
1542
|
+
const transferMemberIds = [...memberRecords.values()]
|
|
1543
|
+
.filter(item => (item.value.status === 'active'
|
|
1544
|
+
&& item.value.memberId !== hostMemberId
|
|
1545
|
+
&& Date.parse(item.value.createdAt) <= Date.parse(recovery.value.createdAt)))
|
|
1546
|
+
.map(item => item.value.memberId);
|
|
1536
1547
|
if (!isCancellation) {
|
|
1537
|
-
const hostMemberId = direction === 'lan-to-cloud'
|
|
1538
|
-
? recovery.value.sourceHostMemberId
|
|
1539
|
-
: recovery.value.targetHostMemberId;
|
|
1540
|
-
const eligibleMemberIds = [...memberRecords.values()]
|
|
1541
|
-
.filter(item => item.value.status === 'active' && item.value.memberId !== hostMemberId)
|
|
1542
|
-
.map(item => item.value.memberId);
|
|
1543
|
-
const memberCustodyRequired = normalPhaseIndex >= 3;
|
|
1544
1548
|
const batchReceiptRequired = direction === 'cloud-to-lan'
|
|
1545
1549
|
? normalPhaseIndex >= 3
|
|
1546
1550
|
: normalPhaseIndex >= 4;
|
|
1547
1551
|
if ((batchReceipt !== undefined) !== batchReceiptRequired) {
|
|
1548
1552
|
throw invalidPayload('records');
|
|
1549
1553
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1554
|
+
}
|
|
1555
|
+
const memberCustody = transferMemberIds.map((memberId) => {
|
|
1556
|
+
const identity = `${recovery.value.transferId}:${memberId}`;
|
|
1557
|
+
const claimPresent = claims.has(identity);
|
|
1558
|
+
const envelopePresent = protectedEnvelopes.has(identity);
|
|
1559
|
+
const redemptionPresent = redemptionReceipts.has(identity);
|
|
1560
|
+
return {
|
|
1561
|
+
absent: !claimPresent && !envelopePresent && !redemptionPresent,
|
|
1562
|
+
exact: direction === 'lan-to-cloud'
|
|
1556
1563
|
? claimPresent && !envelopePresent
|
|
1557
|
-
: !claimPresent && envelopePresent !== redemptionPresent
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1564
|
+
: !claimPresent && envelopePresent !== redemptionPresent,
|
|
1565
|
+
};
|
|
1566
|
+
});
|
|
1567
|
+
const cleanupInProgress = isCancellation
|
|
1568
|
+
&& cancellationIndex === 1
|
|
1569
|
+
&& lifecycle.value.batchRevision !== null;
|
|
1570
|
+
if (cleanupInProgress) {
|
|
1571
|
+
const allAbsent = memberCustody.every(item => item.absent);
|
|
1572
|
+
const allExact = memberCustody.every(item => item.exact);
|
|
1573
|
+
if (!allAbsent && !allExact) {
|
|
1574
|
+
throw invalidPayload('records');
|
|
1561
1575
|
}
|
|
1562
1576
|
}
|
|
1577
|
+
else {
|
|
1578
|
+
const memberCustodyRequired = isCancellation
|
|
1579
|
+
? !cleanupCompleted && lifecycle.value.batchRevision !== null
|
|
1580
|
+
: normalPhaseIndex >= 3;
|
|
1581
|
+
if (memberCustody.some(item => (memberCustodyRequired ? !item.exact : !item.absent)))
|
|
1582
|
+
throw invalidPayload('records');
|
|
1583
|
+
}
|
|
1563
1584
|
if (recovery.value.targetEvidence !== null && (recovery.value.targetHostMemberId === null
|
|
1564
1585
|
|| principalBindings.get(recovery.value.targetHostMemberId)
|
|
1565
1586
|
!== recovery.value.targetEvidence.principalId))
|
|
@@ -1575,6 +1596,7 @@ function validateContinuity(records) {
|
|
|
1575
1596
|
? 'cloud-to-lan'
|
|
1576
1597
|
: 'lan-to-cloud')
|
|
1577
1598
|
|| lifecycle.value.expectedAuthorityGeneration !== recovery.value.sourceAuthority.generation
|
|
1599
|
+
|| lifecycle.value.createdAt !== recovery.value.createdAt
|
|
1578
1600
|
|| lifecycle.value.scheduledAt !== recovery.value.expiresAt
|
|
1579
1601
|
|| (recovery.value.sourceHostMemberId !== null
|
|
1580
1602
|
&& !members.has(recovery.value.sourceHostMemberId))
|
|
@@ -1630,6 +1652,7 @@ function validateContinuity(records) {
|
|
|
1630
1652
|
for (const claim of claims.values()) {
|
|
1631
1653
|
const lifecycle = lifecycles.get(claim.value.transferId);
|
|
1632
1654
|
const recovery = recoveries.get(claim.value.transferId);
|
|
1655
|
+
const member = memberRecords.get(claim.value.memberId);
|
|
1633
1656
|
const redemptionReceipt = redemptionReceipts.get(`${claim.value.transferId}:${claim.value.memberId}`);
|
|
1634
1657
|
const redeemedOverride = redemptionReceipt === undefined
|
|
1635
1658
|
? undefined
|
|
@@ -1638,8 +1661,9 @@ function validateContinuity(records) {
|
|
|
1638
1661
|
&& item.value.state === 'redeemed'
|
|
1639
1662
|
&& item.value.claimSha256 === redemptionReceipt.value.receipt.claimSha256
|
|
1640
1663
|
&& item.value.redemptionReceiptId === redemptionReceipt.value.receipt.receiptId));
|
|
1641
|
-
if (
|
|
1664
|
+
if (member?.value.status !== 'active'
|
|
1642
1665
|
|| recovery === undefined
|
|
1666
|
+
|| Date.parse(member.value.createdAt) > Date.parse(recovery.value.createdAt)
|
|
1643
1667
|
|| recovery.value.sourceAuthority.kind !== 'lan'
|
|
1644
1668
|
|| claim.value.memberId === recovery.value.sourceHostMemberId
|
|
1645
1669
|
|| lifecycle?.value.batchRevision !== claim.value.batchRevision
|
|
@@ -1822,10 +1846,12 @@ function validateContinuity(records) {
|
|
|
1822
1846
|
const identity = `${item.value.transferId}:${item.value.memberId}`;
|
|
1823
1847
|
const recovery = recoveries.get(item.value.transferId);
|
|
1824
1848
|
const lifecycle = lifecycles.get(item.value.transferId);
|
|
1849
|
+
const member = memberRecords.get(item.value.memberId);
|
|
1825
1850
|
if (item.recordId !== identity
|
|
1826
1851
|
|| recovery === undefined
|
|
1827
1852
|
|| lifecycle === undefined
|
|
1828
|
-
||
|
|
1853
|
+
|| member?.value.status !== 'active'
|
|
1854
|
+
|| Date.parse(member.value.createdAt) > Date.parse(recovery.value.createdAt)
|
|
1829
1855
|
|| item.value.memberId === recovery.value.targetHostMemberId
|
|
1830
1856
|
|| item.value.associatedData.authorityGeneration
|
|
1831
1857
|
!== recovery.value.sourceAuthority.generation
|