jsgar 2.2.2 → 3.1.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.
Files changed (2) hide show
  1. package/dist/gar.umd.js +182 -42
  2. package/package.json +1 -1
package/dist/gar.umd.js CHANGED
@@ -50,7 +50,7 @@
50
50
  this.user = user;
51
51
  this.working_namespace = working_namespace;
52
52
  this.heartbeatTimeoutInterval = heartbeatTimeoutInterval;
53
- this.version = 650705;
53
+ this.version = 650706;
54
54
 
55
55
  if (typeof window !== 'undefined' && window.location) {
56
56
  this.application = window.location.href;
@@ -343,6 +343,19 @@
343
343
  }, subscriptionGroup);
344
344
  }
345
345
 
346
+ /**
347
+ * Register handler for BatchUpdate message.
348
+ * If a batch handler is registered it is expected to process all the updates in the batch.
349
+ * If no batch handler is registered, individual key introductions and record updates will be fanned out to their respective handlers.
350
+ * @param {Function} handler - Callback with (batchData, subscriptionGroup)
351
+ * @param {number} [subscriptionGroup=0] - The subscription group for callback
352
+ */
353
+ registerBatchUpdateHandler(handler, subscriptionGroup = 0) {
354
+ this.registerHandler('BatchUpdate', (msg) => {
355
+ handler(msg.value, subscriptionGroup);
356
+ }, subscriptionGroup);
357
+ }
358
+
346
359
  /**
347
360
  * Register a callback to handle heartbeat timeout events.
348
361
  * @param {Function} handler - Callback with no arguments
@@ -569,6 +582,84 @@
569
582
  subscriptionGroup = this.activeSubscriptionGroup;
570
583
  const {key_id: keyId, topic_id: topicId} = message.value;
571
584
  this.recordMap.delete(`${keyId}:${topicId}`);
585
+ } else if (msgType === 'BatchUpdate') {
586
+ subscriptionGroup = this.activeSubscriptionGroup;
587
+ const value = message.value;
588
+ const defaultClass = value.default_class;
589
+
590
+ // Check if there's a specific batch update handler
591
+ const batchHandlerKey = subscriptionGroup ? `BatchUpdate ${subscriptionGroup}` : 'BatchUpdate';
592
+ const hasBatchHandler = this.messageHandlers.has(batchHandlerKey);
593
+
594
+ // Pre-check for individual handlers if no batch handler
595
+ let keyHandler = null;
596
+ let recordHandler = null;
597
+ if (!hasBatchHandler) {
598
+ const keyHandlerKey = subscriptionGroup ? `KeyIntroduction ${subscriptionGroup}` : 'KeyIntroduction';
599
+ keyHandler = this.messageHandlers.get(keyHandlerKey);
600
+
601
+ const recordHandlerKey = subscriptionGroup ? `JSONRecordUpdate ${subscriptionGroup}` : 'JSONRecordUpdate';
602
+ recordHandler = this.messageHandlers.get(recordHandlerKey);
603
+ }
604
+
605
+ for (const keyUpdate of value.keys || []) {
606
+ const keyId = keyUpdate.key_id;
607
+ const keyName = keyUpdate.name;
608
+
609
+ // Handle key introduction if name is provided and key is new
610
+ if (keyName && !this.serverKeyIdToName.has(keyId)) {
611
+ this.serverKeyIdToName.set(keyId, keyName);
612
+ this.serverKeyNameToId.set(keyName, keyId);
613
+
614
+ // If no batch handler but key handler exists, call KeyIntroduction handler
615
+ if (!hasBatchHandler && keyHandler) {
616
+ // Determine class_list: use key's classes, or default_class, or null
617
+ let keyClasses = keyUpdate.classes;
618
+ if (!keyClasses && keyUpdate.class) {
619
+ keyClasses = [keyUpdate.class];
620
+ } else if (!keyClasses && defaultClass) {
621
+ keyClasses = [defaultClass];
622
+ }
623
+
624
+ const keyIntroMsg = {
625
+ message_type: 'KeyIntroduction',
626
+ value: {
627
+ key_id: keyId,
628
+ name: keyName,
629
+ ...(keyClasses ? { class_list: keyClasses } : {})
630
+ }
631
+ };
632
+ keyHandler(keyIntroMsg);
633
+ }
634
+ }
635
+
636
+ // Process topics for this key - topic IDs are object keys
637
+ const topicsDict = keyUpdate.topics || {};
638
+ for (const [topicIdStr, recordValue] of Object.entries(topicsDict)) {
639
+ const topicId = parseInt(topicIdStr, 10);
640
+ this.recordMap.set(`${keyId}:${topicId}`, recordValue);
641
+
642
+ // If no batch handler but record handler exists, call JSONRecordUpdate handler
643
+ if (!hasBatchHandler && recordHandler) {
644
+ const recordUpdateMsg = {
645
+ message_type: 'JSONRecordUpdate',
646
+ value: {
647
+ record_id: { key_id: keyId, topic_id: topicId },
648
+ value: recordValue
649
+ }
650
+ };
651
+ recordHandler(recordUpdateMsg);
652
+ }
653
+ }
654
+ }
655
+
656
+ // If there is a batch handler, call it
657
+ if (hasBatchHandler) {
658
+ const batchHandler = this.messageHandlers.get(batchHandlerKey);
659
+ if (batchHandler) {
660
+ batchHandler(message);
661
+ }
662
+ }
572
663
  } else if (msgType === "ActiveSubscription") {
573
664
  this.activeSubscriptionGroup = message["value"]["subscription_group"];
574
665
  } else if (msgType === 'Logoff') {
@@ -589,58 +680,62 @@
589
680
  }
590
681
 
591
682
  /**
592
- * Send a subscription request using local IDs.
683
+ * Send an already-formatted subscription message
684
+ * @param {object} subscriptionMessageValue - json representation of the gar `subscribe` struct
685
+ */
686
+ subscribeFormatted(subscriptionMessageValue) {
687
+ const subMsg = {
688
+ message_type: 'Subscribe',
689
+ value: subscriptionMessageValue,
690
+ };
691
+ this.sendMessage(subMsg);
692
+ }
693
+
694
+ /**
695
+ * Send a subscription request
593
696
  * @param {string} name - Subscription name
594
697
  * @param {string} [subscriptionMode='Streaming'] - Subscription mode
595
- * @param {string} [mode] - Deprecated: use subscriptionMode instead
596
- * @param {string} [subscriptionSet] - Subscription set identifier
597
- * @param {string} [maxHistory] - Maximum history to include
598
- * @param {number} [snapshotSizeLimit=0] - Limit snapshot size
599
- * @param {number} [nagleInterval=0] - Nagle interval in milliseconds
600
- * @param {number} [subscriptionGroup=0] - Subscription group ID for isolating callbacks
601
- * @param {string} [density] - For performance tuning
602
- * @param {boolean} [includeReferencedKeys=false] - Add keys from key references in matched records
603
- * @param {boolean} [includeReferencingKeys=false] - Add keys that have one or more records referencing any matched keys
604
- * @param {boolean} [includeAllNamespace=false] - Include keys and topics from all namespaces
605
- * @param {number} [limit=0] - Limits records in initial snapshot (0 = all)
606
698
  * @param {string|Array<string>|null} [keyName=null] - Key name(s)
607
699
  * @param {string|Array<string>|null} [topicName=null] - Topic name(s)
608
700
  * @param {string|Array<string>|null} [className=null] - Class name(s)
609
701
  * @param {string|null} [keyFilter=null] - Key filter regex (cannot use with keyName)
610
- * @param {string|null} [excludeKeyFilter=null] - Exclude key filter regex (cannot use with keyName)
611
702
  * @param {string|null} [topicFilter=null] - Topic filter regex (cannot use with topicName)
703
+ * @param {string|null} [excludeKeyFilter=null] - Exclude key filter regex (cannot use with keyName)
612
704
  * @param {string|null} [excludeTopicFilter=null] - Exclude topic filter regex (cannot use with topicName)
613
- * @param {string} [workingNamespace] - Namespace for matching relative paths
705
+ * @param {string|null} [maxHistory] - Maximum history to include
706
+ * @param {boolean} [includeReferencedKeys=false] - Add keys from key references in matched records
707
+ * @param {boolean} [includeReferencingKeys=false] - Add keys that have one or more records referencing any matched keys
708
+ * @param {boolean} [includeAllNamespace=false] - Include keys and topics from all namespaces
709
+ * @param {string|null} [workingNamespace] - Namespace for matching relative paths
710
+ * @param {string|null} [density] - For performance tuning
711
+ * @param {number} [subscriptionGroup=0] - Subscription group ID for isolating callbacks
712
+ * @param {string|null} [subscriptionSet] - Subscription set identifier
713
+ * @param {number} [snapshotSizeLimit=0] - Limit snapshot size
714
+ * @param {number} [nagleInterval=0] - Nagle interval in milliseconds
715
+ * @param {number} [limit=0] - Limits records in initial snapshot (0 = all)
614
716
  */
