jsgar 4.10.0 → 4.13.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 (3) hide show
  1. package/dist/gar.umd.js +100 -47
  2. package/gar.js +99 -47
  3. package/package.json +1 -1
package/dist/gar.umd.js CHANGED
@@ -4,6 +4,7 @@
4
4
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.GARClient = {}));
5
5
  })(this, (function (exports) { 'use strict';
6
6
 
7
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
7
8
  /**
8
9
  * A client implementation for the Generic Active Records (GAR) protocol using WebSockets.
9
10
  * See trsgar.js for example usage.
@@ -805,25 +806,27 @@
805
806
 
806
807
  async _ensureWebSocketCtor() {
807
808
  // Return a WebSocket constructor for current environment.
808
- // Prefer any existing globalThis.WebSocket (browser or pre-set in Node).
809
- if (typeof globalThis !== 'undefined' && globalThis.WebSocket) {
810
- return globalThis.WebSocket;
811
- }
812
- // Node.js: resolve 'ws' at runtime
809
+ // In Node, always prefer the 'ws' package over globalThis.WebSocket.
810
+ // Node ≥21 ships a browser-spec WebSocket on globalThis that ignores the
811
+ // third-arg options object (rejectUnauthorized, headers, agent, etc.),
812
+ // which breaks self-signed certs and custom TLS configuration.
813
813
  const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
814
814
  if (isNode) {
815
815
  try {
816
- // createRequire works in both ESM and CJS; resolves from cwd
816
+ // Anchor resolution at this module so ws is found regardless of cwd.
817
817
  const { createRequire } = await import('node:module');
818
- const req = createRequire(process.cwd() + '/');
818
+ const req = createRequire((typeof document === 'undefined' && typeof location === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : typeof document === 'undefined' ? location.href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('gar.umd.js', document.baseURI).href)));
819
819
  const mod = req('ws');
820
820
  const WS = mod.default || mod.WebSocket || mod;
821
- globalThis.WebSocket = WS;
822
821
  return WS;
823
822
  } catch {
824
823
  throw new Error("'ws' module is required in Node.js environment. Please install it: npm install ws");
825
824
  }
826
825
  }
826
+ // Browser: use the platform WebSocket.
827
+ if (typeof globalThis !== 'undefined' && globalThis.WebSocket) {
828
+ return globalThis.WebSocket;
829
+ }
827
830
  throw new Error('WebSocket constructor not found in this environment');
828
831
  }
829
832
 
@@ -1092,13 +1095,8 @@
1092
1095
  }
1093
1096
  }
1094
1097
 
1095
- // If there is a batch handler, call it
1096
- if (hasBatchHandler) {
1097
- const batchHandler = this.messageHandlers.get(batchHandlerKey);
1098
- if (batchHandler) {
1099
- batchHandler(message);
1100
- }
1101
- }
1098
+ // If there is a batch handler the generic dispatch at the bottom of this method will
1099
+ // invoke it — do not call it explicitly here (would double-dispatch).
1102
1100
  } else if (msgType === "ActiveSubscription") {
1103
1101
  this.activeSubscriptionGroup = message["value"]["subscription_group"];
1104
1102
  } else if (msgType === 'Logoff') {
@@ -1140,9 +1138,15 @@
1140
1138
  }
1141
1139
 
1142
1140
  /**
1143
- * Send a subscription request
1144
- * @param {string} name - Subscription name
1145
- * @param {string} [subscriptionMode='Streaming'] - Subscription mode
1141
+ * Send a subscription request from a named-options object — the canonical entry point.
1142
+ * Options are passed by name, so adding a new field can never positionally misalign an
1143
+ * existing caller (the failure mode of the positional `subscribe()` shim below). Owns the
1144
+ * convenience layer: mutual-exclusion assertions, string→single-element-list coercion, and
1145
+ * keyName/topicName/className resolution into the wire `key_id_list`/`topic_id_list`/
1146
+ * `class_list`. Produces a `g::proto::subscribe` value dict and hands it to subscribeFormatted.
1147
+ * @param {Object} opts - Named subscription options (fields below).
1148
+ * @param {string} opts.name - Subscription name
1149
+ * @param {string} [opts.subscriptionMode='Streaming'] - Subscription mode
1146
1150
  * @param {string|Array<string>|null} [keyName=null] - Key name(s)
1147
1151
  * @param {string|Array<string>|null} [topicName=null] - Topic name(s)
1148
1152
  * @param {string|Array<string>|null} [className=null] - Class name(s)
@@ -1153,6 +1157,7 @@
1153
1157
  * @param {string|null} [maxHistory] - Maximum history to include
1154
1158
  * @param {boolean} [excludeDerived=false] - Exclude derived topics (derived topics are included by default)
1155
1159
  * @param {boolean} [includeHeavy=false] - Include heavy derived topics (comprehensions, array-typed, heavy functions)
1160
+ * @param {boolean} [includeExplicit=false] - Include explicit-only topics (the `explicit` attribute, defaulted on for once-gated topics; excluded by default like includeHeavy)
1156
1161
  * @param {boolean} [trimDefaultValues=false] - Trim records containing default values from the snapshot
1157
1162
  * @param {string|null} [workingNamespace] - Namespace for matching relative paths
1158
1163
  * @param {string|Array<string>|null} [restrictNamespace] - Restricts topics and keys to children of these namespaces (union). Defaults to the working namespace. Use ["::"] for root / no restriction.
@@ -1165,7 +1170,7 @@
1165
1170
  * @param {number} [limit=0] - Limits records in initial snapshot (0 = all)
1166
1171
  * @param {string|Array<string>|null} [referencingClassList=null] - Referencing class name(s); if provided, include referencing keys and records per proto.gar subscribe.referencing_class_list
1167
1172
  */
1168
- subscribe(
1173
+ subscribeOptions({
1169
1174
  name,
1170
1175
  subscriptionMode = 'Streaming',
1171
1176
  keyName = null,
@@ -1178,6 +1183,7 @@
1178
1183
  maxHistory = null,
1179
1184
  excludeDerived = false,
1180
1185
  includeHeavy = false,
1186
+ includeExplicit = false,
1181
1187
  trimDefaultValues = false,
1182
1188
  workingNamespace = null,
1183
1189
  restrictNamespace = null,
@@ -1189,7 +1195,7 @@
1189
1195
  nagleInterval = 0,
1190
1196
  limit = 0,
1191
1197
  referencingClassList = null
1192
- ) {
1198
+ } = {}) {
1193
1199
  // Validate mutually exclusive parameters
1194
1200
  if (keyName && (keyFilter || excludeKeyFilter)) {
1195
1201
  throw new Error('keyName cannot be used with keyFilter or excludeKeyFilter');
@@ -1271,6 +1277,9 @@
1271
1277
  if (includeHeavy) {
1272
1278
  valueDict.include_heavy = includeHeavy;
1273
1279
  }
1280
+ if (includeExplicit) {
1281
+ valueDict.include_explicit = includeExplicit;
1282
+ }
1274
1283
  if (restrictNamespace) {
1275
1284
  valueDict.restrict_namespace = typeof restrictNamespace === 'string' ? [restrictNamespace] : restrictNamespace;
1276
1285
  }
@@ -1314,6 +1323,47 @@
1314
1323
  this.subscribeFormatted(valueDict);
1315
1324
  }
1316
1325
 
1326
+ /**
1327
+ * Positional compatibility shim — packs its arguments by name and delegates to
1328
+ * subscribeOptions(). Prefer subscribeOptions({ ... }) in new code: its named signature makes
1329
+ * the positional-argument misalignment trap impossible. The parameter order here is frozen for
1330
+ * existing callers — never insert a new parameter mid-list; add new options to subscribeOptions.
1331
+ */
1332
+ subscribe(
1333
+ name,
1334
+ subscriptionMode = 'Streaming',
1335
+ keyName = null,
1336
+ topicName = null,
1337
+ className = null,
1338
+ keyFilter = null,
1339
+ topicFilter = null,
1340
+ excludeKeyFilter = null,
1341
+ excludeTopicFilter = null,
1342
+ maxHistory = null,
1343
+ excludeDerived = false,
1344
+ includeHeavy = false,
1345
+ includeExplicit = false,
1346
+ trimDefaultValues = false,
1347
+ workingNamespace = null,
1348
+ restrictNamespace = null,
1349
+ excludeNamespace = null,
1350
+ density = null,
1351
+ subscriptionGroup = 0,
1352
+ subscriptionSet = null,
1353
+ snapshotSizeLimit = 0,
1354
+ nagleInterval = 0,
1355
+ limit = 0,
1356
+ referencingClassList = null
1357
+ ) {
1358
+ this.subscribeOptions({
1359
+ name, subscriptionMode, keyName, topicName, className, keyFilter, topicFilter,
1360
+ excludeKeyFilter, excludeTopicFilter, maxHistory, excludeDerived, includeHeavy,
1361
+ includeExplicit, trimDefaultValues, workingNamespace, restrictNamespace, excludeNamespace,
1362
+ density, subscriptionGroup, subscriptionSet, snapshotSizeLimit, nagleInterval, limit,
1363
+ referencingClassList,
1364
+ });
1365
+ }
1366
+
1317
1367
  /**
1318
1368
  * Wrapper to `subscribe` with a smaller set of arguments.
1319
1369
  * @param {string} name - Subscription name
@@ -1351,16 +1401,17 @@
1351
1401
  topicFilter,
1352
1402
  excludeKeyFilter,
1353
1403
  excludeTopicFilter,
1354
- null,
1355
- false,
1356
- false,
1357
- false,
1358
- null,
1359
- null,
1360
- null,
1361
- null,
1404
+ null, // maxHistory
1405
+ false, // excludeDerived
1406
+ false, // includeHeavy
1407
+ false, // includeExplicit
1408
+ false, // trimDefaultValues
1409
+ null, // workingNamespace
1410
+ null, // restrictNamespace
1411
+ null, // excludeNamespace
1412
+ null, // density
1362
1413
  subscriptionGroup,
1363
- null,
1414
+ null, // subscriptionSet
1364
1415
  snapshotSizeLimit
1365
1416
  );
1366
1417
  }
@@ -1692,15 +1743,16 @@
1692
1743
  null,
1693
1744
  null,
1694
1745
  null,
1695
- null,
1696
- false,
1697
- false,
1698
- false,
1699
- 'g::deployment::Server',
1700
- null,
1701
- null,
1702
- null,
1703
- subscriptionGroupServerRecords,
1746
+ null, // maxHistory
1747
+ false, // excludeDerived
1748
+ false, // includeHeavy
1749
+ false, // includeExplicit
1750
+ false, // trimDefaultValues
1751
+ 'g::deployment::Server', // workingNamespace
1752
+ null, // restrictNamespace
1753
+ null, // excludeNamespace
1754
+ null, // density
1755
+ subscriptionGroupServerRecords, // subscriptionGroup
1704
1756
  );
1705
1757
  };
1706
1758
 
@@ -1742,15 +1794,16 @@
1742
1794
  null,
1743
1795
  null,
1744
1796
  null,
1745
- null,
1746
- false,
1747
- false,
1748
- false,
1749
- 'g::deployment::RecordFilter',
1750
- null,
1751
- null,
1752
- null,
1753
- subscriptionGroupServerKeys,
1797
+ null, // maxHistory
1798
+ false, // excludeDerived
1799
+ false, // includeHeavy
1800
+ false, // includeExplicit
1801
+ false, // trimDefaultValues
1802
+ 'g::deployment::RecordFilter', // workingNamespace
1803
+ null, // restrictNamespace
1804
+ null, // excludeNamespace
1805
+ null, // density
1806
+ subscriptionGroupServerKeys, // subscriptionGroup
1754
1807
  );
1755
1808
 
1756
1809
  // Timeout handling
package/gar.js CHANGED
@@ -799,25 +799,27 @@ class GARClient {
799
799
 
800
800
  async _ensureWebSocketCtor() {
801
801
  // Return a WebSocket constructor for current environment.
802
- // Prefer any existing globalThis.WebSocket (browser or pre-set in Node).
803
- if (typeof globalThis !== 'undefined' && globalThis.WebSocket) {
804
- return globalThis.WebSocket;
805
- }
806
- // Node.js: resolve 'ws' at runtime
802
+ // In Node, always prefer the 'ws' package over globalThis.WebSocket.
803
+ // Node ≥21 ships a browser-spec WebSocket on globalThis that ignores the
804
+ // third-arg options object (rejectUnauthorized, headers, agent, etc.),
805
+ // which breaks self-signed certs and custom TLS configuration.
807
806
  const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
808
807
  if (isNode) {
809
808
  try {
810
- // createRequire works in both ESM and CJS; resolves from cwd
809
+ // Anchor resolution at this module so ws is found regardless of cwd.
811
810
  const { createRequire } = await import('node:module');
812
- const req = createRequire(process.cwd() + '/');
811
+ const req = createRequire(import.meta.url);
813
812
  const mod = req('ws');
814
813
  const WS = mod.default || mod.WebSocket || mod;
815
- globalThis.WebSocket = WS;
816
814
  return WS;
817
815
  } catch {
818
816
  throw new Error("'ws' module is required in Node.js environment. Please install it: npm install ws");
819
817
  }
820
818
  }
819
+ // Browser: use the platform WebSocket.
820
+ if (typeof globalThis !== 'undefined' && globalThis.WebSocket) {
821
+ return globalThis.WebSocket;
822
+ }
821
823
  throw new Error('WebSocket constructor not found in this environment');
822
824
  }
823
825
 
@@ -1086,13 +1088,8 @@ class GARClient {
1086
1088
  }
1087
1089
  }
1088
1090
 
1089
- // If there is a batch handler, call it
1090
- if (hasBatchHandler) {
1091
- const batchHandler = this.messageHandlers.get(batchHandlerKey);
1092
- if (batchHandler) {
1093
- batchHandler(message);
1094
- }
1095
- }
1091
+ // If there is a batch handler the generic dispatch at the bottom of this method will
1092
+ // invoke it — do not call it explicitly here (would double-dispatch).
1096
1093
  } else if (msgType === "ActiveSubscription") {
1097
1094
  this.activeSubscriptionGroup = message["value"]["subscription_group"]
1098
1095
  } else if (msgType === 'Logoff') {
@@ -1134,9 +1131,15 @@ class GARClient {
1134
1131
  }
1135
1132
 
1136
1133
  /**
1137
- * Send a subscription request
1138
- * @param {string} name - Subscription name
1139
- * @param {string} [subscriptionMode='Streaming'] - Subscription mode
1134
+ * Send a subscription request from a named-options object — the canonical entry point.
1135
+ * Options are passed by name, so adding a new field can never positionally misalign an
1136
+ * existing caller (the failure mode of the positional `subscribe()` shim below). Owns the
1137
+ * convenience layer: mutual-exclusion assertions, string→single-element-list coercion, and
1138
+ * keyName/topicName/className resolution into the wire `key_id_list`/`topic_id_list`/
1139
+ * `class_list`. Produces a `g::proto::subscribe` value dict and hands it to subscribeFormatted.
1140
+ * @param {Object} opts - Named subscription options (fields below).
1141
+ * @param {string} opts.name - Subscription name
1142
+ * @param {string} [opts.subscriptionMode='Streaming'] - Subscription mode
1140
1143
  * @param {string|Array<string>|null} [keyName=null] - Key name(s)
1141
1144
  * @param {string|Array<string>|null} [topicName=null] - Topic name(s)
1142
1145
  * @param {string|Array<string>|null} [className=null] - Class name(s)
@@ -1147,6 +1150,7 @@ class GARClient {
1147
1150
  * @param {string|null} [maxHistory] - Maximum history to include
1148
1151
  * @param {boolean} [excludeDerived=false] - Exclude derived topics (derived topics are included by default)
1149
1152
  * @param {boolean} [includeHeavy=false] - Include heavy derived topics (comprehensions, array-typed, heavy functions)
1153
+ * @param {boolean} [includeExplicit=false] - Include explicit-only topics (the `explicit` attribute, defaulted on for once-gated topics; excluded by default like includeHeavy)
1150
1154
  * @param {boolean} [trimDefaultValues=false] - Trim records containing default values from the snapshot
1151
1155
  * @param {string|null} [workingNamespace] - Namespace for matching relative paths
1152
1156
  * @param {string|Array<string>|null} [restrictNamespace] - Restricts topics and keys to children of these namespaces (union). Defaults to the working namespace. Use ["::"] for root / no restriction.
@@ -1159,7 +1163,7 @@ class GARClient {
1159
1163
  * @param {number} [limit=0] - Limits records in initial snapshot (0 = all)
1160
1164
  * @param {string|Array<string>|null} [referencingClassList=null] - Referencing class name(s); if provided, include referencing keys and records per proto.gar subscribe.referencing_class_list
1161
1165
  */
1162
- subscribe(
1166
+ subscribeOptions({
1163
1167
  name,
1164
1168
  subscriptionMode = 'Streaming',
1165
1169
  keyName = null,
@@ -1172,6 +1176,7 @@ class GARClient {
1172
1176
  maxHistory = null,
1173
1177
  excludeDerived = false,
1174
1178
  includeHeavy = false,
1179
+ includeExplicit = false,
1175
1180
  trimDefaultValues = false,
1176
1181
  workingNamespace = null,
1177
1182
  restrictNamespace = null,
@@ -1183,7 +1188,7 @@ class GARClient {
1183
1188
  nagleInterval = 0,
1184
1189
  limit = 0,
1185
1190
  referencingClassList = null
1186
- ) {
1191
+ } = {}) {
1187
1192
  // Validate mutually exclusive parameters
1188
1193
  if (keyName && (keyFilter || excludeKeyFilter)) {
1189
1194
  throw new Error('keyName cannot be used with keyFilter or excludeKeyFilter');
@@ -1265,6 +1270,9 @@ class GARClient {
1265
1270
  if (includeHeavy) {
1266
1271
  valueDict.include_heavy = includeHeavy;
1267
1272
  }
1273
+ if (includeExplicit) {
1274
+ valueDict.include_explicit = includeExplicit;
1275
+ }
1268
1276
  if (restrictNamespace) {
1269
1277
  valueDict.restrict_namespace = typeof restrictNamespace === 'string' ? [restrictNamespace] : restrictNamespace;
1270
1278
  }
@@ -1308,6 +1316,47 @@ class GARClient {
1308
1316
  this.subscribeFormatted(valueDict);
1309
1317
  }
1310
1318
 
1319
+ /**
1320
+ * Positional compatibility shim — packs its arguments by name and delegates to
1321
+ * subscribeOptions(). Prefer subscribeOptions({ ... }) in new code: its named signature makes
1322
+ * the positional-argument misalignment trap impossible. The parameter order here is frozen for
1323
+ * existing callers — never insert a new parameter mid-list; add new options to subscribeOptions.
1324
+ */
1325
+ subscribe(
1326
+ name,
1327
+ subscriptionMode = 'Streaming',
1328
+ keyName = null,
1329
+ topicName = null,
1330
+ className = null,
1331
+ keyFilter = null,
1332
+ topicFilter = null,
1333
+ excludeKeyFilter = null,
1334
+ excludeTopicFilter = null,
1335
+ maxHistory = null,
1336
+ excludeDerived = false,
1337
+ includeHeavy = false,
1338
+ includeExplicit = false,
1339
+ trimDefaultValues = false,
1340
+ workingNamespace = null,
1341
+ restrictNamespace = null,
1342
+ excludeNamespace = null,
1343
+ density = null,
1344
+ subscriptionGroup = 0,
1345
+ subscriptionSet = null,
1346
+ snapshotSizeLimit = 0,
1347
+ nagleInterval = 0,
1348
+ limit = 0,
1349
+ referencingClassList = null
1350
+ ) {
1351
+ this.subscribeOptions({
1352
+ name, subscriptionMode, keyName, topicName, className, keyFilter, topicFilter,
1353
+ excludeKeyFilter, excludeTopicFilter, maxHistory, excludeDerived, includeHeavy,
1354
+ includeExplicit, trimDefaultValues, workingNamespace, restrictNamespace, excludeNamespace,
1355
+ density, subscriptionGroup, subscriptionSet, snapshotSizeLimit, nagleInterval, limit,
1356
+ referencingClassList,
1357
+ });
1358
+ }
1359
+
1311
1360
  /**
1312
1361
  * Wrapper to `subscribe` with a smaller set of arguments.
1313
1362
  * @param {string} name - Subscription name
@@ -1345,16 +1394,17 @@ class GARClient {
1345
1394
  topicFilter,
1346
1395
  excludeKeyFilter,
1347
1396
  excludeTopicFilter,
1348
- null,
1349
- false,
1350
- false,
1351
- false,
1352
- null,
1353
- null,
1354
- null,
1355
- null,
1397
+ null, // maxHistory
1398
+ false, // excludeDerived
1399
+ false, // includeHeavy
1400
+ false, // includeExplicit
1401
+ false, // trimDefaultValues
1402
+ null, // workingNamespace
1403
+ null, // restrictNamespace
1404
+ null, // excludeNamespace
1405
+ null, // density
1356
1406
  subscriptionGroup,
1357
- null,
1407
+ null, // subscriptionSet
1358
1408
  snapshotSizeLimit
1359
1409
  );
1360
1410
  }
@@ -1686,15 +1736,16 @@ class GARClient {
1686
1736
  null,
1687
1737
  null,
1688
1738
  null,
1689
- null,
1690
- false,
1691
- false,
1692
- false,
1693
- 'g::deployment::Server',
1694
- null,
1695
- null,
1696
- null,
1697
- subscriptionGroupServerRecords,
1739
+ null, // maxHistory
1740
+ false, // excludeDerived
1741
+ false, // includeHeavy
1742
+ false, // includeExplicit
1743
+ false, // trimDefaultValues
1744
+ 'g::deployment::Server', // workingNamespace
1745
+ null, // restrictNamespace
1746
+ null, // excludeNamespace
1747
+ null, // density
1748
+ subscriptionGroupServerRecords, // subscriptionGroup
1698
1749
  );
1699
1750
  };
1700
1751
 
@@ -1736,15 +1787,16 @@ class GARClient {
1736
1787
  null,
1737
1788
  null,
1738
1789
  null,
1739
- null,
1740
- false,
1741
- false,
1742
- false,
1743
- 'g::deployment::RecordFilter',
1744
- null,
1745
- null,
1746
- null,
1747
- subscriptionGroupServerKeys,
1790
+ null, // maxHistory
1791
+ false, // excludeDerived
1792
+ false, // includeHeavy
1793
+ false, // includeExplicit
1794
+ false, // trimDefaultValues
1795
+ 'g::deployment::RecordFilter', // workingNamespace
1796
+ null, // restrictNamespace
1797
+ null, // excludeNamespace
1798
+ null, // density
1799
+ subscriptionGroupServerKeys, // subscriptionGroup
1748
1800
  );
1749
1801
 
1750
1802
  // Timeout handling
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsgar",
3
- "version": "4.10.0",
3
+ "version": "4.13.0",
4
4
  "description": "A Javascript client for the GAR protocol",
5
5
  "type": "module",
6
6
  "main": "dist/gar.umd.js",