@iam-protocol/pulse-sdk 0.1.1 → 0.2.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.
package/dist/index.mjs CHANGED
@@ -12946,6 +12946,7 @@ var BN254_SCALAR_FIELD = BigInt(
12946
12946
  );
12947
12947
  var FINGERPRINT_BITS = 256;
12948
12948
  var DEFAULT_THRESHOLD = 30;
12949
+ var DEFAULT_MIN_DISTANCE = 3;
12949
12950
  var PROOF_A_SIZE = 64;
12950
12951
  var PROOF_B_SIZE = 128;
12951
12952
  var PROOF_C_SIZE = 64;
@@ -12966,8 +12967,7 @@ async function captureAudio(options = {}) {
12966
12967
  const {
12967
12968
  signal,
12968
12969
  minDurationMs = MIN_CAPTURE_MS,
12969
- maxDurationMs = MAX_CAPTURE_MS,
12970
- onAudioLevel
12970
+ maxDurationMs = MAX_CAPTURE_MS
12971
12971
  } = options;
12972
12972
  const stream = await navigator.mediaDevices.getUserMedia({
12973
12973
  audio: {
@@ -12987,13 +12987,7 @@ async function captureAudio(options = {}) {
12987
12987
  const bufferSize = 4096;
12988
12988
  const processor = ctx.createScriptProcessor(bufferSize, 1, 1);
12989
12989
  processor.onaudioprocess = (e2) => {
12990
- const data = e2.inputBuffer.getChannelData(0);
12991
- chunks.push(new Float32Array(data));
12992
- if (onAudioLevel) {
12993
- let sum = 0;
12994
- for (let i = 0; i < data.length; i++) sum += data[i] * data[i];
12995
- onAudioLevel(Math.sqrt(sum / data.length));
12996
- }
12990
+ chunks.push(new Float32Array(e2.inputBuffer.getChannelData(0)));
12997
12991
  };
12998
12992
  source.connect(processor);
12999
12993
  processor.connect(ctx.destination);
@@ -13192,6 +13186,37 @@ function condense(values) {
13192
13186
  kurtosis: kurtosis(values)
13193
13187
  };
13194
13188
  }
13189
+ function entropy(values, bins = 16) {
13190
+ if (values.length < 2) return 0;
13191
+ const min = Math.min(...values);
13192
+ const max = Math.max(...values);
13193
+ if (min === max) return 0;
13194
+ const counts = new Array(bins).fill(0);
13195
+ const range = max - min;
13196
+ for (const v of values) {
13197
+ const idx = Math.min(Math.floor((v - min) / range * bins), bins - 1);
13198
+ counts[idx]++;
13199
+ }
13200
+ let h = 0;
13201
+ for (const c of counts) {
13202
+ if (c > 0) {
13203
+ const p = c / values.length;
13204
+ h -= p * Math.log2(p);
13205
+ }
13206
+ }
13207
+ return h;
13208
+ }
13209
+ function autocorrelation(values, lag = 1) {
13210
+ if (values.length <= lag) return 0;
13211
+ const m = mean(values);
13212
+ const v = variance(values, m);
13213
+ if (v === 0) return 0;
13214
+ let sum = 0;
13215
+ for (let i = 0; i < values.length - lag; i++) {
13216
+ sum += (values[i] - m) * (values[i + lag] - m);
13217
+ }
13218
+ return sum / ((values.length - lag) * v);
13219
+ }
13195
13220
  function fuseFeatures(audio, motion, touch) {
13196
13221
  return [...audio, ...motion, ...touch];
13197
13222
  }
@@ -13206,10 +13231,10 @@ function extractMFCC(audio) {
13206
13231
  try {
13207
13232
  Meyda = __require("meyda");
13208
13233
  } catch {
13209
- return new Array(NUM_MFCC * 3 * 4).fill(0);
13234
+ return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
13210
13235
  }
13211
13236
  const numFrames = Math.floor((samples.length - FRAME_SIZE) / HOP_SIZE) + 1;
13212
- if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4).fill(0);
13237
+ if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
13213
13238
  const mfccFrames = [];
13214
13239
  for (let i = 0; i < numFrames; i++) {
13215
13240
  const start = i * HOP_SIZE;
@@ -13244,6 +13269,10 @@ function extractMFCC(audio) {
13244
13269
  const stats = condense(dd);
13245
13270
  features.push(stats.mean, stats.variance, stats.skewness, stats.kurtosis);
13246
13271
  }
13272
+ for (let c = 0; c < NUM_MFCC; c++) {
13273
+ const raw = mfccFrames.map((f) => f[c] ?? 0);
13274
+ features.push(entropy(raw));
13275
+ }
13247
13276
  return features;
13248
13277
  }
13249
13278
  function computeDeltas(frames) {
@@ -13258,7 +13287,7 @@ function computeDeltas(frames) {
13258
13287
 
13259
13288
  // src/extraction/kinematic.ts
13260
13289
  function extractMotionFeatures(samples) {
13261
- if (samples.length < 5) return new Array(48).fill(0);
13290
+ if (samples.length < 5) return new Array(54).fill(0);
13262
13291
  const axes = {
13263
13292
  ax: samples.map((s) => s.ax),
13264
13293
  ay: samples.map((s) => s.ay),
@@ -13284,10 +13313,19 @@ function extractMotionFeatures(samples) {
13284
13313
  jounceStats.kurtosis
13285
13314
  );
13286
13315
  }
13316
+ for (const values of Object.values(axes)) {
13317
+ const jerk = derivative(values);
13318
+ const windowSize = Math.max(5, Math.floor(jerk.length / 4));
13319
+ const windowVariances = [];
13320
+ for (let i = 0; i <= jerk.length - windowSize; i += windowSize) {
13321
+ windowVariances.push(variance(jerk.slice(i, i + windowSize)));
13322
+ }
13323
+ features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
13324
+ }
13287
13325
  return features;
13288
13326
  }
13289
13327
  function extractTouchFeatures(samples) {
13290
- if (samples.length < 5) return new Array(32).fill(0);
13328
+ if (samples.length < 5) return new Array(36).fill(0);
13291
13329
  const x = samples.map((s) => s.x);
13292
13330
  const y = samples.map((s) => s.y);
13293
13331
  const pressure = samples.map((s) => s.pressure);
@@ -13307,6 +13345,14 @@ function extractTouchFeatures(samples) {
13307
13345
  const jerkY = derivative(accY);
13308
13346
  features.push(...Object.values(condense(jerkX)));
13309
13347
  features.push(...Object.values(condense(jerkY)));
13348
+ for (const values of [vx, vy, pressure, area]) {
13349
+ const windowSize = Math.max(5, Math.floor(values.length / 4));
13350
+ const windowVariances = [];
13351
+ for (let i = 0; i <= values.length - windowSize; i += windowSize) {
13352
+ windowVariances.push(variance(values.slice(i, i + windowSize)));
13353
+ }
13354
+ features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
13355
+ }
13310
13356
  return features;
13311
13357
  }
13312
13358
  function derivative(values) {
@@ -13488,7 +13534,7 @@ async function getSnarkjs() {
13488
13534
  }
13489
13535
  return snarkjsModule;
13490
13536
  }
13491
- function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
13537
+ function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD, minDistance = DEFAULT_MIN_DISTANCE) {
13492
13538
  return {
13493
13539
  ft_new: current.fingerprint,
13494
13540
  ft_prev: previous.fingerprint,
@@ -13496,7 +13542,8 @@ function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
13496
13542
  salt_prev: previous.salt.toString(),
13497
13543
  commitment_new: current.commitment.toString(),
13498
13544
  commitment_prev: previous.commitment.toString(),
13499
- threshold: threshold.toString()
13545
+ threshold: threshold.toString(),
13546
+ min_distance: minDistance.toString()
13500
13547
  };
13501
13548
  }
13502
13549
  async function generateProof(input, wasmPath, zkeyPath) {
@@ -13714,7 +13761,7 @@ var inMemoryStore = null;
13714
13761
 
13715
13762
  // src/pulse.ts
13716
13763
  function extractFeatures(data) {
13717
- const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(156).fill(0);
13764
+ const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(169).fill(0);
13718
13765
  const motionFeatures = extractMotionFeatures(data.motion);
13719
13766
  const touchFeatures = extractTouchFeatures(data.touch);
13720
13767
  return fuseFeatures(audioFeatures, motionFeatures, touchFeatures);
@@ -13810,14 +13857,13 @@ var PulseSession = class {
13810
13857
  this.touchElement = touchElement;
13811
13858
  }
13812
13859
  // --- Audio ---
13813
- async startAudio(onAudioLevel) {
13860
+ async startAudio() {
13814
13861
  if (this.audioStageState !== "idle")
13815
13862
  throw new Error("Audio capture already started");
13816
13863
  this.audioStageState = "capturing";
13817
13864
  this.audioController = new AbortController();
13818
13865
  this.audioPromise = captureAudio({
13819
- signal: this.audioController.signal,
13820
- onAudioLevel
13866
+ signal: this.audioController.signal
13821
13867
  }).catch(() => null);
13822
13868
  }
13823
13869
  async stopAudio() {
@@ -14065,6 +14111,28 @@ function generatePhrase(wordCount = 5) {
14065
14111
  }
14066
14112
  return words.join(" ");
14067
14113
  }
14114
+ function generatePhraseSequence(count = 3, wordCount = 4) {
14115
+ const subsetSize = Math.floor(SYLLABLES.length / count);
14116
+ const phrases = [];
14117
+ for (let p = 0; p < count; p++) {
14118
+ const start = p * subsetSize % SYLLABLES.length;
14119
+ const subset = [
14120
+ ...SYLLABLES.slice(start, start + subsetSize),
14121
+ ...SYLLABLES.slice(0, Math.max(0, start + subsetSize - SYLLABLES.length))
14122
+ ];
14123
+ const words = [];
14124
+ for (let w = 0; w < wordCount; w++) {
14125
+ const syllableCount = 2 + Math.floor(Math.random() * 2);
14126
+ let word = "";
14127
+ for (let s = 0; s < syllableCount; s++) {
14128
+ word += subset[Math.floor(Math.random() * subset.length)];
14129
+ }
14130
+ words.push(word);
14131
+ }
14132
+ phrases.push(words.join(" "));
14133
+ }
14134
+ return phrases;
14135
+ }
14068
14136
 
14069
14137
  // src/challenge/lissajous.ts
14070
14138
  function randomLissajousParams() {
@@ -14095,8 +14163,36 @@ function generateLissajousPoints(params) {
14095
14163
  }
14096
14164
  return result;
14097
14165
  }
14166
+ function generateLissajousSequence(count = 2) {
14167
+ const allRatios = [
14168
+ [1, 2],
14169
+ [2, 3],
14170
+ [3, 4],
14171
+ [3, 5],
14172
+ [4, 5],
14173
+ [1, 3],
14174
+ [2, 5],
14175
+ [5, 6],
14176
+ [3, 7],
14177
+ [4, 7]
14178
+ ];
14179
+ const shuffled = [...allRatios].sort(() => Math.random() - 0.5);
14180
+ const sequence = [];
14181
+ for (let i = 0; i < count; i++) {
14182
+ const pair = shuffled[i % shuffled.length];
14183
+ const params = {
14184
+ a: pair[0],
14185
+ b: pair[1],
14186
+ delta: Math.PI * (0.1 + Math.random() * 0.8),
14187
+ points: 200
14188
+ };
14189
+ sequence.push({ params, points: generateLissajousPoints(params) });
14190
+ }
14191
+ return sequence;
14192
+ }
14098
14193
  export {
14099
14194
  DEFAULT_CAPTURE_MS,
14195
+ DEFAULT_MIN_DISTANCE,
14100
14196
  DEFAULT_THRESHOLD,
14101
14197
  FINGERPRINT_BITS,
14102
14198
  MAX_CAPTURE_MS,
@@ -14104,13 +14200,17 @@ export {
14104
14200
  PROGRAM_IDS,
14105
14201
  PulseSDK,
14106
14202
  PulseSession,
14203
+ autocorrelation,
14107
14204
  bigintToBytes32,
14108
14205
  computeCommitment,
14109
14206
  condense,
14207
+ entropy,
14110
14208
  fetchIdentityState,
14111
14209
  fuseFeatures,
14112
14210
  generateLissajousPoints,
14211
+ generateLissajousSequence,
14113
14212
  generatePhrase,
14213
+ generatePhraseSequence,
14114
14214
  generateProof,
14115
14215
  generateSalt,
14116
14216
  generateSolanaProof,