615
717
  subscribe(
616
718
  name,
617
719
  subscriptionMode = 'Streaming',
618
- mode = null,
619
- subscriptionSet = null,
620
- maxHistory = null,
621
- snapshotSizeLimit = 0,
622
- nagleInterval = 0,
623
- subscriptionGroup = 0,
624
- density = null,
625
- includeReferencedKeys = false,
626
- includeReferencingKeys = false,
627
- includeAllNamespace = false,
628
- limit = 0,
629
720
  keyName = null,
630
721
  topicName = null,
631
722
  className = null,
632
723
  keyFilter = null,
633
- excludeKeyFilter = null,
634
724
  topicFilter = null,
725
+ excludeKeyFilter = null,
635
726
  excludeTopicFilter = null,
636
- workingNamespace = null
727
+ maxHistory = null,
728
+ includeReferencedKeys = false,
729
+ includeReferencingKeys = false,
730
+ includeAllNamespace = false,
731
+ workingNamespace = null,
732
+ density = null,
733
+ subscriptionGroup = 0,
734
+ subscriptionSet = null,
735
+ snapshotSizeLimit = 0,
736
+ nagleInterval = 0,
737
+ limit = 0
637
738
  ) {
638
- // Handle deprecated mode parameter
639
- if (mode !== null) {
640
- console.warn('The "mode" parameter is deprecated. Use "subscriptionMode" instead.');
641
- subscriptionMode = mode;
642
- }
643
-
644
739
  // Validate mutually exclusive parameters
645
740
  if (keyName && (keyFilter || excludeKeyFilter)) {
646
741
  throw new Error('keyName cannot be used with keyFilter or excludeKeyFilter');
@@ -690,10 +785,10 @@
690
785
  };
691
786
 
692
787
  // Add optional fields only if they have values
693
- if (subscriptionSet !== null) {
788
+ if (subscriptionSet) {
694
789
  valueDict.subscription_set = subscriptionSet;
695
790
  }
696
- if (maxHistory !== null) {
791
+ if (maxHistory) {
697
792
  valueDict.max_history = maxHistory;
698
793
  }
699
794
  if (snapshotSizeLimit > 0) {
@@ -705,7 +800,7 @@
705
800
  if (subscriptionGroup > 0) {
706
801
  valueDict.subscription_group = subscriptionGroup;
707
802
  }
708
- if (density !== null) {
803
+ if (density) {
709
804
  valueDict.density = density;
710
805
  }
711
806
  if (includeReferencedKeys) {
@@ -745,11 +840,56 @@
745
840
  valueDict.working_namespace = workingNamespace;
746
841
  }
747
842
 
748
- const subMsg = {
749
- message_type: 'Subscribe',
750
- value: valueDict,
751
- };
752
- this.sendMessage(subMsg);
843
+ this.subscribeFormatted(valueDict);
844
+ }
845
+
846
+ /**
847
+ * Wrapper to `subscribe` with a smaller set of arguments.
848
+ * @param {string} name - Subscription name
849
+ * @param {string} [subscriptionMode='Streaming'] - Subscription mode
850
+ * @param {number} [subscriptionGroup=0] - Subscription group ID for isolating callbacks
851
+ * @param {number} [snapshotSizeLimit=0] - Limit snapshot size
852
+ * @param {string|Array<string>|null} [keyName=null] - Key name(s)
853
+ * @param {string|Array<string>|null} [topicName=null] - Topic name(s)
854
+ * @param {string|Array<string>|null} [className=null] - Class name(s)
855
+ * @param {string|null} [keyFilter=null] - Key filter regex (cannot use with keyName)
856
+ * @param {string|null} [topicFilter=null] - Topic filter regex (cannot use with topicName)
857
+ * @param {string|null} [excludeKeyFilter=null] - Exclude key filter regex (cannot use with keyName)
858
+ * @param {string|null} [excludeTopicFilter=null] - Exclude topic filter regex (cannot use with topicName)
859
+ */
860
+ subscribeCommon(
861
+ name,
862
+ subscriptionMode = 'Streaming',
863
+ subscriptionGroup = 0,
864
+ snapshotSizeLimit = 0,
865
+ keyName = null,
866
+ topicName = null,
867
+ className = null,
868
+ keyFilter = null,
869
+ topicFilter = null,
870
+ excludeKeyFilter = null,
871
+ excludeTopicFilter = null,
872
+ ) {
873
+ this.subscribe(
874
+ name,
875
+ subscriptionMode,
876
+ keyName,
877
+ topicName,
878
+ className,
879
+ keyFilter,
880
+ topicFilter,
881
+ excludeKeyFilter,
882
+ excludeTopicFilter,
883
+ null,
884
+ false,
885
+ false,
886
+ false,
887
+ null,
888
+ null,
889
+ subscriptionGroup,
890
+ null,
891
+ snapshotSizeLimit
892
+ );
753
893
  }
754
894
 
755
895
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsgar",
3
- "version": "2.2.2",
3
+ "version": "3.1.0",
4
4
  "description": "A Javascript client for the GAR protocol",
5
5
  "type": "module",
6
6
  "main": "dist/gar.umd.js",