craft-native 0.0.150 → 0.0.152

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/mobile.js CHANGED
@@ -823,6 +823,164 @@ var music = {
823
823
  };
824
824
  }
825
825
  };
826
+ var HR_AUTHORIZATIONS = ["authorized", "denied", "restricted", "notDetermined", "unavailable"];
827
+ var HR_BLUETOOTH = ["poweredOn", "poweredOff", "unauthorized", "unsupported", "resetting", "unknown"];
828
+ var HR_CONNECTIONS = ["disconnected", "connecting", "connected", "reconnecting"];
829
+ function hrRecord(value) {
830
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
831
+ }
832
+ function hrNumber(value) {
833
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
834
+ }
835
+ function hrBpm(value) {
836
+ const bpm = hrNumber(value);
837
+ return bpm !== null && bpm >= 20 && bpm <= 250 ? Math.round(bpm) : null;
838
+ }
839
+ function hrIntervals(value) {
840
+ return Array.isArray(value) ? value.filter((ms) => typeof ms === "number" && Number.isFinite(ms) && ms > 0 && ms < 5000) : [];
841
+ }
842
+ function normalizeHeartRateDevice(value) {
843
+ const raw = hrRecord(value);
844
+ if (typeof raw.id !== "string" || !raw.id)
845
+ return null;
846
+ return { id: raw.id, name: typeof raw.name === "string" && raw.name.trim() ? raw.name : "Heart rate monitor", rssi: hrNumber(raw.rssi) };
847
+ }
848
+ function normalizeHeartRateRecording(value) {
849
+ const raw = hrRecord(value);
850
+ return {
851
+ id: typeof raw.id === "string" ? raw.id : null,
852
+ active: raw.active === true,
853
+ paused: raw.paused === true,
854
+ startedAt: hrNumber(raw.startedAt),
855
+ sampleCount: hrNumber(raw.sampleCount)
856
+ };
857
+ }
858
+ function normalizeHeartRateState(value) {
859
+ const raw = hrRecord(value);
860
+ return {
861
+ available: raw.available !== false && Object.keys(raw).length > 0,
862
+ bluetooth: HR_BLUETOOTH.includes(raw.bluetooth) ? raw.bluetooth : "unknown",
863
+ authorization: HR_AUTHORIZATIONS.includes(raw.authorization) ? raw.authorization : "unavailable",
864
+ scanning: raw.scanning === true,
865
+ connection: HR_CONNECTIONS.includes(raw.connection) ? raw.connection : "disconnected",
866
+ device: normalizeHeartRateDevice(raw.device),
867
+ lastDevice: normalizeHeartRateDevice(raw.lastDevice),
868
+ bpm: hrBpm(raw.bpm),
869
+ lastReadingAt: hrNumber(raw.lastReadingAt),
870
+ recording: normalizeHeartRateRecording(raw.recording)
871
+ };
872
+ }
873
+ function normalizeHeartRateReading(value) {
874
+ const raw = hrRecord(value);
875
+ const bpm = hrBpm(raw.bpm);
876
+ if (bpm === null)
877
+ return null;
878
+ return {
879
+ bpm,
880
+ rrIntervals: hrIntervals(raw.rrIntervals),
881
+ contact: typeof raw.contact === "boolean" ? raw.contact : null,
882
+ energyExpended: hrNumber(raw.energyExpended),
883
+ timestamp: hrNumber(raw.timestamp) ?? Date.now(),
884
+ deviceId: typeof raw.deviceId === "string" ? raw.deviceId : null
885
+ };
886
+ }
887
+ function normalizeHeartRateSamples(value) {
888
+ if (!Array.isArray(value))
889
+ return [];
890
+ const seen = new Set;
891
+ const samples = [];
892
+ for (const item of value) {
893
+ const raw = hrRecord(item);
894
+ const timestamp = hrNumber(raw.timestamp);
895
+ const bpm = hrBpm(raw.bpm);
896
+ if (timestamp === null || bpm === null || seen.has(timestamp))
897
+ continue;
898
+ seen.add(timestamp);
899
+ const rr = hrIntervals(raw.rr);
900
+ samples.push(rr.length ? { timestamp, bpm, rr } : { timestamp, bpm });
901
+ }
902
+ return samples.sort((a, b) => a.timestamp - b.timestamp);
903
+ }
904
+ function noHeartRate() {
905
+ return normalizeHeartRateState({ available: false });
906
+ }
907
+ function nativeHeartRate() {
908
+ const craft = getCraftRoot();
909
+ if (!craft?.heartRate || craft.capabilities?.heartRate === false)
910
+ return;
911
+ return craft.heartRate;
912
+ }
913
+ var heartRateWatch = { readings: new Set, devices: new Set, states: new Set, stop: null };
914
+ function hrNotify(listeners, value) {
915
+ for (const listener of [...listeners]) {
916
+ try {
917
+ listener(value);
918
+ } catch {}
919
+ }
920
+ }
921
+ function hrSubscribe(set, callback) {
922
+ if (!nativeHeartRate() || typeof window === "undefined")
923
+ return () => {};
924
+ set.add(callback);
925
+ if (!heartRateWatch.stop) {
926
+ const onEvent = (event) => {
927
+ const detail = hrRecord(event.detail);
928
+ if (detail.type === "reading") {
929
+ const reading = normalizeHeartRateReading(detail);
930
+ if (reading)
931
+ hrNotify(heartRateWatch.readings, reading);
932
+ } else if (detail.type === "device") {
933
+ const device2 = normalizeHeartRateDevice(detail.device);
934
+ if (device2)
935
+ hrNotify(heartRateWatch.devices, device2);
936
+ } else if (detail.type === "state") {
937
+ hrNotify(heartRateWatch.states, normalizeHeartRateState(detail));
938
+ }
939
+ };
940
+ window.addEventListener("craftHeartRate", onEvent);
941
+ heartRateWatch.stop = () => window.removeEventListener("craftHeartRate", onEvent);
942
+ }
943
+ return () => {
944
+ if (!set.delete(callback))
945
+ return;
946
+ if (heartRateWatch.readings.size + heartRateWatch.devices.size + heartRateWatch.states.size > 0)
947
+ return;
948
+ heartRateWatch.stop?.();
949
+ heartRateWatch.stop = null;
950
+ };
951
+ }
952
+ async function heartRateCall(method, fallback, normalize, ...args) {
953
+ const native = nativeHeartRate();
954
+ if (typeof native?.[method] !== "function")
955
+ return fallback();
956
+ return normalize(await native[method](...args));
957
+ }
958
+ var noRecording = () => normalizeHeartRateRecording({});
959
+ var heartRate = {
960
+ isAvailable() {
961
+ return typeof nativeHeartRate()?.getState === "function";
962
+ },
963
+ supports(method) {
964
+ return typeof nativeHeartRate()?.[method] === "function";
965
+ },
966
+ getState: () => heartRateCall("getState", noHeartRate, normalizeHeartRateState),
967
+ requestAuthorization: () => heartRateCall("requestAuthorization", () => "unavailable", (value) => HR_AUTHORIZATIONS.includes(value) ? value : "unavailable"),
968
+ scan: (options = {}) => heartRateCall("scan", noHeartRate, normalizeHeartRateState, options),
969
+ stopScan: () => heartRateCall("stopScan", noHeartRate, normalizeHeartRateState),
970
+ getDevices: () => heartRateCall("getDevices", () => [], (value) => Array.isArray(value) ? value.map(normalizeHeartRateDevice).filter((device2) => device2 !== null) : []),
971
+ connect: (id) => heartRateCall("connect", noHeartRate, normalizeHeartRateState, id),
972
+ reconnect: () => heartRateCall("reconnect", noHeartRate, normalizeHeartRateState),
973
+ disconnect: (options = {}) => heartRateCall("disconnect", noHeartRate, normalizeHeartRateState, options),
974
+ startRecording: () => heartRateCall("startRecording", noRecording, normalizeHeartRateRecording),
975
+ pauseRecording: () => heartRateCall("pauseRecording", noRecording, normalizeHeartRateRecording),
976
+ resumeRecording: () => heartRateCall("resumeRecording", noRecording, normalizeHeartRateRecording),
977
+ stopRecording: () => heartRateCall("stopRecording", () => ({ ...noRecording(), samples: [] }), (value) => ({ ...normalizeHeartRateRecording(value), samples: normalizeHeartRateSamples(hrRecord(value).samples) })),
978
+ getRecordingState: () => heartRateCall("getRecordingState", noRecording, normalizeHeartRateRecording),
979
+ readRecording: (options = {}) => heartRateCall("readRecording", () => [], normalizeHeartRateSamples, options),
980
+ onReading: (callback) => hrSubscribe(heartRateWatch.readings, callback),
981
+ onDevice: (callback) => hrSubscribe(heartRateWatch.devices, callback),
982
+ onState: (callback) => hrSubscribe(heartRateWatch.states, callback)
983
+ };
826
984
  var deepLinks = {
827
985
  async getInitialURL() {
828
986
  return normalizeDeepLinkURL(await getCraftRoot()?.deepLinks?.getInitialURL?.());
@@ -1314,6 +1472,10 @@ export {
1314
1472
  permissions,
1315
1473
  notifications,
1316
1474
  normalizeNowPlaying,
1475
+ normalizeHeartRateState,
1476
+ normalizeHeartRateSamples,
1477
+ normalizeHeartRateReading,
1478
+ normalizeHeartRateDevice,
1317
1479
  normalizeDeepLinkURL,
1318
1480
  network,
1319
1481
  music,
@@ -1323,6 +1485,7 @@ export {
1323
1485
  keepAwake,
1324
1486
  isSnapshotName,
1325
1487
  hybrid,
1488
+ heartRate,
1326
1489
  health,
1327
1490
  haptics,
1328
1491
  dialog,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "craft-native",
3
- "version": "0.0.150",
3
+ "version": "0.0.152",
4
4
  "type": "module",
5
5
  "description": "Build desktop apps with web languages - TypeScript SDK for Craft",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",