jsgar 4.13.6 → 4.14.0
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/gar.umd.js +39 -10
- package/gar.js +39 -10
- package/package.json +1 -1
package/dist/gar.umd.js
CHANGED
|
@@ -212,7 +212,10 @@
|
|
|
212
212
|
}
|
|
213
213
|
this.unique_key_and_record_updates = uniqueKeyAndRecordUpdates;
|
|
214
214
|
this.logEveryMessage = logEveryMessage;
|
|
215
|
-
this.version =
|
|
215
|
+
this.version = 650711;
|
|
216
|
+
// Monotonic correlation id stamped inline on each compare-exchange (required protocol field); the
|
|
217
|
+
// result echoes it. Correlation no longer rides the active_ownership marker — see compare_exchange.md.
|
|
218
|
+
this._casMessageReference = 0;
|
|
216
219
|
this.uuid = GARClient._generateUUID();
|
|
217
220
|
|
|
218
221
|
if (typeof window !== 'undefined' && window.location) {
|
|
@@ -229,6 +232,10 @@
|
|
|
229
232
|
this.serverKeyNameToId = new Map();
|
|
230
233
|
this.localTopicMap = new Map();
|
|
231
234
|
this.localKeyMap = new Map();
|
|
235
|
+
// Classes each locally-introduced key has been introduced under, so a later
|
|
236
|
+
// getAndPossiblyIntroduceKeyId with a NEW class sends an additive KeyIntroduction (the server's
|
|
237
|
+
// class_list is additive) rather than silently no-op'ing. Keyed by key name -> Set<className>.
|
|
238
|
+
this.localKeyClasses = new Map();
|
|
232
239
|
this.invalidatedKeyIds = new Set();
|
|
233
240
|
this.recordMap = new Map();
|
|
234
241
|
this._activeOwnership = { msg_ref: 0, ownership_action: 'None', skip_ownership_checks: false, require_existing: false, client_key_id: 0 };
|
|
@@ -296,6 +303,7 @@
|
|
|
296
303
|
this.localKeyCounter = 1;
|
|
297
304
|
this.localTopicMap.clear();
|
|
298
305
|
this.localKeyMap.clear();
|
|
306
|
+
this.localKeyClasses.clear();
|
|
299
307
|
this.invalidatedKeyIds.clear();
|
|
300
308
|
|
|
301
309
|
// Active ownership state
|
|
@@ -1504,15 +1512,30 @@
|
|
|
1504
1512
|
if (existingId !== undefined && this.invalidatedKeyIds.has(existingId)) {
|
|
1505
1513
|
this.invalidatedKeyIds.delete(existingId);
|
|
1506
1514
|
this.localKeyMap.delete(name);
|
|
1515
|
+
this.localKeyClasses.delete(name);
|
|
1507
1516
|
}
|
|
1517
|
+
const newClasses = typeof className === 'string'
|
|
1518
|
+
? className.split(/\s+/).filter(Boolean)
|
|
1519
|
+
: (Array.isArray(className) ? className.filter(Boolean) : []);
|
|
1508
1520
|
if (!this.localKeyMap.has(name)) {
|
|
1509
|
-
if (!
|
|
1521
|
+
if (!newClasses.length) throw new Error(`getAndPossiblyIntroduceKeyId: className required to introduce key "${name}"`);
|
|
1510
1522
|
const keyId = this.localKeyCounter++;
|
|
1511
1523
|
this.localKeyMap.set(name, keyId);
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1524
|
+
this.localKeyClasses.set(name, new Set(newClasses));
|
|
1525
|
+
this.sendMessage({ message_type: 'KeyIntroduction', value: { key_id: keyId, name, class_list: newClasses } });
|
|
1526
|
+
} else {
|
|
1527
|
+
// Already introduced: add any classes the key isn't yet a member of via an additive
|
|
1528
|
+
// KeyIntroduction (same key_id, only the new classes — the server's class_list adds to the
|
|
1529
|
+
// existing membership). Without this, a key first introduced under one class never gains a
|
|
1530
|
+
// second class on a later call, and a write/CAS on a topic of the new class is rejected
|
|
1531
|
+
// server-side ("topic X does not apply to key of class ...").
|
|
1532
|
+
let known = this.localKeyClasses.get(name);
|
|
1533
|
+
if (!known) { known = new Set(); this.localKeyClasses.set(name, known); }
|
|
1534
|
+
const added = newClasses.filter(c => !known.has(c));
|
|
1535
|
+
if (added.length) {
|
|
1536
|
+
added.forEach(c => known.add(c));
|
|
1537
|
+
this.sendMessage({ message_type: 'KeyIntroduction', value: { key_id: this.localKeyMap.get(name), name, class_list: added } });
|
|
1538
|
+
}
|
|
1516
1539
|
}
|
|
1517
1540
|
return this.localKeyMap.get(name);
|
|
1518
1541
|
}
|
|
@@ -1566,10 +1589,14 @@
|
|
|
1566
1589
|
const keyId = this.localKeyMap.get(keyName);
|
|
1567
1590
|
if (keyId === undefined) return;
|
|
1568
1591
|
const value = { key_id: keyId, name: keyName, deleted_class: className };
|
|
1592
|
+
const known = this.localKeyClasses.get(keyName);
|
|
1593
|
+
if (known) known.delete(className);
|
|
1569
1594
|
if (downgradeToClassList) {
|
|
1570
|
-
|
|
1595
|
+
const downgrade = typeof downgradeToClassList === 'string'
|
|
1571
1596
|
? downgradeToClassList.split(/\s+/).filter(Boolean)
|
|
1572
|
-
: downgradeToClassList;
|
|
1597
|
+
: downgradeToClassList.filter(Boolean);
|
|
1598
|
+
value.class_list = downgrade;
|
|
1599
|
+
if (known) downgrade.forEach(c => known.add(c));
|
|
1573
1600
|
}
|
|
1574
1601
|
this.sendMessage({ message_type: 'KeyIntroduction', value });
|
|
1575
1602
|
}
|
|
@@ -1591,8 +1618,9 @@
|
|
|
1591
1618
|
const keyId = this.localKeyMap.get(keyName);
|
|
1592
1619
|
if (keyId === undefined) return;
|
|
1593
1620
|
this.publishDeleteKey(keyId);
|
|
1594
|
-
// Forget the local id so future re-introduction allocates a new one.
|
|
1621
|
+
// Forget the local id (and its class set) so future re-introduction allocates a new one.
|
|
1595
1622
|
this.localKeyMap.delete(keyName);
|
|
1623
|
+
this.localKeyClasses.delete(keyName);
|
|
1596
1624
|
}
|
|
1597
1625
|
|
|
1598
1626
|
/**
|
|
@@ -1674,9 +1702,10 @@
|
|
|
1674
1702
|
* @param {any} value - New value to set on match
|
|
1675
1703
|
*/
|
|
1676
1704
|
compareExchangeRecordWithIds(keyId, topicId, test, value) {
|
|
1705
|
+
const msgRef = ++this._casMessageReference;
|
|
1677
1706
|
this.sendMessage({
|
|
1678
1707
|
message_type: 'JSONCompareExchange',
|
|
1679
|
-
value: { record_id: { key_id: keyId, topic_id: topicId }, test, value }
|
|
1708
|
+
value: { record_id: { key_id: keyId, topic_id: topicId }, msg_ref: msgRef, test, value }
|
|
1680
1709
|
});
|
|
1681
1710
|
}
|
|
1682
1711
|
|
package/gar.js
CHANGED
|
@@ -205,7 +205,10 @@ class GARClient {
|
|
|
205
205
|
}
|
|
206
206
|
this.unique_key_and_record_updates = uniqueKeyAndRecordUpdates;
|
|
207
207
|
this.logEveryMessage = logEveryMessage;
|
|
208
|
-
this.version =
|
|
208
|
+
this.version = 650711;
|
|
209
|
+
// Monotonic correlation id stamped inline on each compare-exchange (required protocol field); the
|
|
210
|
+
// result echoes it. Correlation no longer rides the active_ownership marker — see compare_exchange.md.
|
|
211
|
+
this._casMessageReference = 0;
|
|
209
212
|
this.uuid = GARClient._generateUUID();
|
|
210
213
|
|
|
211
214
|
if (typeof window !== 'undefined' && window.location) {
|
|
@@ -222,6 +225,10 @@ class GARClient {
|
|
|
222
225
|
this.serverKeyNameToId = new Map();
|
|
223
226
|
this.localTopicMap = new Map();
|
|
224
227
|
this.localKeyMap = new Map();
|
|
228
|
+
// Classes each locally-introduced key has been introduced under, so a later
|
|
229
|
+
// getAndPossiblyIntroduceKeyId with a NEW class sends an additive KeyIntroduction (the server's
|
|
230
|
+
// class_list is additive) rather than silently no-op'ing. Keyed by key name -> Set<className>.
|
|
231
|
+
this.localKeyClasses = new Map();
|
|
225
232
|
this.invalidatedKeyIds = new Set();
|
|
226
233
|
this.recordMap = new Map();
|
|
227
234
|
this._activeOwnership = { msg_ref: 0, ownership_action: 'None', skip_ownership_checks: false, require_existing: false, client_key_id: 0 };
|
|
@@ -289,6 +296,7 @@ class GARClient {
|
|
|
289
296
|
this.localKeyCounter = 1;
|
|
290
297
|
this.localTopicMap.clear();
|
|
291
298
|
this.localKeyMap.clear();
|
|
299
|
+
this.localKeyClasses.clear();
|
|
292
300
|
this.invalidatedKeyIds.clear();
|
|
293
301
|
|
|
294
302
|
// Active ownership state
|
|
@@ -1497,15 +1505,30 @@ class GARClient {
|
|
|
1497
1505
|
if (existingId !== undefined && this.invalidatedKeyIds.has(existingId)) {
|
|
1498
1506
|
this.invalidatedKeyIds.delete(existingId);
|
|
1499
1507
|
this.localKeyMap.delete(name);
|
|
1508
|
+
this.localKeyClasses.delete(name);
|
|
1500
1509
|
}
|
|
1510
|
+
const newClasses = typeof className === 'string'
|
|
1511
|
+
? className.split(/\s+/).filter(Boolean)
|
|
1512
|
+
: (Array.isArray(className) ? className.filter(Boolean) : []);
|
|
1501
1513
|
if (!this.localKeyMap.has(name)) {
|
|
1502
|
-
if (!
|
|
1514
|
+
if (!newClasses.length) throw new Error(`getAndPossiblyIntroduceKeyId: className required to introduce key "${name}"`);
|
|
1503
1515
|
const keyId = this.localKeyCounter++;
|
|
1504
1516
|
this.localKeyMap.set(name, keyId);
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1517
|
+
this.localKeyClasses.set(name, new Set(newClasses));
|
|
1518
|
+
this.sendMessage({ message_type: 'KeyIntroduction', value: { key_id: keyId, name, class_list: newClasses } });
|
|
1519
|
+
} else {
|
|
1520
|
+
// Already introduced: add any classes the key isn't yet a member of via an additive
|
|
1521
|
+
// KeyIntroduction (same key_id, only the new classes — the server's class_list adds to the
|
|
1522
|
+
// existing membership). Without this, a key first introduced under one class never gains a
|
|
1523
|
+
// second class on a later call, and a write/CAS on a topic of the new class is rejected
|
|
1524
|
+
// server-side ("topic X does not apply to key of class ...").
|
|
1525
|
+
let known = this.localKeyClasses.get(name);
|
|
1526
|
+
if (!known) { known = new Set(); this.localKeyClasses.set(name, known); }
|
|
1527
|
+
const added = newClasses.filter(c => !known.has(c));
|
|
1528
|
+
if (added.length) {
|
|
1529
|
+
added.forEach(c => known.add(c));
|
|
1530
|
+
this.sendMessage({ message_type: 'KeyIntroduction', value: { key_id: this.localKeyMap.get(name), name, class_list: added } });
|
|
1531
|
+
}
|
|
1509
1532
|
}
|
|
1510
1533
|
return this.localKeyMap.get(name);
|
|
1511
1534
|
}
|
|
@@ -1559,10 +1582,14 @@ class GARClient {
|
|
|
1559
1582
|
const keyId = this.localKeyMap.get(keyName);
|
|
1560
1583
|
if (keyId === undefined) return;
|
|
1561
1584
|
const value = { key_id: keyId, name: keyName, deleted_class: className };
|
|
1585
|
+
const known = this.localKeyClasses.get(keyName);
|
|
1586
|
+
if (known) known.delete(className);
|
|
1562
1587
|
if (downgradeToClassList) {
|
|
1563
|
-
|
|
1588
|
+
const downgrade = typeof downgradeToClassList === 'string'
|
|
1564
1589
|
? downgradeToClassList.split(/\s+/).filter(Boolean)
|
|
1565
|
-
: downgradeToClassList;
|
|
1590
|
+
: downgradeToClassList.filter(Boolean);
|
|
1591
|
+
value.class_list = downgrade;
|
|
1592
|
+
if (known) downgrade.forEach(c => known.add(c));
|
|
1566
1593
|
}
|
|
1567
1594
|
this.sendMessage({ message_type: 'KeyIntroduction', value });
|
|
1568
1595
|
}
|
|
@@ -1584,8 +1611,9 @@ class GARClient {
|
|
|
1584
1611
|
const keyId = this.localKeyMap.get(keyName);
|
|
1585
1612
|
if (keyId === undefined) return;
|
|
1586
1613
|
this.publishDeleteKey(keyId);
|
|
1587
|
-
// Forget the local id so future re-introduction allocates a new one.
|
|
1614
|
+
// Forget the local id (and its class set) so future re-introduction allocates a new one.
|
|
1588
1615
|
this.localKeyMap.delete(keyName);
|
|
1616
|
+
this.localKeyClasses.delete(keyName);
|
|
1589
1617
|
}
|
|
1590
1618
|
|
|
1591
1619
|
/**
|
|
@@ -1667,9 +1695,10 @@ class GARClient {
|
|
|
1667
1695
|
* @param {any} value - New value to set on match
|
|
1668
1696
|
*/
|
|
1669
1697
|
compareExchangeRecordWithIds(keyId, topicId, test, value) {
|
|
1698
|
+
const msgRef = ++this._casMessageReference;
|
|
1670
1699
|
this.sendMessage({
|
|
1671
1700
|
message_type: 'JSONCompareExchange',
|
|
1672
|
-
value: { record_id: { key_id: keyId, topic_id: topicId }, test, value }
|
|
1701
|
+
value: { record_id: { key_id: keyId, topic_id: topicId }, msg_ref: msgRef, test, value }
|
|
1673
1702
|
});
|
|
1674
1703
|
}
|
|
1675
1704
|
|