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