@neurosity/sdk 6.3.0 → 6.4.0-next.1

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 (36) hide show
  1. package/dist/browser/neurosity.iife.js +1550 -1536
  2. package/dist/browser/neurosity.js +15 -15
  3. package/dist/browser/neurosity.js.map +1 -1
  4. package/dist/cjs/Neurosity.d.ts +5 -0
  5. package/dist/cjs/Neurosity.js +20 -6
  6. package/dist/cjs/api/bluetooth/BluetoothClient.d.ts +2 -0
  7. package/dist/cjs/api/bluetooth/BluetoothClient.js +35 -30
  8. package/dist/cjs/api/bluetooth/BluetoothTransport.d.ts +2 -2
  9. package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.d.ts +2 -3
  10. package/dist/cjs/api/bluetooth/react-native/ReactNativeTransport.js +37 -45
  11. package/dist/cjs/api/bluetooth/utils/osHasBluetoothSupport.d.ts +2 -2
  12. package/dist/cjs/api/bluetooth/utils/osHasBluetoothSupport.js +4 -2
  13. package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.d.ts +2 -3
  14. package/dist/cjs/api/bluetooth/web/WebBluetoothTransport.js +35 -41
  15. package/dist/cjs/api/index.d.ts +3 -1
  16. package/dist/cjs/api/index.js +4 -0
  17. package/dist/electron/index.js +10 -10
  18. package/dist/electron/index.js.map +1 -1
  19. package/dist/esm/Neurosity.d.ts +5 -0
  20. package/dist/esm/Neurosity.js +20 -6
  21. package/dist/esm/api/bluetooth/BluetoothClient.d.ts +2 -0
  22. package/dist/esm/api/bluetooth/BluetoothClient.js +36 -31
  23. package/dist/esm/api/bluetooth/BluetoothTransport.d.ts +2 -2
  24. package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.d.ts +2 -3
  25. package/dist/esm/api/bluetooth/react-native/ReactNativeTransport.js +33 -41
  26. package/dist/esm/api/bluetooth/utils/osHasBluetoothSupport.d.ts +2 -2
  27. package/dist/esm/api/bluetooth/utils/osHasBluetoothSupport.js +4 -2
  28. package/dist/esm/api/bluetooth/web/WebBluetoothTransport.d.ts +2 -3
  29. package/dist/esm/api/bluetooth/web/WebBluetoothTransport.js +33 -39
  30. package/dist/esm/api/index.d.ts +3 -1
  31. package/dist/esm/api/index.js +4 -0
  32. package/dist/esm/neurosity.mjs +1550 -1536
  33. package/dist/examples/neurosity.iife.js +1550 -1536
  34. package/dist/examples/neurosity.js +15 -15
  35. package/dist/examples/neurosity.mjs +1550 -1536
  36. package/package.json +1 -1
@@ -43004,6 +43004,7 @@ class CloudClient {
43004
43004
  this.firebaseUser = new FirebaseUser(this.firebaseApp);
43005
43005
  this._selectedDevice.next(undefined);
43006
43006
  this.status$ = heartbeatAwareStatus(this.observeNamespace("status").pipe(share())).pipe(filterInternalKeys(), shareReplay(1));
43007
+ this.osVersion$ = this.observeNamespace("info/osVersion").pipe(shareReplay(1));
43007
43008
  this.firebaseUser.onAuthStateChanged().subscribe((user) => {
43008
43009
  this.user = user;
43009
43010
  });
@@ -43037,6 +43038,9 @@ class CloudClient {
43037
43038
  .asObservable()
43038
43039
  .pipe(filter((value) => value !== undefined));
43039
43040
  }
43041
+ osVersion() {
43042
+ return this.osVersion$;
43043
+ }
43040
43044
  // Automatically select device when user logs in
43041
43045
  setAutoSelectedDevice() {
43042
43046
  return __awaiter$b(this, void 0, void 0, function* () {
@@ -45747,577 +45751,412 @@ const DEFAULT_ACTION_RESPONSE_TIMEOUT = 1000 * 60; // 1 minute
45747
45751
  // Reverse BLUETOOTH_CHARACTERISTICS key/values for easy lookup
45748
45752
  const CHARACTERISTIC_UUIDS_TO_NAMES = Object.fromEntries(Object.entries(BLUETOOTH_CHARACTERISTICS).map((entries) => entries.reverse()));
45749
45753
 
45750
- const debug = (
45751
- typeof process === 'object' &&
45752
- process.env &&
45753
- process.env.NODE_DEBUG &&
45754
- /\bsemver\b/i.test(process.env.NODE_DEBUG)
45755
- ) ? (...args) => console.error('SEMVER', ...args)
45756
- : () => {};
45757
-
45758
- var debug_1 = debug;
45759
-
45760
- // Note: this is the semver.org version of the spec that it implements
45761
- // Not necessarily the package version of this code.
45762
- const SEMVER_SPEC_VERSION = '2.0.0';
45763
-
45764
- const MAX_LENGTH = 256;
45765
- const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
45766
- /* istanbul ignore next */ 9007199254740991;
45767
-
45768
- // Max safe segment length for coercion.
45769
- const MAX_SAFE_COMPONENT_LENGTH = 16;
45770
-
45771
- var constants = {
45772
- SEMVER_SPEC_VERSION,
45773
- MAX_LENGTH,
45774
- MAX_SAFE_INTEGER,
45775
- MAX_SAFE_COMPONENT_LENGTH,
45754
+ var __awaiter$d = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
45755
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
45756
+ return new (P || (P = Promise))(function (resolve, reject) {
45757
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
45758
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
45759
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
45760
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
45761
+ });
45776
45762
  };
45777
-
45778
- function createCommonjsModule(fn, module) {
45779
- return module = { exports: {} }, fn(module, module.exports), module.exports;
45780
- }
45781
-
45782
- var re_1 = createCommonjsModule(function (module, exports) {
45783
- const { MAX_SAFE_COMPONENT_LENGTH } = constants;
45784
-
45785
- exports = module.exports = {};
45786
-
45787
- // The actual regexps go on exports.re
45788
- const re = exports.re = [];
45789
- const src = exports.src = [];
45790
- const t = exports.t = {};
45791
- let R = 0;
45792
-
45793
- const createToken = (name, value, isGlobal) => {
45794
- const index = R++;
45795
- debug_1(name, index, value);
45796
- t[name] = index;
45797
- src[index] = value;
45798
- re[index] = new RegExp(value, isGlobal ? 'g' : undefined);
45763
+ const defaultOptions$1 = {
45764
+ autoConnect: true
45799
45765
  };
45800
-
45801
- // The following Regular Expressions can be used for tokenizing,
45802
- // validating, and parsing SemVer version strings.
45803
-
45804
- // ## Numeric Identifier
45805
- // A single `0`, or a non-zero digit followed by zero or more digits.
45806
-
45807
- createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*');
45808
- createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+');
45809
-
45810
- // ## Non-numeric Identifier
45811
- // Zero or more digits, followed by a letter or hyphen, and then zero or
45812
- // more letters, digits, or hyphens.
45813
-
45814
- createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*');
45815
-
45816
- // ## Main Version
45817
- // Three dot-separated numeric identifiers.
45818
-
45819
- createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
45820
- `(${src[t.NUMERICIDENTIFIER]})\\.` +
45821
- `(${src[t.NUMERICIDENTIFIER]})`);
45822
-
45823
- createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
45824
- `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
45825
- `(${src[t.NUMERICIDENTIFIERLOOSE]})`);
45826
-
45827
- // ## Pre-release Version Identifier
45828
- // A numeric identifier, or a non-numeric identifier.
45829
-
45830
- createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
45831
- }|${src[t.NONNUMERICIDENTIFIER]})`);
45832
-
45833
- createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
45834
- }|${src[t.NONNUMERICIDENTIFIER]})`);
45835
-
45836
- // ## Pre-release Version
45837
- // Hyphen, followed by one or more dot-separated pre-release version
45838
- // identifiers.
45839
-
45840
- createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
45841
- }(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`);
45842
-
45843
- createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
45844
- }(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`);
45845
-
45846
- // ## Build Metadata Identifier
45847
- // Any combination of digits, letters, or hyphens.
45848
-
45849
- createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+');
45850
-
45851
- // ## Build Metadata
45852
- // Plus sign, followed by one or more period-separated build metadata
45853
- // identifiers.
45854
-
45855
- createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
45856
- }(?:\\.${src[t.BUILDIDENTIFIER]})*))`);
45857
-
45858
- // ## Full Version String
45859
- // A main version, followed optionally by a pre-release version and
45860
- // build metadata.
45861
-
45862
- // Note that the only major, minor, patch, and pre-release sections of
45863
- // the version string are capturing groups. The build metadata is not a
45864
- // capturing group, because it should not ever be used in version
45865
- // comparison.
45866
-
45867
- createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
45868
- }${src[t.PRERELEASE]}?${
45869
- src[t.BUILD]}?`);
45870
-
45871
- createToken('FULL', `^${src[t.FULLPLAIN]}$`);
45872
-
45873
- // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
45874
- // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
45875
- // common in the npm registry.
45876
- createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
45877
- }${src[t.PRERELEASELOOSE]}?${
45878
- src[t.BUILD]}?`);
45879
-
45880
- createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`);
45881
-
45882
- createToken('GTLT', '((?:<|>)?=?)');
45883
-
45884
- // Something like "2.*" or "1.2.x".
45885
- // Note that "x.x" is a valid xRange identifer, meaning "any version"
45886
- // Only the first item is strictly required.
45887
- createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);
45888
- createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`);
45889
-
45890
- createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
45891
- `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
45892
- `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
45893
- `(?:${src[t.PRERELEASE]})?${
45894
- src[t.BUILD]}?` +
45895
- `)?)?`);
45896
-
45897
- createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
45898
- `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
45899
- `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
45900
- `(?:${src[t.PRERELEASELOOSE]})?${
45901
- src[t.BUILD]}?` +
45902
- `)?)?`);
45903
-
45904
- createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`);
45905
- createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`);
45906
-
45907
- // Coercion.
45908
- // Extract anything that could conceivably be a part of a valid semver
45909
- createToken('COERCE', `${'(^|[^\\d])' +
45910
- '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
45911
- `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
45912
- `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
45913
- `(?:$|[^\\d])`);
45914
- createToken('COERCERTL', src[t.COERCE], true);
45915
-
45916
- // Tilde ranges.
45917
- // Meaning is "reasonably at or greater than"
45918
- createToken('LONETILDE', '(?:~>?)');
45919
-
45920
- createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true);
45921
- exports.tildeTrimReplace = '$1~';
45922
-
45923
- createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`);
45924
- createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`);
45925
-
45926
- // Caret ranges.
45927
- // Meaning is "at least and backwards compatible with"
45928
- createToken('LONECARET', '(?:\\^)');
45929
-
45930
- createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true);
45931
- exports.caretTrimReplace = '$1^';
45932
-
45933
- createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`);
45934
- createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`);
45935
-
45936
- // A simple gt/lt/eq thing, or just "" to indicate "any version"
45937
- createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`);
45938
- createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`);
45939
-
45940
- // An expression to strip any whitespace between the gtlt and the thing
45941
- // it modifies, so that `> 1.2.3` ==> `>1.2.3`
45942
- createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
45943
- }\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true);
45944
- exports.comparatorTrimReplace = '$1$2$3';
45945
-
45946
- // Something like `1.2.3 - 1.2.4`
45947
- // Note that these all use the loose form, because they'll be
45948
- // checked against either the strict or loose comparator form
45949
- // later.
45950
- createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
45951
- `\\s+-\\s+` +
45952
- `(${src[t.XRANGEPLAIN]})` +
45953
- `\\s*$`);
45954
-
45955
- createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
45956
- `\\s+-\\s+` +
45957
- `(${src[t.XRANGEPLAINLOOSE]})` +
45958
- `\\s*$`);
45959
-
45960
- // Star ranges basically just allow anything at all.
45961
- createToken('STAR', '(<|>)?=?\\s*\\*');
45962
- // >=0.0.0 is like a star
45963
- createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$');
45964
- createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$');
45965
- });
45966
- var re_2 = re_1.re;
45967
- var re_3 = re_1.src;
45968
- var re_4 = re_1.t;
45969
- var re_5 = re_1.tildeTrimReplace;
45970
- var re_6 = re_1.caretTrimReplace;
45971
- var re_7 = re_1.comparatorTrimReplace;
45972
-
45973
- // parse out just the options we care about so we always get a consistent
45974
- // obj with keys in a consistent order.
45975
- const opts = ['includePrerelease', 'loose', 'rtl'];
45976
- const parseOptions = options =>
45977
- !options ? {}
45978
- : typeof options !== 'object' ? { loose: true }
45979
- : opts.filter(k => options[k]).reduce((o, k) => {
45980
- o[k] = true;
45981
- return o
45982
- }, {});
45983
- var parseOptions_1 = parseOptions;
45984
-
45985
- const numeric = /^[0-9]+$/;
45986
- const compareIdentifiers = (a, b) => {
45987
- const anum = numeric.test(a);
45988
- const bnum = numeric.test(b);
45989
-
45990
- if (anum && bnum) {
45991
- a = +a;
45992
- b = +b;
45993
- }
45994
-
45995
- return a === b ? 0
45996
- : (anum && !bnum) ? -1
45997
- : (bnum && !anum) ? 1
45998
- : a < b ? -1
45999
- : 1
46000
- };
46001
-
46002
- const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a);
46003
-
46004
- var identifiers = {
46005
- compareIdentifiers,
46006
- rcompareIdentifiers,
46007
- };
46008
-
46009
- const { MAX_LENGTH: MAX_LENGTH$1, MAX_SAFE_INTEGER: MAX_SAFE_INTEGER$1 } = constants;
46010
- const { re: re$1, t: t$1 } = re_1;
46011
-
46012
-
46013
- const { compareIdentifiers: compareIdentifiers$1 } = identifiers;
46014
- class SemVer {
46015
- constructor (version, options) {
46016
- options = parseOptions_1(options);
46017
-
46018
- if (version instanceof SemVer) {
46019
- if (version.loose === !!options.loose &&
46020
- version.includePrerelease === !!options.includePrerelease) {
46021
- return version
46022
- } else {
46023
- version = version.version;
46024
- }
46025
- } else if (typeof version !== 'string') {
46026
- throw new TypeError(`Invalid Version: ${version}`)
46027
- }
46028
-
46029
- if (version.length > MAX_LENGTH$1) {
46030
- throw new TypeError(
46031
- `version is longer than ${MAX_LENGTH$1} characters`
46032
- )
45766
+ class WebBluetoothTransport {
45767
+ constructor(options = {}) {
45768
+ this.type = TRANSPORT_TYPE.WEB;
45769
+ this.characteristicsByName = {};
45770
+ this.connection$ = new BehaviorSubject(BLUETOOTH_CONNECTION.DISCONNECTED);
45771
+ this.pendingActions$ = new BehaviorSubject([]);
45772
+ this.logs$ = new ReplaySubject(10);
45773
+ this.onDisconnected$ = this._onDisconnected().pipe(share());
45774
+ this.connectionStream$ = this.connection$
45775
+ .asObservable()
45776
+ .pipe(filter((connection) => !!connection), distinctUntilChanged(), shareReplay(1));
45777
+ this._isAutoConnectEnabled$ = new ReplaySubject(1);
45778
+ this.options = Object.assign(Object.assign({}, defaultOptions$1), options);
45779
+ if (!isWebBluetoothSupported()) {
45780
+ const errorMessage = "Web Bluetooth is not supported";
45781
+ this.addLog(errorMessage);
45782
+ throw new Error(errorMessage);
45783
+ }
45784
+ this._isAutoConnectEnabled$.subscribe((autoConnect) => {
45785
+ this.addLog(`Auto connect: ${autoConnect ? "enabled" : "disabled"}`);
45786
+ });
45787
+ this._isAutoConnectEnabled$.next(this.options.autoConnect);
45788
+ this.connection$.asObservable().subscribe((connection) => {
45789
+ this.addLog(`connection status is ${connection}`);
45790
+ });
45791
+ this.onDisconnected$.subscribe(() => {
45792
+ this.connection$.next(BLUETOOTH_CONNECTION.DISCONNECTED);
45793
+ });
46033
45794
  }
46034
-
46035
- debug_1('SemVer', version, options);
46036
- this.options = options;
46037
- this.loose = !!options.loose;
46038
- // this isn't actually relevant for versions, but keep it so that we
46039
- // don't run into trouble passing this.options around.
46040
- this.includePrerelease = !!options.includePrerelease;
46041
-
46042
- const m = version.trim().match(options.loose ? re$1[t$1.LOOSE] : re$1[t$1.FULL]);
46043
-
46044
- if (!m) {
46045
- throw new TypeError(`Invalid Version: ${version}`)
45795
+ _getPairedDevices() {
45796
+ return __awaiter$d(this, void 0, void 0, function* () {
45797
+ return yield navigator.bluetooth.getDevices();
45798
+ });
46046
45799
  }
46047
-
46048
- this.raw = version;
46049
-
46050
- // these are actually numbers
46051
- this.major = +m[1];
46052
- this.minor = +m[2];
46053
- this.patch = +m[3];
46054
-
46055
- if (this.major > MAX_SAFE_INTEGER$1 || this.major < 0) {
46056
- throw new TypeError('Invalid major version')
45800
+ _autoConnect(selectedDevice$) {
45801
+ return this._isAutoConnectEnabled$.pipe(switchMap((isAutoConnectEnabled) => isAutoConnectEnabled
45802
+ ? merge(selectedDevice$, this.onDisconnected$.pipe(switchMap(() => selectedDevice$)))
45803
+ : NEVER), switchMap((selectedDevice) => __awaiter$d(this, void 0, void 0, function* () {
45804
+ var _a;
45805
+ const { deviceNickname } = selectedDevice;
45806
+ if (this.isConnected()) {
45807
+ this.addLog(`Auto connect: ${deviceNickname} is already connected. Skipping auto connect.`);
45808
+ return;
45809
+ }
45810
+ const [devicesError, devices] = yield this._getPairedDevices()
45811
+ .then((devices) => [null, devices])
45812
+ .catch((error) => [error, null]);
45813
+ if (devicesError) {
45814
+ throw new Error(`failed to get devices: ${(_a = devicesError === null || devicesError === void 0 ? void 0 : devicesError.message) !== null && _a !== void 0 ? _a : devicesError}`);
45815
+ }
45816
+ this.addLog(`Auto connect: found ${devices.length} devices ${devices
45817
+ .map(({ name }) => name)
45818
+ .join(", ")}`);
45819
+ // @important - Using `findLast` instead of `find` because somehow the browser
45820
+ // is finding multiple peripherals with the same name
45821
+ const device = devices.findLast((device) => device.name === deviceNickname);
45822
+ if (!device) {
45823
+ throw new Error(`couldn't find selected device in the list of paired devices.`);
45824
+ }
45825
+ this.addLog(`Auto connect: ${deviceNickname} was detected and previously paired`);
45826
+ return device;
45827
+ })), tap(() => {
45828
+ this.connection$.next(BLUETOOTH_CONNECTION.SCANNING);
45829
+ }), switchMap((device) => onAdvertisementReceived(device)), switchMap((advertisement) => __awaiter$d(this, void 0, void 0, function* () {
45830
+ this.addLog(`Advertisement received for ${advertisement.device.name}`);
45831
+ return yield this.getServerServiceAndCharacteristics(advertisement.device);
45832
+ })));
46057
45833
  }
