@iam-protocol/pulse-sdk 0.3.4 → 0.3.6
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/index.d.mts +5 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +48 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +48 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/pulse.ts +25 -12
package/dist/index.mjs
CHANGED
|
@@ -30,9 +30,10 @@ async function captureAudio(options = {}) {
|
|
|
30
30
|
signal,
|
|
31
31
|
minDurationMs = MIN_CAPTURE_MS,
|
|
32
32
|
maxDurationMs = MAX_CAPTURE_MS,
|
|
33
|
-
onAudioLevel
|
|
33
|
+
onAudioLevel,
|
|
34
|
+
stream: preAcquiredStream
|
|
34
35
|
} = options;
|
|
35
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
36
|
+
const stream = preAcquiredStream ?? await navigator.mediaDevices.getUserMedia({
|
|
36
37
|
audio: {
|
|
37
38
|
sampleRate: TARGET_SAMPLE_RATE,
|
|
38
39
|
channelCount: 1,
|
|
@@ -42,6 +43,7 @@ async function captureAudio(options = {}) {
|
|
|
42
43
|
}
|
|
43
44
|
});
|
|
44
45
|
const ctx = new AudioContext({ sampleRate: TARGET_SAMPLE_RATE });
|
|
46
|
+
await ctx.resume();
|
|
45
47
|
const capturedSampleRate = ctx.sampleRate;
|
|
46
48
|
const source = ctx.createMediaStreamSource(stream);
|
|
47
49
|
const chunks = [];
|
|
@@ -118,7 +120,7 @@ async function captureMotion(options = {}) {
|
|
|
118
120
|
minDurationMs = MIN_CAPTURE_MS,
|
|
119
121
|
maxDurationMs = MAX_CAPTURE_MS
|
|
120
122
|
} = options;
|
|
121
|
-
const hasPermission = await requestMotionPermission();
|
|
123
|
+
const hasPermission = options.permissionGranted ?? await requestMotionPermission();
|
|
122
124
|
if (!hasPermission) return [];
|
|
123
125
|
const samples = [];
|
|
124
126
|
const startTime = performance.now();
|
|
@@ -1390,6 +1392,15 @@ async function processSensorData(sensorData, config, wallet, connection) {
|
|
|
1390
1392
|
error: "No voice data detected. Please speak the phrase clearly during capture."
|
|
1391
1393
|
};
|
|
1392
1394
|
}
|
|
1395
|
+
const hasPreviousData = loadVerificationData() !== null;
|
|
1396
|
+
if (hasPreviousData && !hasMotion && !hasTouch) {
|
|
1397
|
+
return {
|
|
1398
|
+
success: false,
|
|
1399
|
+
commitment: new Uint8Array(32),
|
|
1400
|
+
isFirstVerification: false,
|
|
1401
|
+
error: "Insufficient sensor data for re-verification. Please trace the curve and allow motion access."
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1393
1404
|
const features = await extractFeatures(sensorData);
|
|
1394
1405
|
const nonZero = features.filter((v) => v !== 0).length;
|
|
1395
1406
|
console.log(
|
|
@@ -1499,12 +1510,25 @@ var PulseSession = class {
|
|
|
1499
1510
|
async startAudio(onAudioLevel) {
|
|
1500
1511
|
if (this.audioStageState !== "idle")
|
|
1501
1512
|
throw new Error("Audio capture already started");
|
|
1513
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
1514
|
+
audio: {
|
|
1515
|
+
sampleRate: 16e3,
|
|
1516
|
+
channelCount: 1,
|
|
1517
|
+
echoCancellation: false,
|
|
1518
|
+
noiseSuppression: false,
|
|
1519
|
+
autoGainControl: false
|
|
1520
|
+
}
|
|
1521
|
+
});
|
|
1502
1522
|
this.audioStageState = "capturing";
|
|
1503
1523
|
this.audioController = new AbortController();
|
|
1504
1524
|
this.audioPromise = captureAudio({
|
|
1505
1525
|
signal: this.audioController.signal,
|
|
1506
|
-
onAudioLevel
|
|
1507
|
-
|
|
1526
|
+
onAudioLevel,
|
|
1527
|
+
stream
|
|
1528
|
+
}).catch(() => {
|
|
1529
|
+
stream.getTracks().forEach((t) => t.stop());
|
|
1530
|
+
return null;
|
|
1531
|
+
});
|
|
1508
1532
|
}
|
|
1509
1533
|
async stopAudio() {
|
|
1510
1534
|
if (this.audioStageState !== "capturing")
|
|
@@ -1520,10 +1544,16 @@ var PulseSession = class {
|
|
|
1520
1544
|
async startMotion() {
|
|
1521
1545
|
if (this.motionStageState !== "idle")
|
|
1522
1546
|
throw new Error("Motion capture already started");
|
|
1547
|
+
const hasPermission = await requestMotionPermission();
|
|
1548
|
+
if (!hasPermission) {
|
|
1549
|
+
this.motionStageState = "skipped";
|
|
1550
|
+
return;
|
|
1551
|
+
}
|
|
1523
1552
|
this.motionStageState = "capturing";
|
|
1524
1553
|
this.motionController = new AbortController();
|
|
1525
1554
|
this.motionPromise = captureMotion({
|
|
1526
|
-
signal: this.motionController.signal
|
|
1555
|
+
signal: this.motionController.signal,
|
|
1556
|
+
permissionGranted: true
|
|
1527
1557
|
}).catch(() => []);
|
|
1528
1558
|
}
|
|
1529
1559
|
async stopMotion() {
|
|
@@ -1539,6 +1569,9 @@ var PulseSession = class {
|
|
|
1539
1569
|
throw new Error("Motion capture already started");
|
|
1540
1570
|
this.motionStageState = "skipped";
|
|
1541
1571
|
}
|
|
1572
|
+
isMotionCapturing() {
|
|
1573
|
+
return this.motionStageState === "capturing";
|
|
1574
|
+
}
|
|
1542
1575
|
// --- Touch ---
|
|
1543
1576
|
async startTouch() {
|
|
1544
1577
|
if (this.touchStageState !== "idle")
|
|
@@ -1611,22 +1644,23 @@ var PulseSDK = class {
|
|
|
1611
1644
|
const session = this.createSession(touchElement);
|
|
1612
1645
|
const stopPromises = [];
|
|
1613
1646
|
try {
|
|
1614
|
-
await session.
|
|
1647
|
+
await session.startMotion();
|
|
1648
|
+
} catch {
|
|
1649
|
+
}
|
|
1650
|
+
if (session.isMotionCapturing()) {
|
|
1615
1651
|
stopPromises.push(
|
|
1616
|
-
new Promise((r) => setTimeout(r, DEFAULT_CAPTURE_MS)).then(() => session.
|
|
1652
|
+
new Promise((r) => setTimeout(r, DEFAULT_CAPTURE_MS)).then(() => session.stopMotion()).then(() => {
|
|
1617
1653
|
})
|
|
1618
1654
|
);
|
|
1619
|
-
} catch (err) {
|
|
1620
|
-
throw new Error(`Audio capture failed: ${err?.message ?? "microphone unavailable"}`);
|
|
1621
1655
|
}
|
|
1622
1656
|
try {
|
|
1623
|
-
await session.
|
|
1657
|
+
await session.startAudio();
|
|
1624
1658
|
stopPromises.push(
|
|
1625
|
-
new Promise((r) => setTimeout(r, DEFAULT_CAPTURE_MS)).then(() => session.
|
|
1659
|
+
new Promise((r) => setTimeout(r, DEFAULT_CAPTURE_MS)).then(() => session.stopAudio()).then(() => {
|
|
1626
1660
|
})
|
|
1627
1661
|
);
|
|
1628
|
-
} catch {
|
|
1629
|
-
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
throw new Error(`Audio capture failed: ${err?.message ?? "microphone unavailable"}`);
|
|
1630
1664
|
}
|
|
1631
1665
|
if (touchElement) {
|
|
1632
1666
|
try {
|