46058
-
46059
- if (this.minor > MAX_SAFE_INTEGER$1 || this.minor < 0) {
46060
- throw new TypeError('Invalid minor version')
45834
+ enableAutoConnect(autoConnect) {
45835
+ this._isAutoConnectEnabled$.next(autoConnect);
46061
45836
  }
46062
-
46063
- if (this.patch > MAX_SAFE_INTEGER$1 || this.patch < 0) {
46064
- throw new TypeError('Invalid patch version')
45837
+ addLog(log) {
45838
+ this.logs$.next(log);
46065
45839
  }
46066
-
46067
- // numberify any prerelease numeric ids
46068
- if (!m[4]) {
46069
- this.prerelease = [];
46070
- } else {
46071
- this.prerelease = m[4].split('.').map((id) => {
46072
- if (/^[0-9]+$/.test(id)) {
46073
- const num = +id;
46074
- if (num >= 0 && num < MAX_SAFE_INTEGER$1) {
46075
- return num
46076
- }
46077
- }
46078
- return id
46079
- });
45840
+ isConnected() {
45841
+ const connection = this.connection$.getValue();
45842
+ return connection === BLUETOOTH_CONNECTION.CONNECTED;
46080
45843
  }
46081
-
46082
- this.build = m[5] ? m[5].split('.') : [];
46083
- this.format();
46084
- }
46085
-
46086
- format () {
46087
- this.version = `${this.major}.${this.minor}.${this.patch}`;
46088
- if (this.prerelease.length) {
46089
- this.version += `-${this.prerelease.join('.')}`;
45844
+ connection() {
45845
+ return this.connectionStream$;
46090
45846
  }
46091
- return this.version
46092
- }
46093
-
46094
- toString () {
46095
- return this.version
46096
- }
46097
-
46098
- compare (other) {
46099
- debug_1('SemVer.compare', this.version, this.options, other);
46100
- if (!(other instanceof SemVer)) {
46101
- if (typeof other === 'string' && other === this.version) {
46102
- return 0
46103
- }
46104
- other = new SemVer(other, this.options);
45847
+ connect(deviceNickname) {
45848
+ return __awaiter$d(this, void 0, void 0, function* () {
45849
+ try {
45850
+ // requires user gesture
45851
+ const device = yield this.requestDevice(deviceNickname);
45852
+ yield this.getServerServiceAndCharacteristics(device);
45853
+ }
45854
+ catch (error) {
45855
+ return Promise.reject(error);
45856
+ }
45857
+ });
46105
45858
  }
46106
-
46107
- if (other.version === this.version) {
46108
- return 0
45859
+ requestDevice(deviceNickname) {
45860
+ return __awaiter$d(this, void 0, void 0, function* () {
45861
+ try {
45862
+ this.addLog("Requesting Bluetooth Device...");
45863
+ const prefixes = BLUETOOTH_DEVICE_NAME_PREFIXES.map((namePrefix) => ({
45864
+ namePrefix
45865
+ }));
45866
+ // Ability to only show selectedDevice if provided
45867
+ const filters = deviceNickname
45868
+ ? [
45869
+ {
45870
+ name: deviceNickname
45871
+ }
45872
+ ]
45873
+ : prefixes;
45874
+ const device = yield window.navigator.bluetooth.requestDevice({
45875
+ filters: [
45876
+ ...filters,
45877
+ {
45878
+ manufacturerData: [
45879
+ {
45880
+ companyIdentifier: BLUETOOTH_COMPANY_IDENTIFIER_HEX
45881
+ }
45882
+ ]
45883
+ }
45884
+ ],
45885
+ optionalServices: [BLUETOOTH_PRIMARY_SERVICE_UUID_HEX]
45886
+ });
45887
+ return device;
45888
+ }
45889
+ catch (error) {
45890
+ return Promise.reject(error);
45891
+ }
45892
+ });
46109
45893
  }
46110
-
46111
- return this.compareMain(other) || this.comparePre(other)
46112
- }
46113
-
46114
- compareMain (other) {
46115
- if (!(other instanceof SemVer)) {
46116
- other = new SemVer(other, this.options);
45894
+ getServerServiceAndCharacteristics(device) {
45895
+ return __awaiter$d(this, void 0, void 0, function* () {
45896
+ try {
45897
+ this.device = device;
45898
+ const isConnecting = this.connection$.getValue() === BLUETOOTH_CONNECTION.CONNECTING;
45899
+ if (!isConnecting) {
45900
+ this.connection$.next(BLUETOOTH_CONNECTION.CONNECTING);
45901
+ }
45902
+ this.server = yield device.gatt.connect();
45903
+ this.addLog(`Getting service...`);
45904
+ this.service = yield this.server.getPrimaryService(BLUETOOTH_PRIMARY_SERVICE_UUID_HEX);
45905
+ this.addLog(`Got service ${this.service.uuid}, getting characteristics...`);
45906
+ const characteristicsList = yield this.service.getCharacteristics();
45907
+ this.addLog(`Got characteristics`);
45908
+ this.characteristicsByName = Object.fromEntries(characteristicsList.map((characteristic) => [
45909
+ CHARACTERISTIC_UUIDS_TO_NAMES[characteristic.uuid],
45910
+ characteristic
45911
+ ]));
45912
+ this.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
45913
+ }
45914
+ catch (error) {
45915
+ return Promise.reject(error);
45916
+ }
45917
+ });
46117
45918
  }
46118
-
46119
- return (
46120
- compareIdentifiers$1(this.major, other.major) ||
46121
- compareIdentifiers$1(this.minor, other.minor) ||
46122
- compareIdentifiers$1(this.patch, other.patch)
46123
- )
46124
- }
46125
-
46126
- comparePre (other) {
46127
- if (!(other instanceof SemVer)) {
46128
- other = new SemVer(other, this.options);
45919
+ _onDisconnected() {
45920
+ return this.connection$
45921
+ .asObservable()
45922
+ .pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
45923
+ ? fromDOMEvent(this.device, "gattserverdisconnected")
45924
+ : NEVER));
46129
45925
  }
46130
-
46131
- // NOT having a prerelease is > having one
46132
- if (this.prerelease.length && !other.prerelease.length) {
46133
- return -1
46134
- } else if (!this.prerelease.length && other.prerelease.length) {
46135
- return 1
46136
- } else if (!this.prerelease.length && !other.prerelease.length) {
46137
- return 0
45926
+ disconnect() {
45927
+ var _a, _b;
45928
+ return __awaiter$d(this, void 0, void 0, function* () {
45929
+ const isDeviceConnected = (_b = (_a = this === null || this === void 0 ? void 0 : this.device) === null || _a === void 0 ? void 0 : _a.gatt) === null || _b === void 0 ? void 0 : _b.connected;
45930
+ if (isDeviceConnected) {
45931
+ this.device.gatt.disconnect();
45932
+ }
45933
+ });
46138
45934
  }
46139
-
46140
- let i = 0;
46141
- do {
46142
- const a = this.prerelease[i];
46143
- const b = other.prerelease[i];
46144
- debug_1('prerelease compare', i, a, b);
46145
- if (a === undefined && b === undefined) {
46146
- return 0
46147
- } else if (b === undefined) {
46148
- return 1
46149
- } else if (a === undefined) {
46150
- return -1
46151
- } else if (a === b) {
46152
- continue
46153
- } else {
46154
- return compareIdentifiers$1(a, b)
46155
- }
46156
- } while (++i)
46157
- }
46158
-
46159
- compareBuild (other) {
46160
- if (!(other instanceof SemVer)) {
46161
- other = new SemVer(other, this.options);
45935
+ /**
45936
+ *
45937
+ * Bluetooth GATT attributes, services, characteristics, etc. are invalidated
45938
+ * when a device disconnects. This means your code should always retrieve
45939
+ * (through getPrimaryService(s), getCharacteristic(s), etc.) these attributes
45940
+ * after reconnecting.
45941
+ */
45942
+ getCharacteristicByName(characteristicName) {
45943
+ var _a;
45944
+ return __awaiter$d(this, void 0, void 0, function* () {
45945
+ return (_a = this.characteristicsByName) === null || _a === void 0 ? void 0 : _a[characteristicName];
45946
+ });
46162
45947
  }
46163
-
46164
- let i = 0;
46165
- do {
46166
- const a = this.build[i];
46167
- const b = other.build[i];
46168
- debug_1('prerelease compare', i, a, b);
46169
- if (a === undefined && b === undefined) {
46170
- return 0
46171
- } else if (b === undefined) {
46172
- return 1
46173
- } else if (a === undefined) {
46174
- return -1
46175
- } else if (a === b) {
46176
- continue
46177
- } else {
46178
- return compareIdentifiers$1(a, b)
46179
- }
46180
- } while (++i)
46181
- }
46182
-
46183
- // preminor will bump the version up to the next minor release, and immediately
46184
- // down to pre-release. premajor and prepatch work the same way.
46185
- inc (release, identifier) {
46186
- switch (release) {
46187
- case 'premajor':
46188
- this.prerelease.length = 0;
46189
- this.patch = 0;
46190
- this.minor = 0;
46191
- this.major++;
46192
- this.inc('pre', identifier);
46193
- break
46194
- case 'preminor':
46195
- this.prerelease.length = 0;
46196
- this.patch = 0;
46197
- this.minor++;
46198
- this.inc('pre', identifier);
46199
- break
46200
- case 'prepatch':
46201
- // If this is already a prerelease, it will bump to the next version
46202
- // drop any prereleases that might already exist, since they are not
46203
- // relevant at this point.
46204
- this.prerelease.length = 0;
46205
- this.inc('patch', identifier);
46206
- this.inc('pre', identifier);
46207
- break
46208
- // If the input is a non-prerelease version, this acts the same as
46209
- // prepatch.
46210
- case 'prerelease':
46211
- if (this.prerelease.length === 0) {
46212
- this.inc('patch', identifier);
46213
- }
46214
- this.inc('pre', identifier);
46215
- break
46216
-
46217
- case 'major':
46218
- // If this is a pre-major version, bump up to the same major version.
46219
- // Otherwise increment major.
46220
- // 1.0.0-5 bumps to 1.0.0
46221
- // 1.1.0 bumps to 2.0.0
46222
- if (
46223
- this.minor !== 0 ||
46224
- this.patch !== 0 ||
46225
- this.prerelease.length === 0
46226
- ) {
46227
- this.major++;
46228
- }
46229
- this.minor = 0;
46230
- this.patch = 0;
46231
- this.prerelease = [];
46232
- break
46233
- case 'minor':
46234
- // If this is a pre-minor version, bump up to the same minor version.
46235
- // Otherwise increment minor.
46236
- // 1.2.0-5 bumps to 1.2.0
46237
- // 1.2.1 bumps to 1.3.0
46238
- if (this.patch !== 0 || this.prerelease.length === 0) {
46239
- this.minor++;
46240
- }
46241
- this.patch = 0;
46242
- this.prerelease = [];
46243
- break
46244
- case 'patch':
46245
- // If this is not a pre-release version, it will increment the patch.
46246
- // If it is a pre-release it will bump up to the same patch version.
46247
- // 1.2.0-5 patches to 1.2.0
46248
- // 1.2.0 patches to 1.2.1
46249
- if (this.prerelease.length === 0) {
46250
- this.patch++;
46251
- }
46252
- this.prerelease = [];
46253
- break
46254
- // This probably shouldn't be used publicly.
46255
- // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
46256
- case 'pre':
46257
- if (this.prerelease.length === 0) {
46258
- this.prerelease = [0];
46259
- } else {
46260
- let i = this.prerelease.length;
46261
- while (--i >= 0) {
46262
- if (typeof this.prerelease[i] === 'number') {
46263
- this.prerelease[i]++;
46264
- i = -2;
45948
+ subscribeToCharacteristic({ characteristicName, manageNotifications = true }) {
45949
+ const data$ = defer(() => this.getCharacteristicByName(characteristicName)).pipe(switchMap((characteristic) => __awaiter$d(this, void 0, void 0, function* () {
45950
+ var _a;
45951
+ if (this.isConnected() && manageNotifications) {
45952
+ try {
45953
+ yield characteristic.startNotifications();
45954
+ this.addLog(`Started notifications for ${characteristicName} characteristic`);
45955
+ }
45956
+ catch (error) {
45957
+ this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
45958
+ }
46265
45959
  }
46266
- }
46267
- if (i === -1) {
46268
- // didn't increment anything
46269
- this.prerelease.push(0);
46270
- }
46271
- }
46272
- if (identifier) {
46273
- // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
46274
- // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
46275
- if (compareIdentifiers$1(this.prerelease[0], identifier) === 0) {
46276
- if (isNaN(this.prerelease[1])) {
46277
- this.prerelease = [identifier, 0];
45960
+ return characteristic;
45961
+ })), switchMap((characteristic) => {
45962
+ return fromDOMEvent(characteristic, "characteristicvaluechanged", () => __awaiter$d(this, void 0, void 0, function* () {
45963
+ var _a;
45964
+ if (this.isConnected() && manageNotifications) {
45965
+ try {
45966
+ yield characteristic.stopNotifications();
45967
+ this.addLog(`Stopped notifications for ${characteristicName} characteristic`);
45968
+ }
45969
+ catch (error) {
45970
+ this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
45971
+ }
45972
+ }
45973
+ }));
45974
+ }), map((event) => {
45975
+ const buffer$$1 = event.target.value;
45976
+ const decoded = decode$1(this.type, buffer$$1);
45977
+ this.addLog(`Received chunk with buffer size of ${buffer$$1.byteLength} and decoded size ${decoded.length} for ${characteristicName} characteristic: \n${decoded}`);
45978
+ return decoded;
45979
+ }), stitchChunks({ delimiter: BLUETOOTH_CHUNK_DELIMITER }), map((payload) => {
45980
+ var _a;
45981
+ try {
45982
+ return JSON.parse(payload);
46278
45983
  }
46279
- } else {
46280
- this.prerelease = [identifier, 0];
46281
- }
46282
- }
46283
- break
46284
-
46285
- default:
46286
- throw new Error(`invalid increment argument: ${release}`)
45984
+ catch (error) {
45985
+ this.addLog(`Failed to parse JSON for ${characteristicName} characteristic. Falling back to unparsed string. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
45986
+ return payload;
45987
+ }
45988
+ })
45989
+ // when streaming at ultra-low latency, the logs will slow down rendering
45990
+ // tap((data) => {
45991
+ // this.addLog(
45992
+ // `Received data for ${characteristicName} characteristic: \n${JSON.stringify(
45993
+ // data,
45994
+ // null,
45995
+ // 2
45996
+ // )}`
45997
+ // );
45998
+ // })
45999
+ );
46000
+ return this.connection$.pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED ? data$ : NEVER));
46287
46001
  }
46288
- this.format();
46289
- this.raw = this.version;
46290
- return this
46291
- }
46292
- }
46293
-
46294
- var semver = SemVer;
46295
-
46296
- const compare = (a, b, loose) =>
46297
- new semver(a, loose).compare(new semver(b, loose));
46298
-
46299
- var compare_1 = compare;
46300
-
46301
- const gte = (a, b, loose) => compare_1(a, b, loose) >= 0;
46302
- var gte_1 = gte;
46303
-
46304
- function osHasBluetoothSupport(selectedDevice) {
46305
- if (!selectedDevice) {
46306
- return false;
46002
+ readCharacteristic(characteristicName, parse = false) {
46003
+ return __awaiter$d(this, void 0, void 0, function* () {
46004
+ try {
46005
+ this.addLog(`Reading characteristic: ${characteristicName}`);
46006
+ const characteristic = yield this.getCharacteristicByName(characteristicName);
46007
+ if (!characteristic) {
46008
+ this.addLog(`Did not fund ${characteristicName} characteristic`);
46009
+ return Promise.reject(`Did not find characteristic by the name: ${characteristicName}`);
46010
+ }
46011
+ const value = yield characteristic.readValue();
46012
+ const uint8Array = value;
46013
+ const decodedValue = decode$1(this.type, uint8Array);
46014
+ const data = parse ? JSON.parse(decodedValue) : decodedValue;
46015
+ this.addLog(`Received read data from ${characteristicName} characteristic: \n${data}`);
46016
+ return data;
46017
+ }
46018
+ catch (error) {
46019
+ return Promise.reject(`Error reading characteristic: ${error.message}`);
46020
+ }
46021
+ });
46307
46022
  }
46308
- // Only the Crown supports Bluetooth
46309
- const isCrown = Number(selectedDevice.modelVersion) >= 3;
46310
- if (!isCrown) {
46311
- return false;
46023
+ writeCharacteristic(characteristicName, data) {
46024
+ return __awaiter$d(this, void 0, void 0, function* () {
46025
+ this.addLog(`Writing characteristic: ${characteristicName}`);
46026
+ const characteristic = yield this.getCharacteristicByName(characteristicName);
46027
+ if (!characteristic) {
46028
+ this.addLog(`Did not fund ${characteristicName} characteristic`);
46029
+ return Promise.reject(`Did not find characteristic by the name: ${characteristicName}`);
46030
+ }
46031
+ const encoded = encode$1(this.type, data);
46032
+ yield characteristic.writeValueWithResponse(encoded);
46033
+ });
46312
46034
  }
46313
- const isEmulator = !!(selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.emulator);
46314
- if (isEmulator) {
46315
- return false;
46035
+ _addPendingAction(actionId) {
46036
+ const actions = this.pendingActions$.getValue();
46037
+ this.pendingActions$.next([...actions, actionId]);
46038
+ }
46039
+ _removePendingAction(actionId) {
46040
+ const actions = this.pendingActions$.getValue();
46041
+ this.pendingActions$.next(actions.filter((id) => id !== actionId));
46042
+ }
46043
+ _autoToggleActionNotifications() {
46044
+ let actionsCharacteristic;
46045
+ let started = false;
46046
+ return this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46047
+ ? defer(() => this.getCharacteristicByName("actions")).pipe(switchMap((characteristic) => {
46048
+ actionsCharacteristic = characteristic;
46049
+ return this.pendingActions$;
46050
+ }))
46051
+ : NEVER), tap((pendingActions) => __awaiter$d(this, void 0, void 0, function* () {
46052
+ var _a, _b;
46053
+ const hasPendingActions = !!pendingActions.length;
46054
+ if (hasPendingActions && !started) {
46055
+ started = true;
46056
+ try {
46057
+ yield actionsCharacteristic.startNotifications();
46058
+ this.addLog(`Started notifications for [actions] characteristic`);
46059
+ }
46060
+ catch (error) {
46061
+ this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46062
+ }
46063
+ }
46064
+ if (!hasPendingActions && started) {
46065
+ started = false;
46066
+ try {
46067
+ yield actionsCharacteristic.stopNotifications();
46068
+ this.addLog(`Stopped notifications for actions characteristic`);
46069
+ }
46070
+ catch (error) {
46071
+ this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
46072
+ }
46073
+ }
46074
+ })));
46075
+ }
46076
+ dispatchAction({ characteristicName, action }) {
46077
+ return __awaiter$d(this, void 0, void 0, function* () {
46078
+ const { responseRequired = false, responseTimeout = DEFAULT_ACTION_RESPONSE_TIMEOUT } = action;
46079
+ return new Promise((resolve, reject) => __awaiter$d(this, void 0, void 0, function* () {
46080
+ const characteristic = yield this.getCharacteristicByName(characteristicName).catch(() => {
46081
+ reject(`Did not find characteristic by the name: ${characteristicName}`);
46082
+ });
46083
+ if (!characteristic) {
46084
+ return;
46085
+ }
46086
+ const actionId = create6DigitPin(); // use to later identify and filter response
46087
+ const payload = JSON.stringify(Object.assign({ actionId }, action)); // add the response id to the action
46088
+ this.addLog(`Dispatched action with id ${actionId}`);
46089
+ if (responseRequired && responseTimeout) {
46090
+ this._addPendingAction(actionId);
46091
+ const timeout$$1 = timer(responseTimeout).subscribe(() => {
46092
+ this._removePendingAction(actionId);
46093
+ reject(`Action with id ${actionId} timed out after ${responseTimeout}ms`);
46094
+ });
46095
+ // listen for a response before writing
46096
+ this.subscribeToCharacteristic({
46097
+ characteristicName,
46098
+ manageNotifications: false
46099
+ })
46100
+ .pipe(filter((response) => (response === null || response === void 0 ? void 0 : response.actionId) === actionId), take(1))
46101
+ .subscribe((response) => {
46102
+ timeout$$1.unsubscribe();
46103
+ this._removePendingAction(actionId);
46104
+ resolve(response);
46105
+ });
46106
+ // register action by writing
46107
+ this.writeCharacteristic(characteristicName, payload).catch((error) => {
46108
+ this._removePendingAction(actionId);
46109
+ reject(error.message);
46110
+ });
46111
+ }
46112
+ else {
46113
+ this.writeCharacteristic(characteristicName, payload)
46114
+ .then(() => {
46115
+ resolve(null);
46116
+ })
46117
+ .catch((error) => {
46118
+ reject(error.message);
46119
+ });
46120
+ }
46121
+ }));
46122
+ });
46316
46123
  }
46317
- return gte_1(selectedDevice.osVersion, "16.0.0");
46124
+ }
46125
+ function fromDOMEvent(target, eventName, beforeRemove) {
46126
+ return fromEventPattern((addHandler) => {
46127
+ target.addEventListener(eventName, addHandler);
46128
+ }, (removeHandler) => __awaiter$d(this, void 0, void 0, function* () {
46129
+ if (beforeRemove) {
46130
+ yield beforeRemove();
46131
+ }
46132
+ target.removeEventListener(eventName, removeHandler);
46133
+ }));
46134
+ }
46135
+ function onAdvertisementReceived(device) {
46136
+ return new Observable((subscriber) => {
46137
+ const abortController = new AbortController();
46138
+ const { signal } = abortController;
46139
+ const listener = device.addEventListener("advertisementreceived", (advertisement) => {
46140
+ abortController.abort();
46141
+ subscriber.next(advertisement);
46142
+ subscriber.complete();
46143
+ }, {
46144
+ once: true
46145
+ });
46146
+ try {
46147
+ device.watchAdvertisements({ signal });
46148
+ }
46149
+ catch (error) {
46150
+ subscriber.error(error);
46151
+ }
46152
+ return () => {
46153
+ abortController.abort();
46154
+ device.removeEventListener("advertisementreceived", listener);
46155
+ };
46156
+ });
46318
46157
  }
46319
46158
 
46320
- var __awaiter$d = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
46159
+ var __awaiter$e = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
46321
46160
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
46322
46161
  return new (P || (P = Promise))(function (resolve, reject) {
46323
46162
  function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
@@ -46326,31 +46165,69 @@ var __awaiter$d = (undefined && undefined.__awaiter) || function (thisArg, _argu
46326
46165
  step((generator = generator.apply(thisArg, _arguments || [])).next());
46327
46166
  });
46328
46167
  };
46329
- const defaultOptions$1 = {
46168
+ const defaultOptions$2 = {
46330
46169
  autoConnect: true
46331
46170
  };
46332
- class WebBluetoothTransport {
46333
- constructor(options = {}) {
46334
- this.type = TRANSPORT_TYPE.WEB;
46171
+ class ReactNativeTransport {
46172
+ constructor(options) {
46173
+ this.type = TRANSPORT_TYPE.REACT_NATIVE;
46335
46174
  this.characteristicsByName = {};
46336
46175
  this.connection$ = new BehaviorSubject(BLUETOOTH_CONNECTION.DISCONNECTED);
46337
46176
  this.pendingActions$ = new BehaviorSubject([]);
46338
46177
  this.logs$ = new ReplaySubject(10);
46339
- this.onDisconnected$ = this._onDisconnected().pipe(share());
46340
46178
  this.connectionStream$ = this.connection$
46341
46179
  .asObservable()
46342
46180
  .pipe(filter((connection) => !!connection), distinctUntilChanged(), shareReplay(1));
46343
46181
  this._isAutoConnectEnabled$ = new ReplaySubject(1);
46344
- this.options = Object.assign(Object.assign({}, defaultOptions$1), options);
46345
- if (!isWebBluetoothSupported()) {
46346
- const errorMessage = "Web Bluetooth is not supported";
46182
+ if (!options) {
46183
+ const errorMessage = "React Native transport: missing options.";
46184
+ this.addLog(errorMessage);
46185
+ throw new Error(errorMessage);
46186
+ }
46187
+ this.options = Object.assign(Object.assign({}, defaultOptions$2), options);
46188
+ const { BleManager, bleManagerEmitter, platform, autoConnect } = this.options;
46189
+ if (!BleManager) {
46190
+ const errorMessage = "React Native option: BleManager not provided.";
46191
+ this.addLog(errorMessage);
46192
+ throw new Error(errorMessage);
46193
+ }
46194
+ if (!bleManagerEmitter) {
46195
+ const errorMessage = "React Native option: bleManagerEmitter not provided.";
46347
46196
  this.addLog(errorMessage);
46348
46197
  throw new Error(errorMessage);
46349
46198
  }
46199
+ if (!platform) {
46200
+ const errorMessage = "React Native option: platform not provided.";
46201
+ this.addLog(errorMessage);
46202
+ throw new Error(errorMessage);
46203
+ }
46204
+ this.BleManager = BleManager;
46205
+ this.bleManagerEmitter = bleManagerEmitter;
46206
+ this.platform = platform;
46207
+ this._isAutoConnectEnabled$.next(autoConnect);
46350
46208
  this._isAutoConnectEnabled$.subscribe((autoConnect) => {
46351
46209
  this.addLog(`Auto connect: ${autoConnect ? "enabled" : "disabled"}`);
46352
46210
  });
46353
- this._isAutoConnectEnabled$.next(this.options.autoConnect);
46211
+ // We create a single listener per event type to
46212
+ // avoid missing events when multiple listeners are attached.
46213
+ this.bleEvents = {
46214
+ stopScan$: this._fromEvent("BleManagerStopScan"),
46215
+ discoverPeripheral$: this._fromEvent("BleManagerDiscoverPeripheral"),
46216
+ connectPeripheral$: this._fromEvent("BleManagerConnectPeripheral"),
46217
+ disconnectPeripheral$: this._fromEvent("BleManagerDisconnectPeripheral"),
46218
+ didUpdateValueForCharacteristic$: this._fromEvent("BleManagerDidUpdateValueForCharacteristic"),
46219
+ didUpdateState$: this._fromEvent("BleManagerDidUpdateState")
46220
+ };
46221
+ this.onDisconnected$ = this.bleEvents.disconnectPeripheral$.pipe(share());
46222
+ // Initializes the module. This can only be called once.
46223
+ this.BleManager.start({ showAlert: false })
46224
+ .then(() => {
46225
+ this.addLog(`BleManger started`);
46226
+ })
46227
+ .catch((error) => {
46228
+ var _a;
46229
+ this.addLog(`BleManger failed to start. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46230
+ });
46354
46231
  this.connection$.asObservable().subscribe((connection) => {
46355
46232
  this.addLog(`connection status is ${connection}`);
46356
46233
  });
@@ -46358,191 +46235,191 @@ class WebBluetoothTransport {
46358
46235
  this.connection$.next(BLUETOOTH_CONNECTION.DISCONNECTED);
46359
46236
  });
46360
46237
  }
46361
- _getPairedDevices() {
46362
- return __awaiter$d(this, void 0, void 0, function* () {
46363
- return yield navigator.bluetooth.getDevices();
46364
- });
46238
+ addLog(log) {
46239
+ this.logs$.next(log);
46240
+ }
46241
+ isConnected() {
46242
+ const connection = this.connection$.getValue();
46243
+ return connection === BLUETOOTH_CONNECTION.CONNECTED;
46365
46244
  }
46366
46245
  _autoConnect(selectedDevice$) {
46246
+ const selectedDeviceAfterDisconnect$ = this.onDisconnected$.pipe(switchMap(() => selectedDevice$));
46367
46247
  return this._isAutoConnectEnabled$.pipe(switchMap((isAutoConnectEnabled) => isAutoConnectEnabled
46368
- ? merge(selectedDevice$, this.onDisconnected$.pipe(switchMap(() => selectedDevice$)))
46369
- : NEVER), switchMap((selectedDevice) => osHasBluetoothSupport(selectedDevice) ? of(selectedDevice) : EMPTY), switchMap((selectedDevice) => __awaiter$d(this, void 0, void 0, function* () {
46370
- var _a;
46371
- const { deviceNickname } = selectedDevice;
46372
- if (this.isConnected()) {
46373
- this.addLog(`Auto connect: ${deviceNickname} is already connected. Skipping auto connect.`);
46374
- return;
46375
- }
46376
- const [devicesError, devices] = yield this._getPairedDevices()
46377
- .then((devices) => [null, devices])
46378
- .catch((error) => [error, null]);
46379
- if (devicesError) {
46380
- throw new Error(`failed to get devices: ${(_a = devicesError === null || devicesError === void 0 ? void 0 : devicesError.message) !== null && _a !== void 0 ? _a : devicesError}`);
46381
- }
46382
- this.addLog(`Auto connect: found ${devices.length} devices ${devices
46383
- .map(({ name }) => name)
46384
- .join(", ")}`);
46385
- // @important - Using `findLast` instead of `find` because somehow the browser
46386
- // is finding multiple peripherals with the same name
46387
- const device = devices.findLast((device) => device.name === deviceNickname);
46388
- if (!device) {
46389
- throw new Error(`couldn't find selected device in the list of paired devices.`);
46390
- }
46391
- this.addLog(`Auto connect: ${deviceNickname} was detected and previously paired`);
46392
- return device;
46393
- })), tap(() => {
46394
- this.connection$.next(BLUETOOTH_CONNECTION.SCANNING);
46395
- }), switchMap((device) => onAdvertisementReceived(device)), switchMap((advertisement) => __awaiter$d(this, void 0, void 0, function* () {
46396
- this.addLog(`Advertisement received for ${advertisement.device.name}`);
46397
- return yield this.getServerServiceAndCharacteristics(advertisement.device);
46248
+ ? merge(selectedDevice$, selectedDeviceAfterDisconnect$)
46249
+ : NEVER), switchMap((selectedDevice) => this.scan().pipe(switchMap((peripherals) => {
46250
+ const peripheralMatch = peripherals.find((peripheral) => peripheral.name === (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceNickname));
46251
+ return peripheralMatch ? of(peripheralMatch) : NEVER;
46252
+ }), distinct((peripheral) => peripheral.id), take(1))), switchMap((peripheral) => __awaiter$e(this, void 0, void 0, function* () {
46253
+ return yield this.connect(peripheral);
46398
46254
  })));
46399
46255
  }
46400
46256
  enableAutoConnect(autoConnect) {
46401
46257
  this._isAutoConnectEnabled$.next(autoConnect);
46402
46258
  }
46403
- addLog(log) {
46404
- this.logs$.next(log);
46405
- }
46406
- isConnected() {
46407
- const connection = this.connection$.getValue();
46408
- return connection === BLUETOOTH_CONNECTION.CONNECTED;
46409
- }
46410
46259
  connection() {
46411
46260
  return this.connectionStream$;
46412
46261
  }
46413
- connect(deviceNickname) {
46414
- return __awaiter$d(this, void 0, void 0, function* () {
46415
- try {
46416
- // requires user gesture
46417
- const device = yield this.requestDevice(deviceNickname);
46418
- yield this.getServerServiceAndCharacteristics(device);
46419
- }
46420
- catch (error) {
46421
- return Promise.reject(error);
46422
- }
46423
- });
46262
+ _fromEvent(eventName) {
46263
+ return fromEventPattern((addHandler) => {
46264
+ this.bleManagerEmitter.addListener(eventName, addHandler);
46265
+ }, () => {
46266
+ this.bleManagerEmitter.removeAllListeners(eventName);
46267
+ }).pipe(
46268
+ // @important: we need to share the subscription
46269
+ // to avoid missing events
46270
+ share());
46424
46271
  }
46425
- requestDevice(deviceNickname) {
46426
- return __awaiter$d(this, void 0, void 0, function* () {
46272
+ scan(options) {
46273
+ var _a, _b, _c;
46274
+ const RESCAN_INTERVAL = 10000; // 10 seconds
46275
+ const seconds = (_a = options === null || options === void 0 ? void 0 : options.seconds) !== null && _a !== void 0 ? _a : RESCAN_INTERVAL / 1000;
46276
+ const once = (_b = options === null || options === void 0 ? void 0 : options.once) !== null && _b !== void 0 ? _b : false;
46277
+ // If we are already connected to a peripheral and start scanning,
46278
+ // be default, it will set the connection status to SCANNING and not
46279
+ // update it back if no device is connected to
46280
+ const skipConnectionUpdate = (_c = options === null || options === void 0 ? void 0 : options.skipConnectionUpdate) !== null && _c !== void 0 ? _c : false;
46281
+ const serviceUUIDs = [BLUETOOTH_PRIMARY_SERVICE_UUID_STRING];
46282
+ const allowDuplicates = true;
46283
+ const scanOptions = {};
46284
+ const scanOnce$ = new Observable((subscriber) => {
46285
+ var _a;
46427
46286
  try {
46428
- this.addLog("Requesting Bluetooth Device...");
46429
- const prefixes = BLUETOOTH_DEVICE_NAME_PREFIXES.map((namePrefix) => ({
46430
- namePrefix
46431
- }));
46432
- // Ability to only show selectedDevice if provided
46433
- const filters = deviceNickname
46434
- ? [
46435
- {
46436
- name: deviceNickname
46437
- }
46438
- ]
46439
- : prefixes;
46440
- const device = yield window.navigator.bluetooth.requestDevice({
46441
- filters: [
46442
- ...filters,
46443
- {
46444
- manufacturerData: [
46445
- {
46446
- companyIdentifier: BLUETOOTH_COMPANY_IDENTIFIER_HEX
46447
- }
46448
- ]
46449
- }
46450
- ],
46451
- optionalServices: [BLUETOOTH_PRIMARY_SERVICE_UUID_HEX]
46287
+ this.BleManager.scan(serviceUUIDs, seconds, allowDuplicates, scanOptions).then(() => {
46288
+ this.addLog(`BleManger scanning ${once ? "once" : "indefintely"}`);
46289
+ subscriber.next();
46452
46290
  });
46453
- return device;
46454
46291
  }
46455
46292
  catch (error) {
46456
- return Promise.reject(error);
46293
+ this.addLog(`BleManger scanning ${once ? "once" : "indefintely"} failed. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46294
+ subscriber.error(error);
46457
46295
  }
46296
+ return () => {
46297
+ this.BleManager.stopScan();
46298
+ };
46458
46299
  });
46300
+ const scan$ = once
46301
+ ? scanOnce$
46302
+ : timer(0, RESCAN_INTERVAL).pipe(switchMap(() => scanOnce$));
46303
+ const peripherals$ = scan$.pipe(tap(() => {
46304
+ if (!skipConnectionUpdate) {
46305
+ this.connection$.next(BLUETOOTH_CONNECTION.SCANNING);
46306
+ }
46307
+ }), takeUntil(this.onDisconnected$), switchMap(() => this.bleEvents.discoverPeripheral$),
46308
+ // Filter out devices that are not Neurosity devices
46309
+ filter((peripheral) => {
46310
+ var _a, _b, _c;
46311
+ const peripheralName = (_c = (_b = (_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _a === void 0 ? void 0 : _a.localName) !== null && _b !== void 0 ? _b : peripheral.name) !== null && _c !== void 0 ? _c : "";
46312
+ if (!peripheralName) {
46313
+ return false;
46314
+ }
46315
+ const startsWithPrefix = BLUETOOTH_DEVICE_NAME_PREFIXES.findIndex((prefix) => peripheralName.startsWith(prefix)) !== -1;
46316
+ return startsWithPrefix;
46317
+ }), scan((acc, peripheral) => {
46318
+ var _a, _b, _c, _d, _e, _f, _g, _h;
46319
+ // normalized peripheral name for backwards compatibility
46320
+ // Neurosity OS v15 doesn't have peripheral.name as deviceNickname
46321
+ // it only has peripheral.advertising.localName as deviceNickname
46322
+ // and OS v16 has both as deviceNickname
46323
+ const peripheralName = (_c = (_b = (_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _a === void 0 ? void 0 : _a.localName) !== null && _b !== void 0 ? _b : peripheral.name) !== null && _c !== void 0 ? _c : "";
46324
+ const manufactureDataString = (_h = (_g = decode$1(this.type, (_f = (_e = (_d = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _d === void 0 ? void 0 : _d.manufacturerData) === null || _e === void 0 ? void 0 : _e.bytes) !== null && _f !== void 0 ? _f : [])) === null || _g === void 0 ? void 0 : _g.slice) === null || _h === void 0 ? void 0 : _h.call(_g, 2); // First 2 bytes are reserved for the Neurosity company code
46325
+ return Object.assign(Object.assign({}, acc), { [peripheral.id]: Object.assign(Object.assign({}, peripheral), { name: peripheralName, manufactureDataString }) });
46326
+ }, {}), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), map((peripheralMap) => Object.values(peripheralMap)), share());
46327
+ return peripherals$;
46459
46328
  }
46460
- getServerServiceAndCharacteristics(device) {
46461
- return __awaiter$d(this, void 0, void 0, function* () {
46462
- try {
46463
- this.device = device;
46464
- const isConnecting = this.connection$.getValue() === BLUETOOTH_CONNECTION.CONNECTING;
46465
- if (!isConnecting) {
46329
+ connect(peripheral) {
46330
+ return __awaiter$e(this, void 0, void 0, function* () {
46331
+ return new Promise((resolve, reject) => __awaiter$e(this, void 0, void 0, function* () {
46332
+ try {
46333
+ if (!peripheral) {
46334
+ this.addLog("Peripheral not found");
46335
+ return;
46336
+ }
46466
46337
  this.connection$.next(BLUETOOTH_CONNECTION.CONNECTING);
46338
+ yield this.BleManager.connect(peripheral.id);
46339
+ this.addLog(`Getting service...`);
46340
+ const peripheralInfo = yield this.BleManager.retrieveServices(peripheral.id, [
46341
+ BLUETOOTH_PRIMARY_SERVICE_UUID_STRING
46342
+ ]);
46343
+ if (!peripheralInfo) {
46344
+ this.addLog("Could not retreive services");
46345
+ reject(new Error(`Could not retreive services`));
46346
+ return;
46347
+ }
46348
+ this.addLog(`Got service ${BLUETOOTH_PRIMARY_SERVICE_UUID_STRING}, getting characteristics...`);
46349
+ this.device = peripheral;
46350
+ this.characteristicsByName = Object.fromEntries(peripheralInfo.characteristics.map((characteristic) => [
46351
+ CHARACTERISTIC_UUIDS_TO_NAMES[characteristic.characteristic.toLowerCase() // react native uses uppercase
46352
+ ],
46353
+ {
46354
+ characteristicUUID: characteristic.characteristic,
46355
+ serviceUUID: characteristic.service,
46356
+ peripheralId: peripheral.id
46357
+ }
46358
+ ]));
46359
+ this.addLog(`Got characteristics.`);
46360
+ if (this.platform === "android") {
46361
+ yield this.BleManager.requestMTU(peripheral.id, ANDROID_MAX_MTU)
46362
+ .then((updatedMTU) => {
46363
+ this.addLog(`Successfully updated Android MTU to ${updatedMTU} bytes. Requested MTU: ${ANDROID_MAX_MTU} bytes.`);
46364
+ })
46365
+ .catch((error) => {
46366
+ this.addLog(`Failed to set Android MTU of ${ANDROID_MAX_MTU} bytes. Error: ${error}`);
46367
+ });
46368
+ }
46369
+ this.addLog(`Successfully connected to peripheral ${peripheral.id}`);
46370
+ this.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
46371
+ resolve();
46467
46372
  }
46468
- this.server = yield device.gatt.connect();
46469
- this.addLog(`Getting service...`);
46470
- this.service = yield this.server.getPrimaryService(BLUETOOTH_PRIMARY_SERVICE_UUID_HEX);
46471
- this.addLog(`Got service ${this.service.uuid}, getting characteristics...`);
46472
- const characteristicsList = yield this.service.getCharacteristics();
46473
- this.addLog(`Got characteristics`);
46474
- this.characteristicsByName = Object.fromEntries(characteristicsList.map((characteristic) => [
46475
- CHARACTERISTIC_UUIDS_TO_NAMES[characteristic.uuid],
46476
- characteristic
46477
- ]));
46478
- this.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
46479
- }
46480
- catch (error) {
46481
- return Promise.reject(error);
46482
- }
46373
+ catch (error) {
46374
+ reject(error);
46375
+ }
46376
+ }));
46483
46377
  });
46484
46378
  }
46485
- _onDisconnected() {
46486
- return this.connection$
46487
- .asObservable()
46488
- .pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46489
- ? fromDOMEvent(this.device, "gattserverdisconnected")
46490
- : NEVER));
46491
- }
46492
46379
  disconnect() {
46493
- var _a, _b;
46494
- return __awaiter$d(this, void 0, void 0, function* () {
46495
- const isDeviceConnected = (_b = (_a = this === null || this === void 0 ? void 0 : this.device) === null || _a === void 0 ? void 0 : _a.gatt) === null || _b === void 0 ? void 0 : _b.connected;
46496
- if (isDeviceConnected) {
46497
- this.device.gatt.disconnect();
46380
+ var _a;
46381
+ return __awaiter$e(this, void 0, void 0, function* () {
46382
+ try {
46383
+ if (this.isConnected() && ((_a = this === null || this === void 0 ? void 0 : this.device) === null || _a === void 0 ? void 0 : _a.id)) {
46384
+ yield this.BleManager.disconnect(this.device.id);
46385
+ }
46386
+ }
46387
+ catch (error) {
46388
+ return Promise.reject(error);
46498
46389
  }
46499
46390
  });
46500
46391
  }
46501
- /**
46502
- *
46503
- * Bluetooth GATT attributes, services, characteristics, etc. are invalidated
46504
- * when a device disconnects. This means your code should always retrieve
46505
- * (through getPrimaryService(s), getCharacteristic(s), etc.) these attributes
46506
- * after reconnecting.
46507
- */
46508
46392
  getCharacteristicByName(characteristicName) {
46509
46393
  var _a;
46510
- return __awaiter$d(this, void 0, void 0, function* () {
46511
- return (_a = this.characteristicsByName) === null || _a === void 0 ? void 0 : _a[characteristicName];
46512
- });
46394
+ if (!(characteristicName in this.characteristicsByName)) {
46395
+ throw new Error(`Characteristic by name ${characteristicName} is not found`);
46396
+ }
46397
+ return (_a = this.characteristicsByName) === null || _a === void 0 ? void 0 : _a[characteristicName];
46513
46398
  }
46514
46399
  subscribeToCharacteristic({ characteristicName, manageNotifications = true }) {
46515
- const data$ = defer(() => this.getCharacteristicByName(characteristicName)).pipe(switchMap((characteristic) => __awaiter$d(this, void 0, void 0, function* () {
46400
+ const getData = ({ peripheralId, serviceUUID, characteristicUUID }) => defer(() => __awaiter$e(this, void 0, void 0, function* () {
46516
46401
  var _a;
46517
- if (this.isConnected() && manageNotifications) {
46402
+ if (manageNotifications) {
46518
46403
  try {
46519
- yield characteristic.startNotifications();
46404
+ yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
46520
46405
  this.addLog(`Started notifications for ${characteristicName} characteristic`);
46521
46406
  }
46522
46407
  catch (error) {
46523
46408
  this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46524
46409
  }
46525
46410
  }
46526
- return characteristic;
46527
- })), switchMap((characteristic) => {
46528
- return fromDOMEvent(characteristic, "characteristicvaluechanged", () => __awaiter$d(this, void 0, void 0, function* () {
46529
- var _a;
46530
- if (this.isConnected() && manageNotifications) {
46531
- try {
46532
- yield characteristic.stopNotifications();
46533
- this.addLog(`Stopped notifications for ${characteristicName} characteristic`);
46534
- }
46535
- catch (error) {
46536
- this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46537
- }
46411
+ })).pipe(switchMap(() => this.bleEvents.didUpdateValueForCharacteristic$), finalize(() => __awaiter$e(this, void 0, void 0, function* () {
46412
+ var _b;
46413
+ if (manageNotifications) {
46414
+ try {
46415
+ yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
46416
+ this.addLog(`Stopped notifications for ${characteristicName} characteristic`);
46538
46417
  }
46539
- }));
46540
- }), map((event) => {
46541
- const buffer$$1 = event.target.value;
46542
- const decoded = decode$1(this.type, buffer$$1);
46543
- this.addLog(`Received chunk with buffer size of ${buffer$$1.byteLength} and decoded size ${decoded.length} for ${characteristicName} characteristic: \n${decoded}`);
46544
- return decoded;
46545
- }), stitchChunks({ delimiter: BLUETOOTH_CHUNK_DELIMITER }), map((payload) => {
46418
+ catch (error) {
46419
+ this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
46420
+ }
46421
+ }
46422
+ })), filter(({ characteristic }) => characteristic === characteristicUUID), map(({ value }) => decode$1(this.type, value)), stitchChunks({ delimiter: BLUETOOTH_CHUNK_DELIMITER }), map((payload) => {
46546
46423
  var _a;
46547
46424
  try {
46548
46425
  return JSON.parse(payload);
@@ -46551,51 +46428,40 @@ class WebBluetoothTransport {
46551
46428
  this.addLog(`Failed to parse JSON for ${characteristicName} characteristic. Falling back to unparsed string. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46552
46429
  return payload;
46553
46430
  }
46554
- })
46555
- // when streaming at ultra-low latency, the logs will slow down rendering
46556
- // tap((data) => {
46557
- // this.addLog(
46558
- // `Received data for ${characteristicName} characteristic: \n${JSON.stringify(
46559
- // data,
46560
- // null,
46561
- // 2
46562
- // )}`
46563
- // );
46564
- // })
46565
- );
46566
- return this.connection$.pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED ? data$ : NEVER));
46431
+ }));
46432
+ return this.connection$.pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46433
+ ? getData(this.getCharacteristicByName(characteristicName))
46434
+ : NEVER));
46567
46435
  }
46568
46436
  readCharacteristic(characteristicName, parse = false) {
46569
- return __awaiter$d(this, void 0, void 0, function* () {
46437
+ var _a;
46438
+ return __awaiter$e(this, void 0, void 0, function* () {
46439
+ this.addLog(`Reading characteristic: ${characteristicName}`);
46440
+ const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName(characteristicName);
46441
+ if (!characteristicUUID) {
46442
+ return Promise.reject(new Error(`Did not find characteristic matching ${characteristicName}`));
46443
+ }
46570
46444
  try {
46571
- this.addLog(`Reading characteristic: ${characteristicName}`);
46572
- const characteristic = yield this.getCharacteristicByName(characteristicName);
46573
- if (!characteristic) {
46574
- this.addLog(`Did not fund ${characteristicName} characteristic`);
46575
- return Promise.reject(`Did not find characteristic by the name: ${characteristicName}`);
46576
- }
46577
- const value = yield characteristic.readValue();
46578
- const uint8Array = value;
46579
- const decodedValue = decode$1(this.type, uint8Array);
46445
+ const value = yield this.BleManager.read(peripheralId, serviceUUID, characteristicUUID);
46446
+ const decodedValue = decode$1(this.type, value);
46580
46447
  const data = parse ? JSON.parse(decodedValue) : decodedValue;
46581
46448
  this.addLog(`Received read data from ${characteristicName} characteristic: \n${data}`);
46582
46449
  return data;
46583
46450
  }
46584
46451
  catch (error) {
46585
- return Promise.reject(`Error reading characteristic: ${error.message}`);
46452
+ return Promise.reject(new Error(`readCharacteristic ${characteristicName} error. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`));
46586
46453
  }
46587
46454
  });
46588
46455
  }
46589
46456
  writeCharacteristic(characteristicName, data) {
46590
- return __awaiter$d(this, void 0, void 0, function* () {
46457
+ return __awaiter$e(this, void 0, void 0, function* () {
46591
46458
  this.addLog(`Writing characteristic: ${characteristicName}`);
46592
- const characteristic = yield this.getCharacteristicByName(characteristicName);
46593
- if (!characteristic) {
46594
- this.addLog(`Did not fund ${characteristicName} characteristic`);
46595
- return Promise.reject(`Did not find characteristic by the name: ${characteristicName}`);
46459
+ const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName(characteristicName);
46460
+ if (!characteristicUUID) {
46461
+ return Promise.reject(new Error(`Did not find characteristic matching ${characteristicName}`));
46596
46462
  }
46597
46463
  const encoded = encode$1(this.type, data);
46598
- yield characteristic.writeValueWithResponse(encoded);
46464
+ yield this.BleManager.write(peripheralId, serviceUUID, characteristicUUID, encoded, REACT_NATIVE_MAX_BYTE_SIZE);
46599
46465
  });
46600
46466
  }
46601
46467
  _addPendingAction(actionId) {
@@ -46606,54 +46472,40 @@ class WebBluetoothTransport {
46606
46472
  const actions = this.pendingActions$.getValue();
46607
46473
  this.pendingActions$.next(actions.filter((id) => id !== actionId));
46608
46474
  }
46609
- _autoToggleActionNotifications(selectedDevice$) {
46610
- return __awaiter$d(this, void 0, void 0, function* () {
46611
- let actionsCharacteristic;
46612
- let started = false;
46613
- const sideEffects$ = this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46614
- ? defer(() => this.getCharacteristicByName("actions")).pipe(switchMap((characteristic) => {
46615
- actionsCharacteristic = characteristic;
46616
- return this.pendingActions$;
46617
- }))
46618
- : NEVER), tap((pendingActions) => __awaiter$d(this, void 0, void 0, function* () {
46619
- var _a, _b;
46620
- const hasPendingActions = !!pendingActions.length;
46621
- if (hasPendingActions && !started) {
46622
- started = true;
46623
- try {
46624
- yield actionsCharacteristic.startNotifications();
46625
- this.addLog(`Started notifications for [actions] characteristic`);
46626
- }
46627
- catch (error) {
46628
- this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46629
- }
46475
+ _autoToggleActionNotifications() {
46476
+ let started = false;
46477
+ return this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46478
+ ? this.pendingActions$
46479
+ : NEVER), tap((pendingActions) => __awaiter$e(this, void 0, void 0, function* () {
46480
+ var _a, _b;
46481
+ const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName("actions");
46482
+ const hasPendingActions = !!pendingActions.length;
46483
+ if (hasPendingActions && !started) {
46484
+ started = true;
46485
+ try {
46486
+ yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
46487
+ this.addLog(`Started notifications for [actions] characteristic`);
46630
46488
  }
46631
- if (!hasPendingActions && started) {
46632
- started = false;
46633
- try {
46634
- yield actionsCharacteristic.stopNotifications();
46635
- this.addLog(`Stopped notifications for actions characteristic`);
46636
- }
46637
- catch (error) {
46638
- this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
46639
- }
46489
+ catch (error) {
46490
+ this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46640
46491
  }
46641
- })));
46642
- selectedDevice$
46643
- .pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice) ? EMPTY : sideEffects$))
46644
- .subscribe();
46645
- });
46492
+ }
46493
+ if (!hasPendingActions && started) {
46494
+ started = false;
46495
+ try {
46496
+ yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
46497
+ this.addLog(`Stopped notifications for actions characteristic`);
46498
+ }
46499
+ catch (error) {
46500
+ this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
46501
+ }
46502
+ }
46503
+ })));
46646
46504
  }
46647
46505
  dispatchAction({ characteristicName, action }) {
46648
- return __awaiter$d(this, void 0, void 0, function* () {
46506
+ return __awaiter$e(this, void 0, void 0, function* () {
46649
46507
  const { responseRequired = false, responseTimeout = DEFAULT_ACTION_RESPONSE_TIMEOUT } = action;
46650
- return new Promise((resolve, reject) => __awaiter$d(this, void 0, void 0, function* () {
46651
- const characteristic = yield this.getCharacteristicByName(characteristicName).catch(() => {
46652
- reject(`Did not find characteristic by the name: ${characteristicName}`);
46653
- });
46654
- if (!characteristic) {
46655
- return;
46656
- }
46508
+ return new Promise((resolve, reject) => __awaiter$e(this, void 0, void 0, function* () {
46657
46509
  const actionId = create6DigitPin(); // use to later identify and filter response
46658
46510
  const payload = JSON.stringify(Object.assign({ actionId }, action)); // add the response id to the action
46659
46511
  this.addLog(`Dispatched action with id ${actionId}`);
@@ -46661,7 +46513,7 @@ class WebBluetoothTransport {
46661
46513
  this._addPendingAction(actionId);
46662
46514
  const timeout$$1 = timer(responseTimeout).subscribe(() => {
46663
46515
  this._removePendingAction(actionId);
46664
- reject(`Action with id ${actionId} timed out after ${responseTimeout}ms`);
46516
+ reject(new Error(`Action with id ${actionId} timed out after ${responseTimeout}ms`));
46665
46517
  });
46666
46518
  // listen for a response before writing
46667
46519
  this.subscribeToCharacteristic({
@@ -46677,7 +46529,7 @@ class WebBluetoothTransport {
46677
46529
  // register action by writing
46678
46530
  this.writeCharacteristic(characteristicName, payload).catch((error) => {
46679
46531
  this._removePendingAction(actionId);
46680
- reject(error.message);
46532
+ reject(error);
46681
46533
  });
46682
46534
  }
46683
46535
  else {
@@ -46686,48 +46538,108 @@ class WebBluetoothTransport {
46686
46538
  resolve(null);
46687
46539
  })
46688
46540
  .catch((error) => {
46689
- reject(error.message);
46541
+ reject(error);
46690
46542
  });
46691
46543
  }
46692
46544
  }));
46693
46545
  });
46694
46546
  }
46695
46547
  }
46696
- function fromDOMEvent(target, eventName, beforeRemove) {
46697
- return fromEventPattern((addHandler) => {
46698
- target.addEventListener(eventName, addHandler);
46699
- }, (removeHandler) => __awaiter$d(this, void 0, void 0, function* () {
46700
- if (beforeRemove) {
46701
- yield beforeRemove();
46702
- }
46703
- target.removeEventListener(eventName, removeHandler);
46548
+
46549
+ const defaultDataProp = "data";
46550
+ const defaultSamplingRate = 256;
46551
+ const isObject$2 = (object) => object instanceof Object && object === Object(object);
46552
+ const isFunction$2 = (object) => typeof object === "function";
46553
+ const patch = (sample$$1) => (info) => {
46554
+ var _a;
46555
+ return (Object.assign(Object.assign({}, sample$$1), { info: Object.assign(Object.assign({}, ((_a = sample$$1 === null || sample$$1 === void 0 ? void 0 : sample$$1.info) !== null && _a !== void 0 ? _a : {})), (info || {})) }));
46556
+ };
46557
+ /**
46558
+ * Annotates stream with user-defined metadata
46559
+ * @method addInfo
46560
+ * @example eeg$.pipe(addinfo({ samplingRate: 256, channelNames: ["Af7", "Fp1", "Fp2", "Af8"] })
46561
+ * @param {Object} info Info to be added to the EEG stream. Relevant info may include: `samplingRate` and `channelNames`
46562
+ * @returns {Observable<Sample|Epoch|PSD>}
46563
+ */
46564
+ const addInfo = (infoValue) => pipe(map((sample$$1) => {
46565
+ if (!isObject$2(sample$$1) ||
46566
+ (!isObject$2(infoValue) && !isFunction$2(infoValue))) {
46567
+ return sample$$1;
46568
+ }
46569
+ const info = isFunction$2(infoValue) ? infoValue(sample$$1) : infoValue;
46570
+ return patch(sample$$1)(info);
46571
+ }));
46572
+ /**
46573
+ * Get a 2D data array organized by channel from an array of Samples. Credit to Ken from Seattle's elegant transposition
46574
+ * http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
46575
+ * @method groupByChannel
46576
+ * @param {Array<Sample>} samplesBuffer Array of Samples to be grouped
46577
+ * @param {string} [dataProp] Name of the key associated with EEG data
46578
+ * @returns {Array<Array<number>>}
46579
+ */
46580
+ const groupByChannel = (samplesBuffer, dataProp = defaultDataProp) => samplesBuffer[0][dataProp].map((_, channelIndex) => samplesBuffer.map((sample$$1) => sample$$1[dataProp][channelIndex]));
46581
+ /**
46582
+ * Takes an array or RxJS buffer of EEG Samples and returns an Epoch.
46583
+ * @method bufferToEpoch
46584
+ * @example eeg$.pipe(bufferTime(1000), bufferToEpoch({ samplingRate: 256 }))
46585
+ *
46586
+ * @param {Object} options - Data structure options
46587
+ * @param {number} [options.samplingRate] Sampling rate
46588
+ * @param {string} [options.dataProp='data'] Name of the key associated with eeg data
46589
+ *
46590
+ * @returns {Observable<Epoch>}
46591
+ */
46592
+ const bufferToEpoch = ({ samplingRate = defaultSamplingRate, dataProp = defaultDataProp } = {}) => pipe(map((samplesArray) => ({
46593
+ [dataProp]: groupByChannel(samplesArray, dataProp),
46594
+ info: Object.assign(Object.assign({}, (samplesArray[0] && samplesArray[0].info
46595
+ ? samplesArray[0].info
46596
+ : {})), { startTime: samplesArray[0].timestamp, samplingRate: samplesArray[0].info && samplesArray[0].info.samplingRate
46597
+ ? samplesArray[0].info.samplingRate
46598
+ : samplingRate })
46599
+ })));
46600
+ /**
46601
+ * Converts a stream of individual Samples of EEG data into a stream of Epochs of a given duration emitted at specified interval. This operator functions similarly to a circular buffer internally and allows overlapping Epochs of data to be emitted (e.g. emitting the last one second of data every 100ms).
46602
+ * @method epoch
46603
+ * @example eeg$.pipe(epoch({ duration: 1024, interval: 100, samplingRate: 256 }))
46604
+ * @param {Object} options - Epoching options
46605
+ * @param {number} [options.duration=256] Number of samples to include in each epoch
46606
+ * @param {number} [options.interval=100] Time (ms) between emitted Epochs
46607
+ * @param {number} [options.samplingRate=256] Sampling rate
46608
+ * @param {string} [options.dataProp='data'] Name of the key associated with eeg data
46609
+ * @returns {Observable} Epoch
46610
+ */
46611
+ const epoch = ({ duration, interval: interval$$1, samplingRate, dataProp = defaultDataProp }) => pipe(bufferCount(interval$$1), scan((acc, val) => acc.concat(val).slice(acc.length < duration ? 0 : -duration)), filter((samplesArray) => samplesArray.length === duration), bufferToEpoch({ samplingRate, dataProp }));
46612
+
46613
+ const EPOCH_BUFFER_SIZE = 16;
46614
+ const SAMPLING_RATE_FALLBACK = 256; // Crown's sampling rate
46615
+ /**
46616
+ * @hidden
46617
+ */
46618
+ function csvBufferToEpoch(deviceInfo) {
46619
+ var _a;
46620
+ if (!(deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate)) {
46621
+ console.warn(`Didn't receive a sampling rate, defaulting to ${SAMPLING_RATE_FALLBACK}`);
46622
+ }
46623
+ return pipe(csvBufferToSamples(), epoch({
46624
+ duration: EPOCH_BUFFER_SIZE,
46625
+ interval: EPOCH_BUFFER_SIZE,
46626
+ samplingRate: (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate) !== null && _a !== void 0 ? _a : SAMPLING_RATE_FALLBACK
46627
+ }), addInfo({
46628
+ channelNames: deviceInfo.channelNames,
46629
+ samplingRate: deviceInfo.samplingRate
46704
46630
  }));
46705
46631
  }
46706
- function onAdvertisementReceived(device) {
46707
- return new Observable((subscriber) => {
46708
- const abortController = new AbortController();
46709
- const { signal } = abortController;
46710
- const listener = device.addEventListener("advertisementreceived", (advertisement) => {
46711
- abortController.abort();
46712
- subscriber.next(advertisement);
46713
- subscriber.complete();
46714
- }, {
46715
- once: true
46716
- });
46717
- try {
46718
- device.watchAdvertisements({ signal });
46719
- }
46720
- catch (error) {
46721
- subscriber.error(error);
46722
- }
46723
- return () => {
46724
- abortController.abort();
46725
- device.removeEventListener("advertisementreceived", listener);
46726
- };
46727
- });
46632
+ /**
46633
+ * @hidden
46634
+ */
46635
+ function csvBufferToSamples() {
46636
+ return pipe(mergeMap((samples) => from(samples)), map(([timestamp$$1, marker, ...data]) => ({
46637
+ timestamp: timestamp$$1,
46638
+ data
46639
+ })));
46728
46640
  }
46729
46641
 
46730
- var __awaiter$e = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
46642
+ var __awaiter$f = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
46731
46643
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
46732
46644
  return new (P || (P = Promise))(function (resolve, reject) {
46733
46645
  function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
@@ -46736,783 +46648,872 @@ var __awaiter$e = (undefined && undefined.__awaiter) || function (thisArg, _argu
46736
46648
  step((generator = generator.apply(thisArg, _arguments || [])).next());
46737
46649
  });
46738
46650
  };
46739
- const defaultOptions$2 = {
46740
- autoConnect: true
46741
- };
46742
- class ReactNativeTransport {
46651
+ class BluetoothClient {
46743
46652
  constructor(options) {
46744
- this.type = TRANSPORT_TYPE.REACT_NATIVE;
46745
- this.characteristicsByName = {};
46746
- this.connection$ = new BehaviorSubject(BLUETOOTH_CONNECTION.DISCONNECTED);
46747
- this.pendingActions$ = new BehaviorSubject([]);
46748
- this.logs$ = new ReplaySubject(10);
46749
- this.connectionStream$ = this.connection$
46750
- .asObservable()
46751
- .pipe(filter((connection) => !!connection), distinctUntilChanged(), shareReplay(1));
46752
- this._isAutoConnectEnabled$ = new ReplaySubject(1);
46753
- if (!options) {
46754
- const errorMessage = "React Native transport: missing options.";
46755
- this.addLog(errorMessage);
46756
- throw new Error(errorMessage);
46757
- }
46758
- this.options = Object.assign(Object.assign({}, defaultOptions$2), options);
46759
- const { BleManager, bleManagerEmitter, platform, autoConnect } = this.options;
46760
- if (!BleManager) {
46761
- const errorMessage = "React Native option: BleManager not provided.";
46762
- this.addLog(errorMessage);
46763
- throw new Error(errorMessage);
46653
+ this.selectedDevice$ = new ReplaySubject(1);
46654
+ this.osHasBluetoothSupport$ = new ReplaySubject(1);
46655
+ this.isAuthenticated$ = new ReplaySubject(1);
46656
+ const { transport, selectedDevice$, osHasBluetoothSupport$, createBluetoothToken } = options !== null && options !== void 0 ? options : {};
46657
+ if (!transport) {
46658
+ throw new Error(`No bluetooth transport provided.`);
46764
46659
  }
46765
- if (!bleManagerEmitter) {
46766
- const errorMessage = "React Native option: bleManagerEmitter not provided.";
46767
- this.addLog(errorMessage);
46768
- throw new Error(errorMessage);
46660
+ this.transport = transport;
46661
+ // Pass events to the internal selectedDevice$ if selectedDevice$ is passed via options
46662
+ if (selectedDevice$) {
46663
+ selectedDevice$.subscribe(this.selectedDevice$);
46769
46664
  }
46770
- if (!platform) {
46771
- const errorMessage = "React Native option: platform not provided.";
46772
- this.addLog(errorMessage);
46773
- throw new Error(errorMessage);
46665
+ // Pass events to the internal osHasBluetoothSupport$ if osHasBluetoothSupport$ is passed via options
46666
+ if (osHasBluetoothSupport$) {
46667
+ osHasBluetoothSupport$.subscribe(this.osHasBluetoothSupport$);
46774
46668
  }
46775
- this.BleManager = BleManager;
46776
- this.bleManagerEmitter = bleManagerEmitter;
46777
- this.platform = platform;
46778
- this._isAutoConnectEnabled$.next(autoConnect);
46779
- this._isAutoConnectEnabled$.subscribe((autoConnect) => {
46780
- this.addLog(`Auto connect: ${autoConnect ? "enabled" : "disabled"}`);
46781
- });
46782
- // We create a single listener per event type to
46783
- // avoid missing events when multiple listeners are attached.
46784
- this.bleEvents = {
46785
- stopScan$: this._fromEvent("BleManagerStopScan"),
46786
- discoverPeripheral$: this._fromEvent("BleManagerDiscoverPeripheral"),
46787
- connectPeripheral$: this._fromEvent("BleManagerConnectPeripheral"),
46788
- disconnectPeripheral$: this._fromEvent("BleManagerDisconnectPeripheral"),
46789
- didUpdateValueForCharacteristic$: this._fromEvent("BleManagerDidUpdateValueForCharacteristic"),
46790
- didUpdateState$: this._fromEvent("BleManagerDidUpdateState")
46791
- };
46792
- this.onDisconnected$ = this.bleEvents.disconnectPeripheral$.pipe(share());
46793
- // Initializes the module. This can only be called once.
46794
- this.BleManager.start({ showAlert: false })
46795
- .then(() => {
46796
- this.addLog(`BleManger started`);
46797
- })
46798
- .catch((error) => {
46799
- var _a;
46800
- this.addLog(`BleManger failed to start. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46801
- });
46802
- this.connection$.asObservable().subscribe((connection) => {
46803
- this.addLog(`connection status is ${connection}`);
46669
+ this.osHasBluetoothSupport$
46670
+ .pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport
46671
+ ? this.transport._autoConnect(this.selectedDevice$)
46672
+ : EMPTY))
46673
+ .subscribe({
46674
+ error: (error) => {
46675
+ var _a;
46676
+ this.transport.addLog(`Auto connect: error -> ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46677
+ }
46804
46678
  });
46805
- this.onDisconnected$.subscribe(() => {
46806
- this.connection$.next(BLUETOOTH_CONNECTION.DISCONNECTED);
46807
- });
46808
- }
46809
- addLog(log) {
46810
- this.logs$.next(log);
46811
- }
46812
- isConnected() {
46813
- const connection = this.connection$.getValue();
46814
- return connection === BLUETOOTH_CONNECTION.CONNECTED;
46679
+ // Auto authentication
46680
+ if (typeof createBluetoothToken === "function") {
46681
+ this.transport.addLog("Auto authentication enabled");
46682
+ this._autoAuthenticate(createBluetoothToken).subscribe();
46683
+ }
46684
+ else {
46685
+ this.transport.addLog("Auto authentication not enabled");
46686
+ }
46687
+ // Auto manage action notifications
46688
+ this.osHasBluetoothSupport$
46689
+ .pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport
46690
+ ? this.transport._autoToggleActionNotifications()
46691
+ : EMPTY))
46692
+ .subscribe();
46693
+ // Multicast metrics (share)
46694
+ this._focus$ = this._subscribeWhileAuthenticated("focus");
46695
+ this._calm$ = this._subscribeWhileAuthenticated("calm");
46696
+ this._accelerometer$ = this._subscribeWhileAuthenticated("accelerometer");
46697
+ this._brainwavesRaw$ = this._subscribeWhileAuthenticated("raw");
46698
+ this._brainwavesRawUnfiltered$ =
46699
+ this._subscribeWhileAuthenticated("rawUnfiltered");
46700
+ this._brainwavesPSD$ = this._subscribeWhileAuthenticated("psd");
46701
+ this._brainwavesPowerByBand$ =
46702
+ this._subscribeWhileAuthenticated("powerByBand");
46703
+ this._signalQuality$ = this._subscribeWhileAuthenticated("signalQuality");
46704
+ this._status$ = this._subscribeWhileAuthenticated("status");
46705
+ this._settings$ = this._subscribeWhileAuthenticated("settings");
46706
+ this._wifiNearbyNetworks$ =
46707
+ this._subscribeWhileAuthenticated("wifiNearbyNetworks");
46708
+ this._wifiConnections$ =
46709
+ this._subscribeWhileAuthenticated("wifiConnections");
46815
46710
  }
46816
- _autoConnect(selectedDevice$) {
46817
- const selectedDeviceAfterDisconnect$ = this.onDisconnected$.pipe(switchMap(() => selectedDevice$));
46818
- return this._isAutoConnectEnabled$.pipe(switchMap((isAutoConnectEnabled) => isAutoConnectEnabled
46819
- ? merge(selectedDevice$, selectedDeviceAfterDisconnect$)
46820
- : NEVER), switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
46821
- ? NEVER
46822
- : this.scan().pipe(switchMap((peripherals) => {
46823
- const peripheralMatch = peripherals.find((peripheral) => peripheral.name === (selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.deviceNickname));
46824
- return peripheralMatch ? of(peripheralMatch) : NEVER;
46825
- }), distinct((peripheral) => peripheral.id), take(1))), switchMap((peripheral) => __awaiter$e(this, void 0, void 0, function* () {
46826
- return yield this.connect(peripheral);
46711
+ _autoAuthenticate(createBluetoothToken) {
46712
+ const REAUTHENTICATE_INTERVAL = 3600000; // 1 hour
46713
+ const reauthenticateInterval$ = timer(0, REAUTHENTICATE_INTERVAL).pipe(tap(() => {
46714
+ this.transport.addLog(`Auto authentication in progress...`);
46715
+ }));
46716
+ return this.osHasBluetoothSupport$.pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport ? this.connection() : EMPTY), switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
46717
+ ? reauthenticateInterval$
46718
+ : EMPTY), switchMap(() => __awaiter$f(this, void 0, void 0, function* () { return yield this.isAuthenticated(); })), tap(([isAuthenticated]) => __awaiter$f(this, void 0, void 0, function* () {
46719
+ if (!isAuthenticated) {
46720
+ const token = yield createBluetoothToken();
46721
+ yield this.authenticate(token);
46722
+ }
46723
+ else {
46724
+ this.transport.addLog(`Already authenticated`);
46725
+ }
46827
46726
  })));
46828
46727
  }
46829
46728
  enableAutoConnect(autoConnect) {
46830
- this._isAutoConnectEnabled$.next(autoConnect);
46831
- }
46832
- connection() {
46833
- return this.connectionStream$;
46729
+ this.transport.enableAutoConnect(autoConnect);
46834
46730
  }
46835
- _fromEvent(eventName) {
46836
- return fromEventPattern((addHandler) => {
46837
- this.bleManagerEmitter.addListener(eventName, addHandler);
46838
- }, () => {
46839
- this.bleManagerEmitter.removeAllListeners(eventName);
46840
- }).pipe(
46841
- // @important: we need to share the subscription
46842
- // to avoid missing events
46843
- share());
46731
+ _hasBluetoothSupport() {
46732
+ return __awaiter$f(this, void 0, void 0, function* () {
46733
+ return yield firstValueFrom(this.osHasBluetoothSupport$);
46734
+ });
46844
46735
  }
46845
- scan(options) {
46846
- var _a, _b, _c;
46847
- const RESCAN_INTERVAL = 10000; // 10 seconds
46848
- const seconds = (_a = options === null || options === void 0 ? void 0 : options.seconds) !== null && _a !== void 0 ? _a : RESCAN_INTERVAL / 1000;
46849
- const once = (_b = options === null || options === void 0 ? void 0 : options.once) !== null && _b !== void 0 ? _b : false;
46850
- // If we are already connected to a peripheral and start scanning,
46851
- // be default, it will set the connection status to SCANNING and not
46852
- // update it back if no device is connected to
46853
- const skipConnectionUpdate = (_c = options === null || options === void 0 ? void 0 : options.skipConnectionUpdate) !== null && _c !== void 0 ? _c : false;
46854
- const serviceUUIDs = [BLUETOOTH_PRIMARY_SERVICE_UUID_STRING];
46855
- const allowDuplicates = true;
46856
- const scanOptions = {};
46857
- const scanOnce$ = new Observable((subscriber) => {
46858
- var _a;
46859
- try {
46860
- this.BleManager.scan(serviceUUIDs, seconds, allowDuplicates, scanOptions).then(() => {
46861
- this.addLog(`BleManger scanning ${once ? "once" : "indefintely"}`);
46862
- subscriber.next();
46863
- });
46864
- }
46865
- catch (error) {
46866
- this.addLog(`BleManger scanning ${once ? "once" : "indefintely"} failed. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46867
- subscriber.error(error);
46736
+ authenticate(token) {
46737
+ return __awaiter$f(this, void 0, void 0, function* () {
46738
+ const hasBluetoothSupport = yield this._hasBluetoothSupport();
46739
+ if (!hasBluetoothSupport) {
46740
+ const errorMessage = `authenticate method: The OS version does not support Bluetooth.`;
46741
+ this.transport.addLog(errorMessage);
46742
+ return Promise.reject(errorMessage);
46868
46743
  }
46869
- return () => {
46870
- this.BleManager.stopScan();
46871
- };
46744
+ yield this.transport.writeCharacteristic("auth", token);
46745
+ const isAuthenticatedResponse = yield this.isAuthenticated();
46746
+ const [isAuthenticated] = isAuthenticatedResponse;
46747
+ this.transport.addLog(`Authentication ${isAuthenticated ? "succeeded" : "failed"}`);
46748
+ this.isAuthenticated$.next(isAuthenticated);
46749
+ return isAuthenticatedResponse;
46872
46750
  });
46873
- const scan$ = once
46874
- ? scanOnce$
46875
- : timer(0, RESCAN_INTERVAL).pipe(switchMap(() => scanOnce$));
46876
- const peripherals$ = scan$.pipe(tap(() => {
46877
- if (!skipConnectionUpdate) {
46878
- this.connection$.next(BLUETOOTH_CONNECTION.SCANNING);
46879
- }
46880
- }), takeUntil(this.onDisconnected$), switchMap(() => this.bleEvents.discoverPeripheral$),
46881
- // Filter out devices that are not Neurosity devices
46882
- filter((peripheral) => {
46883
- var _a, _b, _c;
46884
- const peripheralName = (_c = (_b = (_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _a === void 0 ? void 0 : _a.localName) !== null && _b !== void 0 ? _b : peripheral.name) !== null && _c !== void 0 ? _c : "";
46885
- if (!peripheralName) {
46886
- return false;
46887
- }
46888
- const startsWithPrefix = BLUETOOTH_DEVICE_NAME_PREFIXES.findIndex((prefix) => peripheralName.startsWith(prefix)) !== -1;
46889
- return startsWithPrefix;
46890
- }), scan((acc, peripheral) => {
46891
- var _a, _b, _c, _d, _e, _f, _g, _h;
46892
- // normalized peripheral name for backwards compatibility
46893
- // Neurosity OS v15 doesn't have peripheral.name as deviceNickname
46894
- // it only has peripheral.advertising.localName as deviceNickname
46895
- // and OS v16 has both as deviceNickname
46896
- const peripheralName = (_c = (_b = (_a = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _a === void 0 ? void 0 : _a.localName) !== null && _b !== void 0 ? _b : peripheral.name) !== null && _c !== void 0 ? _c : "";
46897
- const manufactureDataString = (_h = (_g = decode$1(this.type, (_f = (_e = (_d = peripheral === null || peripheral === void 0 ? void 0 : peripheral.advertising) === null || _d === void 0 ? void 0 : _d.manufacturerData) === null || _e === void 0 ? void 0 : _e.bytes) !== null && _f !== void 0 ? _f : [])) === null || _g === void 0 ? void 0 : _g.slice) === null || _h === void 0 ? void 0 : _h.call(_g, 2); // First 2 bytes are reserved for the Neurosity company code
46898
- return Object.assign(Object.assign({}, acc), { [peripheral.id]: Object.assign(Object.assign({}, peripheral), { name: peripheralName, manufactureDataString }) });
46899
- }, {}), distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)), map((peripheralMap) => Object.values(peripheralMap)), share());
46900
- return peripherals$;
46901
46751
  }
46902
- connect(peripheral) {
46903
- return __awaiter$e(this, void 0, void 0, function* () {
46904
- return new Promise((resolve, reject) => __awaiter$e(this, void 0, void 0, function* () {
46905
- try {
46906
- if (!peripheral) {
46907
- this.addLog("Peripheral not found");
46908
- return;
46909
- }
46910
- this.connection$.next(BLUETOOTH_CONNECTION.CONNECTING);
46911
- yield this.BleManager.connect(peripheral.id);
46912
- this.addLog(`Getting service...`);
46913
- const peripheralInfo = yield this.BleManager.retrieveServices(peripheral.id, [
46914
- BLUETOOTH_PRIMARY_SERVICE_UUID_STRING
46915
- ]);
46916
- if (!peripheralInfo) {
46917
- this.addLog("Could not retreive services");
46918
- reject(new Error(`Could not retreive services`));
46919
- return;
46920
- }
46921
- this.addLog(`Got service ${BLUETOOTH_PRIMARY_SERVICE_UUID_STRING}, getting characteristics...`);
46922
- this.device = peripheral;
46923
- this.characteristicsByName = Object.fromEntries(peripheralInfo.characteristics.map((characteristic) => [
46924
- CHARACTERISTIC_UUIDS_TO_NAMES[characteristic.characteristic.toLowerCase() // react native uses uppercase
46925
- ],
46926
- {
46927
- characteristicUUID: characteristic.characteristic,
46928
- serviceUUID: characteristic.service,
46929
- peripheralId: peripheral.id
46930
- }
46931
- ]));
46932
- this.addLog(`Got characteristics.`);
46933
- if (this.platform === "android") {
46934
- yield this.BleManager.requestMTU(peripheral.id, ANDROID_MAX_MTU)
46935
- .then((updatedMTU) => {
46936
- this.addLog(`Successfully updated Android MTU to ${updatedMTU} bytes. Requested MTU: ${ANDROID_MAX_MTU} bytes.`);
46937
- })
46938
- .catch((error) => {
46939
- this.addLog(`Failed to set Android MTU of ${ANDROID_MAX_MTU} bytes. Error: ${error}`);
46940
- });
46941
- }
46942
- this.addLog(`Successfully connected to peripheral ${peripheral.id}`);
46943
- this.connection$.next(BLUETOOTH_CONNECTION.CONNECTED);
46944
- resolve();
46945
- }
46946
- catch (error) {
46947
- reject(error);
46948
- }
46949
- }));
46752
+ isAuthenticated() {
46753
+ return __awaiter$f(this, void 0, void 0, function* () {
46754
+ const [isAuthenticated, expiresIn] = yield this.transport.readCharacteristic("auth", true);
46755
+ this.isAuthenticated$.next(isAuthenticated);
46756
+ return [isAuthenticated, expiresIn];
46950
46757
  });
46951
46758
  }
46759
+ // Method for React Native only
46760
+ scan(options) {
46761
+ if (this.transport instanceof ReactNativeTransport) {
46762
+ return this.transport.scan(options);
46763
+ }
46764
+ if (this.transport instanceof WebBluetoothTransport) {
46765
+ throw new Error(`scan method is compatibly with the React Native transport only`);
46766
+ }
46767
+ throw new Error(`unknown transport`);
46768
+ }
46769
+ // Argument for React Native only
46770
+ connect(deviceNicknameORPeripheral) {
46771
+ if (this.transport instanceof ReactNativeTransport) {
46772
+ return this.transport.connect(deviceNicknameORPeripheral);
46773
+ }
46774
+ if (this.transport instanceof WebBluetoothTransport) {
46775
+ return deviceNicknameORPeripheral
46776
+ ? this.transport.connect(deviceNicknameORPeripheral)
46777
+ : this.transport.connect();
46778
+ }
46779
+ }
46952
46780
  disconnect() {
46953
- var _a;
46954
- return __awaiter$e(this, void 0, void 0, function* () {
46955
- try {
46956
- if (this.isConnected() && ((_a = this === null || this === void 0 ? void 0 : this.device) === null || _a === void 0 ? void 0 : _a.id)) {
46957
- yield this.BleManager.disconnect(this.device.id);
46958
- }
46781
+ return this.transport.disconnect();
46782
+ }
46783
+ connection() {
46784
+ return this.transport.connection();
46785
+ }
46786
+ logs() {
46787
+ return this.transport.logs$.asObservable();
46788
+ }
46789
+ getDeviceId() {
46790
+ return __awaiter$f(this, void 0, void 0, function* () {
46791
+ // This is a public characteristic and does not require authentication
46792
+ return this.transport.readCharacteristic("deviceId");
46793
+ });
46794
+ }
46795
+ _withAuthentication(getter) {
46796
+ return __awaiter$f(this, void 0, void 0, function* () {
46797
+ // First check if the OS supports Bluetooth before checking if the device is authenticated
46798
+ const hasBluetoothSupport = yield this._hasBluetoothSupport();
46799
+ if (!hasBluetoothSupport) {
46800
+ const errorMessage = `The OS version does not support Bluetooth.`;
46801
+ this.transport.addLog(errorMessage);
46802
+ return Promise.reject(errorMessage);
46959
46803
  }
46960
- catch (error) {
46961
- return Promise.reject(error);
46804
+ const isAuthenticated = yield firstValueFrom(this.isAuthenticated$);
46805
+ if (!isAuthenticated) {
46806
+ const errorMessage = `Authentication required.`;
46807
+ this.transport.addLog(errorMessage);
46808
+ return Promise.reject(errorMessage);
46962
46809
  }
46810
+ return yield getter();
46963
46811
  });
46964
46812
  }
46965
- getCharacteristicByName(characteristicName) {
46966
- var _a;
46967
- if (!(characteristicName in this.characteristicsByName)) {
46968
- throw new Error(`Characteristic by name ${characteristicName} is not found`);
46969
- }
46970
- return (_a = this.characteristicsByName) === null || _a === void 0 ? void 0 : _a[characteristicName];
46813
+ _subscribeWhileAuthenticated(characteristicName) {
46814
+ return this.osHasBluetoothSupport$.pipe(switchMap((osHasBluetoothSupport) => osHasBluetoothSupport ? this.isAuthenticated$ : EMPTY), distinctUntilChanged(), switchMap((isAuthenticated) => isAuthenticated
46815
+ ? this.transport.subscribeToCharacteristic({
46816
+ characteristicName
46817
+ })
46818
+ : EMPTY), share());
46971
46819
  }
46972
- subscribeToCharacteristic({ characteristicName, manageNotifications = true }) {
46973
- const getData = ({ peripheralId, serviceUUID, characteristicUUID }) => defer(() => __awaiter$e(this, void 0, void 0, function* () {
46974
- var _a;
46975
- if (manageNotifications) {
46976
- try {
46977
- yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
46978
- this.addLog(`Started notifications for ${characteristicName} characteristic`);
46979
- }
46980
- catch (error) {
46981
- this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
46982
- }
46983
- }
46984
- })).pipe(switchMap(() => this.bleEvents.didUpdateValueForCharacteristic$), finalize(() => __awaiter$e(this, void 0, void 0, function* () {
46985
- var _b;
46986
- if (manageNotifications) {
46987
- try {
46988
- yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
46989
- this.addLog(`Stopped notifications for ${characteristicName} characteristic`);
46990
- }
46991
- catch (error) {
46992
- this.addLog(`Attemped to stop notifications for ${characteristicName} characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
46820
+ focus() {
46821
+ return this._focus$;
46822
+ }
46823
+ calm() {
46824
+ return this._calm$;
46825
+ }
46826
+ accelerometer() {
46827
+ return this._accelerometer$;
46828
+ }
46829
+ brainwaves(label) {
46830
+ switch (label) {
46831
+ default:
46832
+ case "raw":
46833
+ return defer(() => this.getInfo()).pipe(switchMap((deviceInfo) => this._brainwavesRaw$.pipe(csvBufferToEpoch(deviceInfo))));
46834
+ case "rawUnfiltered":
46835
+ return defer(() => this.getInfo()).pipe(switchMap((deviceInfo) => this._brainwavesRawUnfiltered$.pipe(csvBufferToEpoch(deviceInfo))));
46836
+ case "psd":
46837
+ return this._brainwavesPSD$;
46838
+ case "powerByBand":
46839
+ return this._brainwavesPowerByBand$;
46840
+ }
46841
+ }
46842
+ signalQuality() {
46843
+ return this._signalQuality$;
46844
+ }
46845
+ addMarker(label) {
46846
+ return __awaiter$f(this, void 0, void 0, function* () {
46847
+ yield this.dispatchAction({
46848
+ action: "marker",
46849
+ command: "add",
46850
+ message: {
46851
+ timestamp: Date.now(),
46852
+ label
46993
46853
  }
46994
- }
46995
- })), filter(({ characteristic }) => characteristic === characteristicUUID), map(({ value }) => decode$1(this.type, value)), stitchChunks({ delimiter: BLUETOOTH_CHUNK_DELIMITER }), map((payload) => {
46996
- var _a;
46997
- try {
46998
- return JSON.parse(payload);
46999
- }
47000
- catch (error) {
47001
- this.addLog(`Failed to parse JSON for ${characteristicName} characteristic. Falling back to unparsed string. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
47002
- return payload;
47003
- }
47004
- }));
47005
- return this.connection$.pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
47006
- ? getData(this.getCharacteristicByName(characteristicName))
47007
- : NEVER));
46854
+ });
46855
+ });
47008
46856
  }
47009
- readCharacteristic(characteristicName, parse = false) {
47010
- var _a;
47011
- return __awaiter$e(this, void 0, void 0, function* () {
47012
- this.addLog(`Reading characteristic: ${characteristicName}`);
47013
- const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName(characteristicName);
47014
- if (!characteristicUUID) {
47015
- return Promise.reject(new Error(`Did not find characteristic matching ${characteristicName}`));
47016
- }
47017
- try {
47018
- const value = yield this.BleManager.read(peripheralId, serviceUUID, characteristicUUID);
47019
- const decodedValue = decode$1(this.type, value);
47020
- const data = parse ? JSON.parse(decodedValue) : decodedValue;
47021
- this.addLog(`Received read data from ${characteristicName} characteristic: \n${data}`);
47022
- return data;
47023
- }
47024
- catch (error) {
47025
- return Promise.reject(new Error(`readCharacteristic ${characteristicName} error. ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`));
47026
- }
46857
+ getInfo() {
46858
+ return __awaiter$f(this, void 0, void 0, function* () {
46859
+ return yield this._withAuthentication(() => firstValueFrom(this.transport.subscribeToCharacteristic({
46860
+ characteristicName: "deviceInfo"
46861
+ })));
47027
46862
  });
47028
46863
  }
47029
- writeCharacteristic(characteristicName, data) {
47030
- return __awaiter$e(this, void 0, void 0, function* () {
47031
- this.addLog(`Writing characteristic: ${characteristicName}`);
47032
- const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName(characteristicName);
47033
- if (!characteristicUUID) {
47034
- return Promise.reject(new Error(`Did not find characteristic matching ${characteristicName}`));
47035
- }
47036
- const encoded = encode$1(this.type, data);
47037
- yield this.BleManager.write(peripheralId, serviceUUID, characteristicUUID, encoded, REACT_NATIVE_MAX_BYTE_SIZE);
46864
+ status() {
46865
+ return this._status$;
46866
+ }
46867
+ dispatchAction(action) {
46868
+ return __awaiter$f(this, void 0, void 0, function* () {
46869
+ return yield this._withAuthentication(() => this.transport.dispatchAction({
46870
+ characteristicName: "actions",
46871
+ action
46872
+ }));
47038
46873
  });
47039
46874
  }
47040
- _addPendingAction(actionId) {
47041
- const actions = this.pendingActions$.getValue();
47042
- this.pendingActions$.next([...actions, actionId]);
46875
+ settings() {
46876
+ return this._settings$;
46877
+ }
46878
+ haptics(effects) {
46879
+ const metric = "haptics";
46880
+ return this.dispatchAction({
46881
+ action: metric,
46882
+ command: "queue",
46883
+ responseRequired: true,
46884
+ responseTimeout: 4000,
46885
+ // @TODO: implement validation logic as per SDK
46886
+ message: { effects }
46887
+ });
46888
+ }
46889
+ get wifi() {
46890
+ return {
46891
+ nearbyNetworks: () => this._wifiNearbyNetworks$,
46892
+ connections: () => this._wifiConnections$,
46893
+ connect: (ssid, password) => {
46894
+ if (!ssid) {
46895
+ return Promise.reject(`Missing ssid`);
46896
+ }
46897
+ return this.dispatchAction({
46898
+ action: "wifi",
46899
+ command: "connect",
46900
+ responseRequired: true,
46901
+ responseTimeout: 1000 * 60 * 2,
46902
+ message: {
46903
+ ssid,
46904
+ password: password !== null && password !== void 0 ? password : null
46905
+ }
46906
+ });
46907
+ },
46908
+ forgetConnection: (ssid) => {
46909
+ if (!ssid) {
46910
+ return Promise.reject(`Missing ssid`);
46911
+ }
46912
+ return this.dispatchAction({
46913
+ action: "wifi",
46914
+ command: "forget-connection",
46915
+ responseRequired: true,
46916
+ responseTimeout: 1000 * 15,
46917
+ message: {
46918
+ ssid
46919
+ }
46920
+ });
46921
+ },
46922
+ reset: () => {
46923
+ return this.dispatchAction({
46924
+ action: "wifi",
46925
+ command: "reset",
46926
+ responseRequired: true,
46927
+ responseTimeout: 1000 * 30,
46928
+ message: {
46929
+ // without this, the action will resolve as soon as the
46930
+ // action is received by the OS
46931
+ respondOnSuccess: true
46932
+ }
46933
+ });
46934
+ },
46935
+ speedTest: () => {
46936
+ return this.dispatchAction({
46937
+ action: "wifi",
46938
+ command: "speed-test",
46939
+ responseRequired: true,
46940
+ responseTimeout: 1000 * 60 * 1 // 1 minute
46941
+ });
46942
+ }
46943
+ };
46944
+ }
46945
+ }
46946
+
46947
+ const debug = (
46948
+ typeof process === 'object' &&
46949
+ process.env &&
46950
+ process.env.NODE_DEBUG &&
46951
+ /\bsemver\b/i.test(process.env.NODE_DEBUG)
46952
+ ) ? (...args) => console.error('SEMVER', ...args)
46953
+ : () => {};
46954
+
46955
+ var debug_1 = debug;
46956
+
46957
+ // Note: this is the semver.org version of the spec that it implements
46958
+ // Not necessarily the package version of this code.
46959
+ const SEMVER_SPEC_VERSION = '2.0.0';
46960
+
46961
+ const MAX_LENGTH = 256;
46962
+ const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
46963
+ /* istanbul ignore next */ 9007199254740991;
46964
+
46965
+ // Max safe segment length for coercion.
46966
+ const MAX_SAFE_COMPONENT_LENGTH = 16;
46967
+
46968
+ var constants = {
46969
+ SEMVER_SPEC_VERSION,
46970
+ MAX_LENGTH,
46971
+ MAX_SAFE_INTEGER,
46972
+ MAX_SAFE_COMPONENT_LENGTH,
46973
+ };
46974
+
46975
+ function createCommonjsModule(fn, module) {
46976
+ return module = { exports: {} }, fn(module, module.exports), module.exports;
46977
+ }
46978
+
46979
+ var re_1 = createCommonjsModule(function (module, exports) {
46980
+ const { MAX_SAFE_COMPONENT_LENGTH } = constants;
46981
+
46982
+ exports = module.exports = {};
46983
+
46984
+ // The actual regexps go on exports.re
46985
+ const re = exports.re = [];
46986
+ const src = exports.src = [];
46987
+ const t = exports.t = {};
46988
+ let R = 0;
46989
+
46990
+ const createToken = (name, value, isGlobal) => {
46991
+ const index = R++;
46992
+ debug_1(name, index, value);
46993
+ t[name] = index;
46994
+ src[index] = value;
46995
+ re[index] = new RegExp(value, isGlobal ? 'g' : undefined);
46996
+ };
46997
+
46998
+ // The following Regular Expressions can be used for tokenizing,
46999
+ // validating, and parsing SemVer version strings.
47000
+
47001
+ // ## Numeric Identifier
47002
+ // A single `0`, or a non-zero digit followed by zero or more digits.
47003
+
47004
+ createToken('NUMERICIDENTIFIER', '0|[1-9]\\d*');
47005
+ createToken('NUMERICIDENTIFIERLOOSE', '[0-9]+');
47006
+
47007
+ // ## Non-numeric Identifier
47008
+ // Zero or more digits, followed by a letter or hyphen, and then zero or
47009
+ // more letters, digits, or hyphens.
47010
+
47011
+ createToken('NONNUMERICIDENTIFIER', '\\d*[a-zA-Z-][a-zA-Z0-9-]*');
47012
+
47013
+ // ## Main Version
47014
+ // Three dot-separated numeric identifiers.
47015
+
47016
+ createToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\.` +
47017
+ `(${src[t.NUMERICIDENTIFIER]})\\.` +
47018
+ `(${src[t.NUMERICIDENTIFIER]})`);
47019
+
47020
+ createToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
47021
+ `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.` +
47022
+ `(${src[t.NUMERICIDENTIFIERLOOSE]})`);
47023
+
47024
+ // ## Pre-release Version Identifier
47025
+ // A numeric identifier, or a non-numeric identifier.
47026
+
47027
+ createToken('PRERELEASEIDENTIFIER', `(?:${src[t.NUMERICIDENTIFIER]
47028
+ }|${src[t.NONNUMERICIDENTIFIER]})`);
47029
+
47030
+ createToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NUMERICIDENTIFIERLOOSE]
47031
+ }|${src[t.NONNUMERICIDENTIFIER]})`);
47032
+
47033
+ // ## Pre-release Version
47034
+ // Hyphen, followed by one or more dot-separated pre-release version
47035
+ // identifiers.
47036
+
47037
+ createToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]
47038
+ }(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`);
47039
+
47040
+ createToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]
47041
+ }(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`);
47042
+
47043
+ // ## Build Metadata Identifier
47044
+ // Any combination of digits, letters, or hyphens.
47045
+
47046
+ createToken('BUILDIDENTIFIER', '[0-9A-Za-z-]+');
47047
+
47048
+ // ## Build Metadata
47049
+ // Plus sign, followed by one or more period-separated build metadata
47050
+ // identifiers.
47051
+
47052
+ createToken('BUILD', `(?:\\+(${src[t.BUILDIDENTIFIER]
47053
+ }(?:\\.${src[t.BUILDIDENTIFIER]})*))`);
47054
+
47055
+ // ## Full Version String
47056
+ // A main version, followed optionally by a pre-release version and
47057
+ // build metadata.
47058
+
47059
+ // Note that the only major, minor, patch, and pre-release sections of
47060
+ // the version string are capturing groups. The build metadata is not a
47061
+ // capturing group, because it should not ever be used in version
47062
+ // comparison.
47063
+
47064
+ createToken('FULLPLAIN', `v?${src[t.MAINVERSION]
47065
+ }${src[t.PRERELEASE]}?${
47066
+ src[t.BUILD]}?`);
47067
+
47068
+ createToken('FULL', `^${src[t.FULLPLAIN]}$`);
47069
+
47070
+ // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
47071
+ // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
47072
+ // common in the npm registry.
47073
+ createToken('LOOSEPLAIN', `[v=\\s]*${src[t.MAINVERSIONLOOSE]
47074
+ }${src[t.PRERELEASELOOSE]}?${
47075
+ src[t.BUILD]}?`);
47076
+
47077
+ createToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`);
47078
+
47079
+ createToken('GTLT', '((?:<|>)?=?)');
47080
+
47081
+ // Something like "2.*" or "1.2.x".
47082
+ // Note that "x.x" is a valid xRange identifer, meaning "any version"
47083
+ // Only the first item is strictly required.
47084
+ createToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`);
47085
+ createToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`);
47086
+
47087
+ createToken('XRANGEPLAIN', `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})` +
47088
+ `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
47089
+ `(?:\\.(${src[t.XRANGEIDENTIFIER]})` +
47090
+ `(?:${src[t.PRERELEASE]})?${
47091
+ src[t.BUILD]}?` +
47092
+ `)?)?`);
47093
+
47094
+ createToken('XRANGEPLAINLOOSE', `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +
47095
+ `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
47096
+ `(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +
47097
+ `(?:${src[t.PRERELEASELOOSE]})?${
47098
+ src[t.BUILD]}?` +
47099
+ `)?)?`);
47100
+
47101
+ createToken('XRANGE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`);
47102
+ createToken('XRANGELOOSE', `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`);
47103
+
47104
+ // Coercion.
47105
+ // Extract anything that could conceivably be a part of a valid semver
47106
+ createToken('COERCE', `${'(^|[^\\d])' +
47107
+ '(\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +
47108
+ `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
47109
+ `(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +
47110
+ `(?:$|[^\\d])`);
47111
+ createToken('COERCERTL', src[t.COERCE], true);
47112
+
47113
+ // Tilde ranges.
47114
+ // Meaning is "reasonably at or greater than"
47115
+ createToken('LONETILDE', '(?:~>?)');
47116
+
47117
+ createToken('TILDETRIM', `(\\s*)${src[t.LONETILDE]}\\s+`, true);
47118
+ exports.tildeTrimReplace = '$1~';
47119
+
47120
+ createToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`);
47121
+ createToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`);
47122
+
47123
+ // Caret ranges.
47124
+ // Meaning is "at least and backwards compatible with"
47125
+ createToken('LONECARET', '(?:\\^)');
47126
+
47127
+ createToken('CARETTRIM', `(\\s*)${src[t.LONECARET]}\\s+`, true);
47128
+ exports.caretTrimReplace = '$1^';
47129
+
47130
+ createToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`);
47131
+ createToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`);
47132
+
47133
+ // A simple gt/lt/eq thing, or just "" to indicate "any version"
47134
+ createToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`);
47135
+ createToken('COMPARATOR', `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`);
47136
+
47137
+ // An expression to strip any whitespace between the gtlt and the thing
47138
+ // it modifies, so that `> 1.2.3` ==> `>1.2.3`
47139
+ createToken('COMPARATORTRIM', `(\\s*)${src[t.GTLT]
47140
+ }\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true);
47141
+ exports.comparatorTrimReplace = '$1$2$3';
47142
+
47143
+ // Something like `1.2.3 - 1.2.4`
47144
+ // Note that these all use the loose form, because they'll be
47145
+ // checked against either the strict or loose comparator form
47146
+ // later.
47147
+ createToken('HYPHENRANGE', `^\\s*(${src[t.XRANGEPLAIN]})` +
47148
+ `\\s+-\\s+` +
47149
+ `(${src[t.XRANGEPLAIN]})` +
47150
+ `\\s*$`);
47151
+
47152
+ createToken('HYPHENRANGELOOSE', `^\\s*(${src[t.XRANGEPLAINLOOSE]})` +
47153
+ `\\s+-\\s+` +
47154
+ `(${src[t.XRANGEPLAINLOOSE]})` +
47155
+ `\\s*$`);
47156
+
47157
+ // Star ranges basically just allow anything at all.
47158
+ createToken('STAR', '(<|>)?=?\\s*\\*');
47159
+ // >=0.0.0 is like a star
47160
+ createToken('GTE0', '^\\s*>=\\s*0\\.0\\.0\\s*$');
47161
+ createToken('GTE0PRE', '^\\s*>=\\s*0\\.0\\.0-0\\s*$');
47162
+ });
47163
+ var re_2 = re_1.re;
47164
+ var re_3 = re_1.src;
47165
+ var re_4 = re_1.t;
47166
+ var re_5 = re_1.tildeTrimReplace;
47167
+ var re_6 = re_1.caretTrimReplace;
47168
+ var re_7 = re_1.comparatorTrimReplace;
47169
+
47170
+ // parse out just the options we care about so we always get a consistent
47171
+ // obj with keys in a consistent order.
47172
+ const opts = ['includePrerelease', 'loose', 'rtl'];
47173
+ const parseOptions = options =>
47174
+ !options ? {}
47175
+ : typeof options !== 'object' ? { loose: true }
47176
+ : opts.filter(k => options[k]).reduce((o, k) => {
47177
+ o[k] = true;
47178
+ return o
47179
+ }, {});
47180
+ var parseOptions_1 = parseOptions;
47181
+
47182
+ const numeric = /^[0-9]+$/;
47183
+ const compareIdentifiers = (a, b) => {
47184
+ const anum = numeric.test(a);
47185
+ const bnum = numeric.test(b);
47186
+
47187
+ if (anum && bnum) {
47188
+ a = +a;
47189
+ b = +b;
47190
+ }
47191
+
47192
+ return a === b ? 0
47193
+ : (anum && !bnum) ? -1
47194
+ : (bnum && !anum) ? 1
47195
+ : a < b ? -1
47196
+ : 1
47197
+ };
47198
+
47199
+ const rcompareIdentifiers = (a, b) => compareIdentifiers(b, a);
47200
+
47201
+ var identifiers = {
47202
+ compareIdentifiers,
47203
+ rcompareIdentifiers,
47204
+ };
47205
+
47206
+ const { MAX_LENGTH: MAX_LENGTH$1, MAX_SAFE_INTEGER: MAX_SAFE_INTEGER$1 } = constants;
47207
+ const { re: re$1, t: t$1 } = re_1;
47208
+
47209
+
47210
+ const { compareIdentifiers: compareIdentifiers$1 } = identifiers;
47211
+ class SemVer {
47212
+ constructor (version, options) {
47213
+ options = parseOptions_1(options);
47214
+
47215
+ if (version instanceof SemVer) {
47216
+ if (version.loose === !!options.loose &&
47217
+ version.includePrerelease === !!options.includePrerelease) {
47218
+ return version
47219
+ } else {
47220
+ version = version.version;
47221
+ }
47222
+ } else if (typeof version !== 'string') {
47223
+ throw new TypeError(`Invalid Version: ${version}`)
47043
47224
  }
47044
- _removePendingAction(actionId) {
47045
- const actions = this.pendingActions$.getValue();
47046
- this.pendingActions$.next(actions.filter((id) => id !== actionId));
47225
+
47226
+ if (version.length > MAX_LENGTH$1) {
47227
+ throw new TypeError(
47228
+ `version is longer than ${MAX_LENGTH$1} characters`
47229
+ )
47047
47230
  }
47048
- _autoToggleActionNotifications(selectedDevice$) {
47049
- return __awaiter$e(this, void 0, void 0, function* () {
47050
- let started = false;
47051
- const sideEffects$ = this.connection$.asObservable().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
47052
- ? this.pendingActions$
47053
- : NEVER), tap((pendingActions) => __awaiter$e(this, void 0, void 0, function* () {
47054
- var _a, _b;
47055
- const { peripheralId, serviceUUID, characteristicUUID } = this.getCharacteristicByName("actions");
47056
- const hasPendingActions = !!pendingActions.length;
47057
- if (hasPendingActions && !started) {
47058
- started = true;
47059
- try {
47060
- yield this.BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID);
47061
- this.addLog(`Started notifications for [actions] characteristic`);
47062
- }
47063
- catch (error) {
47064
- this.addLog(`Attemped to start notifications for [actions] characteristic: ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
47065
- }
47066
- }
47067
- if (!hasPendingActions && started) {
47068
- started = false;
47069
- try {
47070
- yield this.BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID);
47071
- this.addLog(`Stopped notifications for actions characteristic`);
47072
- }
47073
- catch (error) {
47074
- this.addLog(`Attemped to stop notifications for [actions] characteristic: ${(_b = error === null || error === void 0 ? void 0 : error.message) !== null && _b !== void 0 ? _b : error}`);
47075
- }
47076
- }
47077
- })));
47078
- selectedDevice$
47079
- .pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice) ? EMPTY : sideEffects$))
47080
- .subscribe();
47081
- });
47231
+
47232
+ debug_1('SemVer', version, options);
47233
+ this.options = options;
47234
+ this.loose = !!options.loose;
47235
+ // this isn't actually relevant for versions, but keep it so that we
47236
+ // don't run into trouble passing this.options around.
47237
+ this.includePrerelease = !!options.includePrerelease;
47238
+
47239
+ const m = version.trim().match(options.loose ? re$1[t$1.LOOSE] : re$1[t$1.FULL]);
47240
+
47241
+ if (!m) {
47242
+ throw new TypeError(`Invalid Version: ${version}`)
47082
47243
  }
47083
- dispatchAction({ characteristicName, action }) {
47084
- return __awaiter$e(this, void 0, void 0, function* () {
47085
- const { responseRequired = false, responseTimeout = DEFAULT_ACTION_RESPONSE_TIMEOUT } = action;
47086
- return new Promise((resolve, reject) => __awaiter$e(this, void 0, void 0, function* () {
47087
- const actionId = create6DigitPin(); // use to later identify and filter response
47088
- const payload = JSON.stringify(Object.assign({ actionId }, action)); // add the response id to the action
47089
- this.addLog(`Dispatched action with id ${actionId}`);
47090
- if (responseRequired && responseTimeout) {
47091
- this._addPendingAction(actionId);
47092
- const timeout$$1 = timer(responseTimeout).subscribe(() => {
47093
- this._removePendingAction(actionId);
47094
- reject(new Error(`Action with id ${actionId} timed out after ${responseTimeout}ms`));
47095
- });
47096
- // listen for a response before writing
47097
- this.subscribeToCharacteristic({
47098
- characteristicName,
47099
- manageNotifications: false
47100
- })
47101
- .pipe(filter((response) => (response === null || response === void 0 ? void 0 : response.actionId) === actionId), take(1))
47102
- .subscribe((response) => {
47103
- timeout$$1.unsubscribe();
47104
- this._removePendingAction(actionId);
47105
- resolve(response);
47106
- });
47107
- // register action by writing
47108
- this.writeCharacteristic(characteristicName, payload).catch((error) => {
47109
- this._removePendingAction(actionId);
47110
- reject(error);
47111
- });
47112
- }
47113
- else {
47114
- this.writeCharacteristic(characteristicName, payload)
47115
- .then(() => {
47116
- resolve(null);
47117
- })
47118
- .catch((error) => {
47119
- reject(error);
47120
- });
47121
- }
47122
- }));
47123
- });
47244
+
47245
+ this.raw = version;
47246
+
47247
+ // these are actually numbers
47248
+ this.major = +m[1];
47249
+ this.minor = +m[2];
47250
+ this.patch = +m[3];
47251
+
47252
+ if (this.major > MAX_SAFE_INTEGER$1 || this.major < 0) {
47253
+ throw new TypeError('Invalid major version')
47124
47254
  }
47125
- }
47126
47255
 
47127
- const defaultDataProp = "data";
47128
- const defaultSamplingRate = 256;
47129
- const isObject$2 = (object) => object instanceof Object && object === Object(object);
47130
- const isFunction$2 = (object) => typeof object === "function";
47131
- const patch = (sample$$1) => (info) => {
47132
- var _a;
47133
- return (Object.assign(Object.assign({}, sample$$1), { info: Object.assign(Object.assign({}, ((_a = sample$$1 === null || sample$$1 === void 0 ? void 0 : sample$$1.info) !== null && _a !== void 0 ? _a : {})), (info || {})) }));
47134
- };
47135
- /**
47136
- * Annotates stream with user-defined metadata
47137
- * @method addInfo
47138
- * @example eeg$.pipe(addinfo({ samplingRate: 256, channelNames: ["Af7", "Fp1", "Fp2", "Af8"] })
47139
- * @param {Object} info Info to be added to the EEG stream. Relevant info may include: `samplingRate` and `channelNames`
47140
- * @returns {Observable<Sample|Epoch|PSD>}
47141
- */
47142
- const addInfo = (infoValue) => pipe(map((sample$$1) => {
47143
- if (!isObject$2(sample$$1) ||
47144
- (!isObject$2(infoValue) && !isFunction$2(infoValue))) {
47145
- return sample$$1;
47256
+ if (this.minor > MAX_SAFE_INTEGER$1 || this.minor < 0) {
47257
+ throw new TypeError('Invalid minor version')
47146
47258
  }
47147
- const info = isFunction$2(infoValue) ? infoValue(sample$$1) : infoValue;
47148
- return patch(sample$$1)(info);
47149
- }));
47150
- /**
47151
- * Get a 2D data array organized by channel from an array of Samples. Credit to Ken from Seattle's elegant transposition
47152
- * http://www.codesuck.com/2012/02/transpose-javascript-array-in-one-line.html
47153
- * @method groupByChannel
47154
- * @param {Array<Sample>} samplesBuffer Array of Samples to be grouped
47155
- * @param {string} [dataProp] Name of the key associated with EEG data
47156
- * @returns {Array<Array<number>>}
47157
- */
47158
- const groupByChannel = (samplesBuffer, dataProp = defaultDataProp) => samplesBuffer[0][dataProp].map((_, channelIndex) => samplesBuffer.map((sample$$1) => sample$$1[dataProp][channelIndex]));
47159
- /**
47160
- * Takes an array or RxJS buffer of EEG Samples and returns an Epoch.
47161
- * @method bufferToEpoch
47162
- * @example eeg$.pipe(bufferTime(1000), bufferToEpoch({ samplingRate: 256 }))
47163
- *
47164
- * @param {Object} options - Data structure options
47165
- * @param {number} [options.samplingRate] Sampling rate
47166
- * @param {string} [options.dataProp='data'] Name of the key associated with eeg data
47167
- *
47168
- * @returns {Observable<Epoch>}
47169
- */
47170
- const bufferToEpoch = ({ samplingRate = defaultSamplingRate, dataProp = defaultDataProp } = {}) => pipe(map((samplesArray) => ({
47171
- [dataProp]: groupByChannel(samplesArray, dataProp),
47172
- info: Object.assign(Object.assign({}, (samplesArray[0] && samplesArray[0].info
47173
- ? samplesArray[0].info
47174
- : {})), { startTime: samplesArray[0].timestamp, samplingRate: samplesArray[0].info && samplesArray[0].info.samplingRate
47175
- ? samplesArray[0].info.samplingRate
47176
- : samplingRate })
47177
- })));
47178
- /**
47179
- * Converts a stream of individual Samples of EEG data into a stream of Epochs of a given duration emitted at specified interval. This operator functions similarly to a circular buffer internally and allows overlapping Epochs of data to be emitted (e.g. emitting the last one second of data every 100ms).
47180
- * @method epoch
47181
- * @example eeg$.pipe(epoch({ duration: 1024, interval: 100, samplingRate: 256 }))
47182
- * @param {Object} options - Epoching options
47183
- * @param {number} [options.duration=256] Number of samples to include in each epoch
47184
- * @param {number} [options.interval=100] Time (ms) between emitted Epochs
47185
- * @param {number} [options.samplingRate=256] Sampling rate
47186
- * @param {string} [options.dataProp='data'] Name of the key associated with eeg data
47187
- * @returns {Observable} Epoch
47188
- */
47189
- const epoch = ({ duration, interval: interval$$1, samplingRate, dataProp = defaultDataProp }) => pipe(bufferCount(interval$$1), scan((acc, val) => acc.concat(val).slice(acc.length < duration ? 0 : -duration)), filter((samplesArray) => samplesArray.length === duration), bufferToEpoch({ samplingRate, dataProp }));
47190
47259
 
47191
- const EPOCH_BUFFER_SIZE = 16;
47192
- const SAMPLING_RATE_FALLBACK = 256; // Crown's sampling rate
47193
- /**
47194
- * @hidden
47195
- */
47196
- function csvBufferToEpoch(deviceInfo) {
47197
- var _a;
47198
- if (!(deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate)) {
47199
- console.warn(`Didn't receive a sampling rate, defaulting to ${SAMPLING_RATE_FALLBACK}`);
47260
+ if (this.patch > MAX_SAFE_INTEGER$1 || this.patch < 0) {
47261
+ throw new TypeError('Invalid patch version')
47200
47262
  }
47201
- return pipe(csvBufferToSamples(), epoch({
47202
- duration: EPOCH_BUFFER_SIZE,
47203
- interval: EPOCH_BUFFER_SIZE,
47204
- samplingRate: (_a = deviceInfo === null || deviceInfo === void 0 ? void 0 : deviceInfo.samplingRate) !== null && _a !== void 0 ? _a : SAMPLING_RATE_FALLBACK
47205
- }), addInfo({
47206
- channelNames: deviceInfo.channelNames,
47207
- samplingRate: deviceInfo.samplingRate
47208
- }));
47209
- }
47210
- /**
47211
- * @hidden
47212
- */
47213
- function csvBufferToSamples() {
47214
- return pipe(mergeMap((samples) => from(samples)), map(([timestamp$$1, marker, ...data]) => ({
47215
- timestamp: timestamp$$1,
47216
- data
47217
- })));
47218
- }
47219
47263
 
47220
- var __awaiter$f = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
47221
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
47222
- return new (P || (P = Promise))(function (resolve, reject) {
47223
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
47224
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
47225
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
47226
- step((generator = generator.apply(thisArg, _arguments || [])).next());
47227
- });
47228
- };
47229
- class BluetoothClient {
47230
- constructor(options) {
47231
- this.selectedDevice$ = new ReplaySubject(1);
47232
- this.isAuthenticated$ = new ReplaySubject(1);
47233
- const { transport, selectedDevice$, createBluetoothToken } = options !== null && options !== void 0 ? options : {};
47234
- if (!transport) {
47235
- throw new Error(`No bluetooth transport provided.`);
47236
- }
47237
- this.transport = transport;
47238
- // Pass events to the internal selectedDevice$ if selectedDevice$ is passed via options
47239
- if (selectedDevice$) {
47240
- selectedDevice$.subscribe(this.selectedDevice$);
47241
- }
47242
- // Auto Connect
47243
- this.transport._autoConnect(this.selectedDevice$).subscribe({
47244
- error: (error) => {
47245
- var _a;
47246
- this.transport.addLog(`Auto connect: error -> ${(_a = error === null || error === void 0 ? void 0 : error.message) !== null && _a !== void 0 ? _a : error}`);
47247
- }
47248
- });
47249
- // Auto authentication
47250
- if (typeof createBluetoothToken === "function") {
47251
- this.transport.addLog("Auto authentication enabled");
47252
- this._autoAuthenticate(createBluetoothToken).subscribe();
47253
- }
47254
- else {
47255
- this.transport.addLog("Auto authentication not enabled");
47264
+ // numberify any prerelease numeric ids
47265
+ if (!m[4]) {
47266
+ this.prerelease = [];
47267
+ } else {
47268
+ this.prerelease = m[4].split('.').map((id) => {
47269
+ if (/^[0-9]+$/.test(id)) {
47270
+ const num = +id;
47271
+ if (num >= 0 && num < MAX_SAFE_INTEGER$1) {
47272
+ return num
47273
+ }
47256
47274
  }
47257
- // Auto manage action notifications
47258
- this.transport._autoToggleActionNotifications(this.selectedDevice$);
47259
- // Multicast metrics (share)
47260
- this._focus$ = this._subscribeWhileAuthenticated("focus");
47261
- this._calm$ = this._subscribeWhileAuthenticated("calm");
47262
- this._accelerometer$ = this._subscribeWhileAuthenticated("accelerometer");
47263
- this._brainwavesRaw$ = this._subscribeWhileAuthenticated("raw");
47264
- this._brainwavesRawUnfiltered$ =
47265
- this._subscribeWhileAuthenticated("rawUnfiltered");
47266
- this._brainwavesPSD$ = this._subscribeWhileAuthenticated("psd");
47267
- this._brainwavesPowerByBand$ =
47268
- this._subscribeWhileAuthenticated("powerByBand");
47269
- this._signalQuality$ = this._subscribeWhileAuthenticated("signalQuality");
47270
- this._status$ = this._subscribeWhileAuthenticated("status");
47271
- this._settings$ = this._subscribeWhileAuthenticated("settings");
47272
- this._wifiNearbyNetworks$ =
47273
- this._subscribeWhileAuthenticated("wifiNearbyNetworks");
47274
- this._wifiConnections$ =
47275
- this._subscribeWhileAuthenticated("wifiConnections");
47275
+ return id
47276
+ });
47276
47277
  }
47277
- _autoAuthenticate(createBluetoothToken) {
47278
- const REAUTHENTICATE_INTERVAL = 3600000; // 1 hour
47279
- const reauthenticateInterval$ = timer(0, REAUTHENTICATE_INTERVAL).pipe(tap(() => {
47280
- this.transport.addLog(`Auto authentication in progress...`);
47281
- }));
47282
- return this.selectedDevice$.pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
47283
- ? EMPTY
47284
- : this.connection().pipe(switchMap((connection) => connection === BLUETOOTH_CONNECTION.CONNECTED
47285
- ? reauthenticateInterval$
47286
- : EMPTY), switchMap(() => __awaiter$f(this, void 0, void 0, function* () { return yield this.isAuthenticated(); })), tap(([isAuthenticated]) => __awaiter$f(this, void 0, void 0, function* () {
47287
- if (!isAuthenticated) {
47288
- const token = yield createBluetoothToken();
47289
- yield this.authenticate(token);
47290
- }
47291
- else {
47292
- this.transport.addLog(`Already authenticated`);
47293
- }
47294
- })))));
47278
+
47279
+ this.build = m[5] ? m[5].split('.') : [];
47280
+ this.format();
47281
+ }
47282
+
47283
+ format () {
47284
+ this.version = `${this.major}.${this.minor}.${this.patch}`;
47285
+ if (this.prerelease.length) {
47286
+ this.version += `-${this.prerelease.join('.')}`;
47295
47287
  }
47296
- enableAutoConnect(autoConnect) {
47297
- this.transport.enableAutoConnect(autoConnect);
47288
+ return this.version
47289
+ }
47290
+
47291
+ toString () {
47292
+ return this.version
47293
+ }
47294
+
47295
+ compare (other) {
47296
+ debug_1('SemVer.compare', this.version, this.options, other);
47297
+ if (!(other instanceof SemVer)) {
47298
+ if (typeof other === 'string' && other === this.version) {
47299
+ return 0
47300
+ }
47301
+ other = new SemVer(other, this.options);
47298
47302
  }
47299
- _hasBluetoothSupport() {
47300
- return __awaiter$f(this, void 0, void 0, function* () {
47301
- const selectedDevice = yield firstValueFrom(this.selectedDevice$);
47302
- return osHasBluetoothSupport(selectedDevice);
47303
- });
47303
+
47304
+ if (other.version === this.version) {
47305
+ return 0
47304
47306
  }
47305
- authenticate(token) {
47306
- return __awaiter$f(this, void 0, void 0, function* () {
47307
- const hasBluetoothSupport = yield this._hasBluetoothSupport();
47308
- if (!hasBluetoothSupport) {
47309
- const errorMessage = `authenticate method: The OS version does not support Bluetooth.`;
47310
- this.transport.addLog(errorMessage);
47311
- return Promise.reject(errorMessage);
47312
- }
47313
- yield this.transport.writeCharacteristic("auth", token);
47314
- const isAuthenticatedResponse = yield this.isAuthenticated();
47315
- const [isAuthenticated] = isAuthenticatedResponse;
47316
- this.transport.addLog(`Authentication ${isAuthenticated ? "succeeded" : "failed"}`);
47317
- this.isAuthenticated$.next(isAuthenticated);
47318
- return isAuthenticatedResponse;
47319
- });
47307
+
47308
+ return this.compareMain(other) || this.comparePre(other)
47309
+ }
47310
+
47311
+ compareMain (other) {
47312
+ if (!(other instanceof SemVer)) {
47313
+ other = new SemVer(other, this.options);
47320
47314
  }
47321
- isAuthenticated() {
47322
- return __awaiter$f(this, void 0, void 0, function* () {
47323
- const [isAuthenticated, expiresIn] = yield this.transport.readCharacteristic("auth", true);
47324
- this.isAuthenticated$.next(isAuthenticated);
47325
- return [isAuthenticated, expiresIn];
47326
- });
47315
+
47316
+ return (
47317
+ compareIdentifiers$1(this.major, other.major) ||
47318
+ compareIdentifiers$1(this.minor, other.minor) ||
47319
+ compareIdentifiers$1(this.patch, other.patch)
47320
+ )
47321
+ }
47322
+
47323
+ comparePre (other) {
47324
+ if (!(other instanceof SemVer)) {
47325
+ other = new SemVer(other, this.options);
47327
47326
  }
47328
- // Method for React Native only
47329
- scan(options) {
47330
- if (this.transport instanceof ReactNativeTransport) {
47331
- return this.transport.scan(options);
47327
+
47328
+ // NOT having a prerelease is > having one
47329
+ if (this.prerelease.length && !other.prerelease.length) {
47330
+ return -1
47331
+ } else if (!this.prerelease.length && other.prerelease.length) {
47332
+ return 1
47333
+ } else if (!this.prerelease.length && !other.prerelease.length) {
47334
+ return 0
47335
+ }
47336
+
47337
+ let i = 0;
47338
+ do {
47339
+ const a = this.prerelease[i];
47340
+ const b = other.prerelease[i];
47341
+ debug_1('prerelease compare', i, a, b);
47342
+ if (a === undefined && b === undefined) {
47343
+ return 0
47344
+ } else if (b === undefined) {
47345
+ return 1
47346
+ } else if (a === undefined) {
47347
+ return -1
47348
+ } else if (a === b) {
47349
+ continue
47350
+ } else {
47351
+ return compareIdentifiers$1(a, b)
47352
+ }
47353
+ } while (++i)
47354
+ }
47355
+
47356
+ compareBuild (other) {
47357
+ if (!(other instanceof SemVer)) {
47358
+ other = new SemVer(other, this.options);
47359
+ }
47360
+
47361
+ let i = 0;
47362
+ do {
47363
+ const a = this.build[i];
47364
+ const b = other.build[i];
47365
+ debug_1('prerelease compare', i, a, b);
47366
+ if (a === undefined && b === undefined) {
47367
+ return 0
47368
+ } else if (b === undefined) {
47369
+ return 1
47370
+ } else if (a === undefined) {
47371
+ return -1
47372
+ } else if (a === b) {
47373
+ continue
47374
+ } else {
47375
+ return compareIdentifiers$1(a, b)
47376
+ }
47377
+ } while (++i)
47378
+ }
47379
+
47380
+ // preminor will bump the version up to the next minor release, and immediately
47381
+ // down to pre-release. premajor and prepatch work the same way.
47382
+ inc (release, identifier) {
47383
+ switch (release) {
47384
+ case 'premajor':
47385
+ this.prerelease.length = 0;
47386
+ this.patch = 0;
47387
+ this.minor = 0;
47388
+ this.major++;
47389
+ this.inc('pre', identifier);
47390
+ break
47391
+ case 'preminor':
47392
+ this.prerelease.length = 0;
47393
+ this.patch = 0;
47394
+ this.minor++;
47395
+ this.inc('pre', identifier);
47396
+ break
47397
+ case 'prepatch':
47398
+ // If this is already a prerelease, it will bump to the next version
47399
+ // drop any prereleases that might already exist, since they are not
47400
+ // relevant at this point.
47401
+ this.prerelease.length = 0;
47402
+ this.inc('patch', identifier);
47403
+ this.inc('pre', identifier);
47404
+ break
47405
+ // If the input is a non-prerelease version, this acts the same as
47406
+ // prepatch.
47407
+ case 'prerelease':
47408
+ if (this.prerelease.length === 0) {
47409
+ this.inc('patch', identifier);
47332
47410
  }
47333
- if (this.transport instanceof WebBluetoothTransport) {
47334
- throw new Error(`scan method is compatibly with the React Native transport only`);
47411
+ this.inc('pre', identifier);
47412
+ break
47413
+
47414
+ case 'major':
47415
+ // If this is a pre-major version, bump up to the same major version.
47416
+ // Otherwise increment major.
47417
+ // 1.0.0-5 bumps to 1.0.0
47418
+ // 1.1.0 bumps to 2.0.0
47419
+ if (
47420
+ this.minor !== 0 ||
47421
+ this.patch !== 0 ||
47422
+ this.prerelease.length === 0
47423
+ ) {
47424
+ this.major++;
47335
47425
  }
47336
- throw new Error(`unknown transport`);
47337
- }
47338
- // Argument for React Native only
47339
- connect(deviceNicknameORPeripheral) {
47340
- if (this.transport instanceof ReactNativeTransport) {
47341
- return this.transport.connect(deviceNicknameORPeripheral);
47426
+ this.minor = 0;
47427
+ this.patch = 0;
47428
+ this.prerelease = [];
47429
+ break
47430
+ case 'minor':
47431
+ // If this is a pre-minor version, bump up to the same minor version.
47432
+ // Otherwise increment minor.
47433
+ // 1.2.0-5 bumps to 1.2.0
47434
+ // 1.2.1 bumps to 1.3.0
47435
+ if (this.patch !== 0 || this.prerelease.length === 0) {
47436
+ this.minor++;
47342
47437
  }
47343
- if (this.transport instanceof WebBluetoothTransport) {
47344
- return deviceNicknameORPeripheral
47345
- ? this.transport.connect(deviceNicknameORPeripheral)
47346
- : this.transport.connect();
47438
+ this.patch = 0;
47439
+ this.prerelease = [];
47440
+ break
47441
+ case 'patch':
47442
+ // If this is not a pre-release version, it will increment the patch.
47443
+ // If it is a pre-release it will bump up to the same patch version.
47444
+ // 1.2.0-5 patches to 1.2.0
47445
+ // 1.2.0 patches to 1.2.1
47446
+ if (this.prerelease.length === 0) {
47447
+ this.patch++;
47347
47448
  }
47348
- }
47349
- disconnect() {
47350
- return this.transport.disconnect();
47351
- }
47352
- connection() {
47353
- return this.transport.connection();
47354
- }
47355
- logs() {
47356
- return this.transport.logs$.asObservable();
47357
- }
47358
- getDeviceId() {
47359
- return __awaiter$f(this, void 0, void 0, function* () {
47360
- // This is a public characteristic and does not require authentication
47361
- return this.transport.readCharacteristic("deviceId");
47362
- });
47363
- }
47364
- _withAuthentication(getter) {
47365
- return __awaiter$f(this, void 0, void 0, function* () {
47366
- // First check if the OS supports Bluetooth before checking if the device is authenticated
47367
- const hasBluetoothSupport = yield this._hasBluetoothSupport();
47368
- if (!hasBluetoothSupport) {
47369
- const errorMessage = `The OS version does not support Bluetooth.`;
47370
- this.transport.addLog(errorMessage);
47371
- return Promise.reject(errorMessage);
47449
+ this.prerelease = [];
47450
+ break
47451
+ // This probably shouldn't be used publicly.
47452
+ // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.
47453
+ case 'pre':
47454
+ if (this.prerelease.length === 0) {
47455
+ this.prerelease = [0];
47456
+ } else {
47457
+ let i = this.prerelease.length;
47458
+ while (--i >= 0) {
47459
+ if (typeof this.prerelease[i] === 'number') {
47460
+ this.prerelease[i]++;
47461
+ i = -2;
47372
47462
  }
47373
- const isAuthenticated = yield firstValueFrom(this.isAuthenticated$);
47374
- if (!isAuthenticated) {
47375
- const errorMessage = `Authentication required.`;
47376
- this.transport.addLog(errorMessage);
47377
- return Promise.reject(errorMessage);
47463
+ }
47464
+ if (i === -1) {
47465
+ // didn't increment anything
47466
+ this.prerelease.push(0);
47467
+ }
47468
+ }
47469
+ if (identifier) {
47470
+ // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
47471
+ // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
47472
+ if (compareIdentifiers$1(this.prerelease[0], identifier) === 0) {
47473
+ if (isNaN(this.prerelease[1])) {
47474
+ this.prerelease = [identifier, 0];
47378
47475
  }
47379
- return yield getter();
47380
- });
47381
- }
47382
- _subscribeWhileAuthenticated(characteristicName) {
47383
- return this.selectedDevice$.pipe(switchMap((selectedDevice) => !osHasBluetoothSupport(selectedDevice)
47384
- ? EMPTY
47385
- : this.isAuthenticated$.pipe(distinctUntilChanged(), switchMap((isAuthenticated) => isAuthenticated
47386
- ? this.transport.subscribeToCharacteristic({
47387
- characteristicName
47388
- })
47389
- : EMPTY))), share());
47390
- }
47391
- focus() {
47392
- return this._focus$;
47393
- }
47394
- calm() {
47395
- return this._calm$;
47396
- }
47397
- accelerometer() {
47398
- return this._accelerometer$;
47399
- }
47400
- brainwaves(label) {
47401
- switch (label) {
47402
- default:
47403
- case "raw":
47404
- return defer(() => this.getInfo()).pipe(switchMap((deviceInfo) => this._brainwavesRaw$.pipe(csvBufferToEpoch(deviceInfo))));
47405
- case "rawUnfiltered":
47406
- return defer(() => this.getInfo()).pipe(switchMap((deviceInfo) => this._brainwavesRawUnfiltered$.pipe(csvBufferToEpoch(deviceInfo))));
47407
- case "psd":
47408
- return this._brainwavesPSD$;
47409
- case "powerByBand":
47410
- return this._brainwavesPowerByBand$;
47476
+ } else {
47477
+ this.prerelease = [identifier, 0];
47478
+ }
47411
47479
  }
47480
+ break
47481
+
47482
+ default:
47483
+ throw new Error(`invalid increment argument: ${release}`)
47412
47484
  }
47413
- signalQuality() {
47414
- return this._signalQuality$;
47415
- }
47416
- addMarker(label) {
47417
- return __awaiter$f(this, void 0, void 0, function* () {
47418
- yield this.dispatchAction({
47419
- action: "marker",
47420
- command: "add",
47421
- message: {
47422
- timestamp: Date.now(),
47423
- label
47424
- }
47425
- });
47426
- });
47427
- }
47428
- getInfo() {
47429
- return __awaiter$f(this, void 0, void 0, function* () {
47430
- return yield this._withAuthentication(() => firstValueFrom(this.transport.subscribeToCharacteristic({
47431
- characteristicName: "deviceInfo"
47432
- })));
47433
- });
47434
- }
47435
- status() {
47436
- return this._status$;
47437
- }
47438
- dispatchAction(action) {
47439
- return __awaiter$f(this, void 0, void 0, function* () {
47440
- return yield this._withAuthentication(() => this.transport.dispatchAction({
47441
- characteristicName: "actions",
47442
- action
47443
- }));
47444
- });
47445
- }
47446
- settings() {
47447
- return this._settings$;
47485
+ this.format();
47486
+ this.raw = this.version;
47487
+ return this
47488
+ }
47489
+ }
47490
+
47491
+ var semver = SemVer;
47492
+
47493
+ const compare = (a, b, loose) =>
47494
+ new semver(a, loose).compare(new semver(b, loose));
47495
+
47496
+ var compare_1 = compare;
47497
+
47498
+ const gte = (a, b, loose) => compare_1(a, b, loose) >= 0;
47499
+ var gte_1 = gte;
47500
+
47501
+ function osHasBluetoothSupport(selectedDevice, osVersion) {
47502
+ if (!selectedDevice) {
47503
+ return false;
47448
47504
  }
47449
- haptics(effects) {
47450
- const metric = "haptics";
47451
- return this.dispatchAction({
47452
- action: metric,
47453
- command: "queue",
47454
- responseRequired: true,
47455
- responseTimeout: 4000,
47456
- // @TODO: implement validation logic as per SDK
47457
- message: { effects }
47458
- });
47505
+ // Only the Crown supports Bluetooth
47506
+ const isCrown = Number(selectedDevice.modelVersion) >= 3;
47507
+ if (!isCrown) {
47508
+ return false;
47459
47509
  }
47460
- get wifi() {
47461
- return {
47462
- nearbyNetworks: () => this._wifiNearbyNetworks$,
47463
- connections: () => this._wifiConnections$,
47464
- connect: (ssid, password) => {
47465
- if (!ssid) {
47466
- return Promise.reject(`Missing ssid`);
47467
- }
47468
- return this.dispatchAction({
47469
- action: "wifi",
47470
- command: "connect",
47471
- responseRequired: true,
47472
- responseTimeout: 1000 * 60 * 2,
47473
- message: {
47474
- ssid,
47475
- password: password !== null && password !== void 0 ? password : null
47476
- }
47477
- });
47478
- },
47479
- forgetConnection: (ssid) => {
47480
- if (!ssid) {
47481
- return Promise.reject(`Missing ssid`);
47482
- }
47483
- return this.dispatchAction({
47484
- action: "wifi",
47485
- command: "forget-connection",
47486
- responseRequired: true,
47487
- responseTimeout: 1000 * 15,
47488
- message: {
47489
- ssid
47490
- }
47491
- });
47492
- },
47493
- reset: () => {
47494
- return this.dispatchAction({
47495
- action: "wifi",
47496
- command: "reset",
47497
- responseRequired: true,
47498
- responseTimeout: 1000 * 30,
47499
- message: {
47500
- // without this, the action will resolve as soon as the
47501
- // action is received by the OS
47502
- respondOnSuccess: true
47503
- }
47504
- });
47505
- },
47506
- speedTest: () => {
47507
- return this.dispatchAction({
47508
- action: "wifi",
47509
- command: "speed-test",
47510
- responseRequired: true,
47511
- responseTimeout: 1000 * 60 * 1 // 1 minute
47512
- });
47513
- }
47514
- };
47510
+ const isEmulator = !!(selectedDevice === null || selectedDevice === void 0 ? void 0 : selectedDevice.emulator);
47511
+ if (isEmulator) {
47512
+ return false;
47515
47513
  }
47514
+ // `osVersion` is updated in real time,
47515
+ // unlike accessing via `selectedDevice.osVersion`
47516
+ return gte_1(osVersion !== null && osVersion !== void 0 ? osVersion : selectedDevice.osVersion, "16.0.0");
47516
47517
  }
47517
47518
 
47518
47519
  var __awaiter$g = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
@@ -47567,6 +47568,7 @@ class Neurosity {
47567
47568
  if (!!bluetoothTransport) {
47568
47569
  this.bluetoothClient = new BluetoothClient({
47569
47570
  selectedDevice$: this.onDeviceChange(),
47571
+ osHasBluetoothSupport$: this._osHasBluetoothSupport(),
47570
47572
  createBluetoothToken: this.createBluetoothToken.bind(this),
47571
47573
  transport: bluetoothTransport
47572
47574
  });
@@ -47597,6 +47599,16 @@ class Neurosity {
47597
47599
  this.streamingMode$.next(streamingMode);
47598
47600
  }
47599
47601
  }
47602
+ /**
47603
+ *
47604
+ * @hidden
47605
+ */
47606
+ _osHasBluetoothSupport() {
47607
+ return combineLatest({
47608
+ selectedDevice: this.onDeviceChange(),
47609
+ osVersion: this.osVersion().pipe(startWith(null))
47610
+ }).pipe(map(({ selectedDevice, osVersion }) => osHasBluetoothSupport(selectedDevice, osVersion)));
47611
+ }
47600
47612
  /**
47601
47613
  * Subscribe to the device's streaming state changes and the current strategy
47602
47614
  *
@@ -47612,12 +47624,14 @@ class Neurosity {
47612
47624
  streamingState() {
47613
47625
  const isWifiOnline = (state) => [STATUS.ONLINE, STATUS.UPDATING].includes(state);
47614
47626
  return this.streamingMode$.pipe(switchMap((streamingMode) => {
47615
- return this.onDeviceChange().pipe(switchMap((selectDevice) => {
47616
- if (!selectDevice) {
47627
+ return combineLatest({
47628
+ selectedDevice: this.onDeviceChange(),
47629
+ osHasBluetoothSupport: this._osHasBluetoothSupport()
47630
+ }).pipe(switchMap(({ selectedDevice, osHasBluetoothSupport: osHasBluetoothSupport$$1 }) => {
47631
+ if (!selectedDevice) {
47617
47632
  return EMPTY;
47618
47633
  }
47619
- const isUnableToUseBluetooth = this.isMissingBluetoothTransport ||
47620
- !osHasBluetoothSupport(selectDevice);
47634
+ const isUnableToUseBluetooth = this.isMissingBluetoothTransport || !osHasBluetoothSupport$$1;
47621
47635
  if (isUnableToUseBluetooth) {
47622
47636
  return this.cloudClient.status().pipe(map(({ state }) => ({
47623
47637
  connected: isWifiOnline(state),
@@ -48283,7 +48297,7 @@ class Neurosity {
48283
48297
  if (hasOAuthError) {
48284
48298
  return throwError(() => OAuthError);
48285
48299
  }
48286
- return this.cloudClient.observeNamespace("info/osVersion");
48300
+ return this.cloudClient.osVersion();
48287
48301
  }
48288
48302
  /**
48289
48303
  * <StreamingModes wifi={true} bluetooth={true} />