@sorisdk/matcher 0.6.10 → 0.6.11

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.
@@ -22,6 +22,7 @@ export class WasmMatcherEngine {
22
22
  free(): void;
23
23
  [Symbol.dispose](): void;
24
24
  bestMatch(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
25
+ bestMatchChecked(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
25
26
  bestMatchWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
26
27
  detectAudioMarker(pcm16_le: Uint8Array, sample_rate: number, config?: any | null): any;
27
28
  detectAudioMarkerWithLimits(pcm16_le: Uint8Array, sample_rate: number, config?: any | null, limits?: any | null): any;
@@ -33,6 +34,7 @@ export class WasmMatcherEngine {
33
34
  extractPitchReference(pcm16_le: Uint8Array, sample_rate: number): any;
34
35
  extractSpectralShiftReference(pcm16_le: Uint8Array, sample_rate: number): any;
35
36
  matchQuery(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
37
+ matchQueryChecked(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
36
38
  matchQueryWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
37
39
  constructor();
38
40
  /**
@@ -170,6 +170,22 @@ export class WasmMatcherEngine {
170
170
  }
171
171
  return takeFromExternrefTable0(ret[0]);
172
172
  }
173
+ /**
174
+ * @param {WasmMatcherDatabase} database
175
+ * @param {Uint8Array} query
176
+ * @param {any | null} [config]
177
+ * @returns {any}
178
+ */
179
+ bestMatchChecked(database, query, config) {
180
+ _assertClass(database, WasmMatcherDatabase);
181
+ const ptr0 = passArray8ToWasm0(query, wasm.__wbindgen_malloc);
182
+ const len0 = WASM_VECTOR_LEN;
183
+ const ret = wasm.wasmmatcherengine_bestMatchChecked(this.__wbg_ptr, database.__wbg_ptr, ptr0, len0, isLikeNone(config) ? 0 : addToExternrefTable0(config));
184
+ if (ret[2]) {
185
+ throw takeFromExternrefTable0(ret[1]);
186
+ }
187
+ return takeFromExternrefTable0(ret[0]);
188
+ }
173
189
  /**
174
190
  * @param {WasmMatcherDatabase} database
175
191
  * @param {Uint8Array} query
@@ -335,6 +351,22 @@ export class WasmMatcherEngine {
335
351
  }
336
352
  return takeFromExternrefTable0(ret[0]);
337
353
  }
354
+ /**
355
+ * @param {WasmMatcherDatabase} database
356
+ * @param {Uint8Array} query
357
+ * @param {any | null} [config]
358
+ * @returns {any}
359
+ */
360
+ matchQueryChecked(database, query, config) {
361
+ _assertClass(database, WasmMatcherDatabase);
362
+ const ptr0 = passArray8ToWasm0(query, wasm.__wbindgen_malloc);
363
+ const len0 = WASM_VECTOR_LEN;
364
+ const ret = wasm.wasmmatcherengine_matchQueryChecked(this.__wbg_ptr, database.__wbg_ptr, ptr0, len0, isLikeNone(config) ? 0 : addToExternrefTable0(config));
365
+ if (ret[2]) {
366
+ throw takeFromExternrefTable0(ret[1]);
367
+ }
368
+ return takeFromExternrefTable0(ret[0]);
369
+ }
338
370
  /**
339
371
  * @param {WasmMatcherDatabase} database
340
372
  * @param {Uint8Array} query
Binary file
@@ -25,6 +25,7 @@ export const wasmmatcherdatabase_new: () => number;
25
25
  export const wasmmatcherdatabase_removeEntry: (a: number, b: number, c: number) => [number, number];
26
26
  export const wasmmatcherdatabase_setEntryVariantMetadata: (a: number, b: number, c: number, d: number, e: number) => [number, number];
27
27
  export const wasmmatcherengine_bestMatch: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
28
+ export const wasmmatcherengine_bestMatchChecked: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
28
29
  export const wasmmatcherengine_bestMatchWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
29
30
  export const wasmmatcherengine_detectAudioMarker: (a: number, b: any, c: number, d: number) => [number, number, number];
30
31
  export const wasmmatcherengine_detectAudioMarkerWithLimits: (a: number, b: any, c: number, d: number, e: number) => [number, number, number];
@@ -35,9 +36,10 @@ export const wasmmatcherengine_estimatePitchRatioAgainstSpectralFeatures: (a: nu
35
36
  export const wasmmatcherengine_extractPitchReference: (a: number, b: number, c: number, d: number) => [number, number, number];
36
37
  export const wasmmatcherengine_extractSpectralShiftReference: (a: number, b: number, c: number, d: number) => [number, number, number];
37
38
  export const wasmmatcherengine_matchQuery: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
39
+ export const wasmmatcherengine_matchQueryChecked: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
38
40
  export const wasmmatcherengine_matchQueryWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
39
- export const wasmmatcherengine_resetDistinctMatchState: (a: number) => void;
40
41
  export const wasmmatcherengine_new: () => number;
42
+ export const wasmmatcherengine_resetDistinctMatchState: (a: number) => void;
41
43
  export const init_matcher_wasm: () => void;
42
44
  export const detectPatternMark: (a: any, b: number, c: number) => [number, number, number];
43
45
  export const wasmmatcherengine_detectPatternMark: (a: number, b: any, c: number, d: number) => [number, number, number];
package/dist/index.d.ts CHANGED
@@ -142,6 +142,24 @@ interface MatchResult {
142
142
  durationMillis?: number;
143
143
  afpType: AudioFingerprintType;
144
144
  }
145
+ type TimingCoordinateSpace = "entry_local" | "segment_local" | "unavailable";
146
+ type TimingUnavailableReason = "rejected_metadata" | "invalidated_metadata" | "incompatible_geometry" | "ambiguous_clock" | "out_of_range" | "window_origin_underflow" | "inconsistent_windows";
147
+ interface MatchTiming {
148
+ coordinateSpace: TimingCoordinateSpace;
149
+ /** Null when timing normalization failed. */
150
+ positionMillis: number | null;
151
+ durationMillis: number | null;
152
+ segmentIndex: number | null;
153
+ reason: TimingUnavailableReason | null;
154
+ }
155
+ interface CheckedMatchResult {
156
+ name: string;
157
+ score: number;
158
+ afpType: AudioFingerprintType;
159
+ /** Diagnostic entry offset only; consumers should use timing. */
160
+ rawPositionMillis: number;
161
+ timing: MatchTiming;
162
+ }
145
163
  interface VariantMatch {
146
164
  matchResult: MatchResult;
147
165
  variant: VariantDecision;
@@ -273,6 +291,11 @@ declare class MatcherSession {
273
291
  clear(): Promise<void>;
274
292
  getEntryMetadata(name: string): Promise<EntryMetadata | null>;
275
293
  match(query: Uint8Array, config?: MatchConfig): Promise<MatchResult[]>;
294
+ /**
295
+ * Read one observation with explicit timing coordinates. This direct accessor
296
+ * does not emit legacy match events or update recognition deduplication state.
297
+ */
298
+ bestMatchChecked(query: Uint8Array, config?: MatchConfig): Promise<CheckedMatchResult | null>;
276
299
  bestMatch(query: Uint8Array, config?: MatchConfig): Promise<MatchResult | null>;
277
300
  estimatePitchRatio(pcm16Le: Uint8Array, sampleRate: number): Promise<PitchRatioEstimate>;
278
301
  extractPitchReference(pcm16Le: Uint8Array, sampleRate: number): Promise<PitchReferenceEstimate>;
@@ -304,4 +327,4 @@ declare function __resetMatcherWasmForTests(): void;
304
327
  /** Maximum gap between successful observations in one continuous material run. */
305
328
  declare const MATCH_CONTINUITY_GAP_MS = 30000;
306
329
 
307
- export { AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_OVERFLOW_REASON, AudioFingerprintType, type AudioMarkerCode, type AudioMarkerConfig, type AudioMarkerDetection, type AudioMarkerLevelDiagnostics, type AudioMarkerLevelPolicy, type AudioMarkerLimits, DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS, DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES, DEFAULT_MAX_AUDIO_MARKER_SAMPLES, DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS, DEFAULT_MAX_PACK_BYTES, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, MATCH_CONTINUITY_GAP_MS, type MatchConfig, type MatchResult, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, type PitchRatioEstimate, type PitchReferenceEstimate, type VariantDecision, type VariantDecisionStatus, type VariantMatch, type VariantMatchConfig, type VariantMatchQuery, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
330
+ export { AUDIO_MARKER_INPUT_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_BUDGET_EXCEEDED_REASON, AUDIO_MARKER_WORK_OVERFLOW_REASON, AudioFingerprintType, type AudioMarkerCode, type AudioMarkerConfig, type AudioMarkerDetection, type AudioMarkerLevelDiagnostics, type AudioMarkerLevelPolicy, type AudioMarkerLimits, type CheckedMatchResult, DEFAULT_MAX_AUDIO_MARKER_DURATION_MILLIS, DEFAULT_MAX_AUDIO_MARKER_PCM_BYTES, DEFAULT_MAX_AUDIO_MARKER_SAMPLES, DEFAULT_MAX_AUDIO_MARKER_WORK_UNITS, DEFAULT_MAX_PACK_BYTES, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, MATCH_CONTINUITY_GAP_MS, type MatchConfig, type MatchResult, type MatchTiming, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, type PitchRatioEstimate, type PitchReferenceEstimate, type TimingCoordinateSpace, type TimingUnavailableReason, type VariantDecision, type VariantDecisionStatus, type VariantMatch, type VariantMatchConfig, type VariantMatchQuery, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
package/dist/index.js CHANGED
@@ -478,6 +478,28 @@ var WrappedDatabase = class {
478
478
  var WrappedMatcher = class {
479
479
  constructor(inner) {
480
480
  this.inner = inner;
481
+ const findMatchesCheckedFn = getCallable(this.inner, [
482
+ "findMatchesChecked",
483
+ "find_matches_checked",
484
+ "matchQueryChecked",
485
+ "match_query_checked"
486
+ ]);
487
+ if (findMatchesCheckedFn) {
488
+ this.findMatchesChecked = (database, query, config) => {
489
+ const dbRecord = typeof database.raw === "function" ? database.raw() : database;
490
+ const raw = findMatchesCheckedFn.call(this.inner, dbRecord, query, config);
491
+ if (!Array.isArray(raw)) throw new Error("Invalid checked match results");
492
+ return raw.map(normalizeCheckedResult);
493
+ };
494
+ }
495
+ const bestMatchCheckedFn = getCallable(this.inner, ["bestMatchChecked", "best_match_checked"]);
496
+ if (bestMatchCheckedFn) {
497
+ this.bestMatchChecked = (database, query, config) => {
498
+ const dbRecord = typeof database.raw === "function" ? database.raw() : database;
499
+ const raw = bestMatchCheckedFn.call(this.inner, dbRecord, query, config);
500
+ return raw == null ? null : normalizeCheckedResult(raw);
501
+ };
502
+ }
481
503
  const estimatePitchRatioFn = getCallable(this.inner, [
482
504
  "estimatePitchRatio",
483
505
  "estimate_pitch_ratio"
@@ -555,6 +577,8 @@ var WrappedMatcher = class {
555
577
  }
556
578
  }
557
579
  inner;
580
+ findMatchesChecked;
581
+ bestMatchChecked;
558
582
  estimatePitchRatio;
559
583
  extractPitchReference;
560
584
  estimatePitchRatioAgainstReference;
@@ -645,6 +669,56 @@ function normalizeResult(raw) {
645
669
  }
646
670
  return result;
647
671
  }
672
+ var TIMING_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
673
+ "rejected_metadata",
674
+ "invalidated_metadata",
675
+ "incompatible_geometry",
676
+ "ambiguous_clock",
677
+ "out_of_range",
678
+ "window_origin_underflow",
679
+ "inconsistent_windows"
680
+ ]);
681
+ function normalizeCheckedResult(raw) {
682
+ const invalid = () => new Error("Invalid checked match result");
683
+ if (!raw || typeof raw !== "object") {
684
+ throw invalid();
685
+ }
686
+ const record = raw;
687
+ if (!record.timing || typeof record.timing !== "object") {
688
+ throw invalid();
689
+ }
690
+ const source = record.timing;
691
+ const integer = (camel, snake) => {
692
+ const value = camel ?? snake;
693
+ if (value == null) return null;
694
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
695
+ throw invalid();
696
+ }
697
+ return value;
698
+ };
699
+ const coordinateSpace = source.coordinateSpace ?? source.coordinate_space;
700
+ const timing = {
701
+ coordinateSpace,
702
+ positionMillis: integer(source.positionMillis, source.position_millis),
703
+ durationMillis: integer(source.durationMillis, source.duration_millis),
704
+ segmentIndex: integer(source.segmentIndex, source.segment_index),
705
+ reason: source.reason ?? null
706
+ };
707
+ const available = timing.reason === null && timing.positionMillis !== null;
708
+ const validTiming = coordinateSpace === "unavailable" ? timing.positionMillis === null && timing.durationMillis === null && timing.segmentIndex === null && TIMING_UNAVAILABLE_REASONS.has(timing.reason) : coordinateSpace === "entry_local" ? available && timing.durationMillis === null && timing.segmentIndex === null : coordinateSpace === "segment_local" && available && timing.durationMillis !== null && timing.segmentIndex !== null && timing.positionMillis < timing.durationMillis;
709
+ const rawPositionMillis = integer(record.rawPositionMillis, record.raw_position_millis);
710
+ const afpType = record.afpType ?? record.afp_type;
711
+ if (!validTiming || typeof record.name !== "string" || record.name.length === 0 || typeof record.score !== "number" || !Number.isFinite(record.score) || record.score < 0 || ![1, 2, 4].includes(afpType) || rawPositionMillis === null) {
712
+ throw invalid();
713
+ }
714
+ return {
715
+ name: record.name,
716
+ score: record.score,
717
+ afpType,
718
+ rawPositionMillis,
719
+ timing
720
+ };
721
+ }
648
722
  function normalizeVariantMatch(raw) {
649
723
  const rawMatch = raw.matchResult ?? raw.match;
650
724
  const rawVariant = raw.variant ?? {};
@@ -1265,6 +1339,26 @@ var MatcherSession = class {
1265
1339
  handleError(this.events, "match", error);
1266
1340
  }
1267
1341
  }
1342
+ /**
1343
+ * Read one observation with explicit timing coordinates. This direct accessor
1344
+ * does not emit legacy match events or update recognition deduplication state.
1345
+ */
1346
+ async bestMatchChecked(query, config) {
1347
+ const generation = this.recognitionGeneration;
1348
+ try {
1349
+ await this.ensureLiveReady();
1350
+ if (generation !== this.recognitionGeneration) return null;
1351
+ const safeQuery = toUint8ArrayCopy(query);
1352
+ const matcher = this.matcherOrThrow();
1353
+ if (!matcher.bestMatchChecked) {
1354
+ throw new Error("WASM matcher is missing bestMatchChecked; upgrade the WASM assets");
1355
+ }
1356
+ if (generation !== this.recognitionGeneration || this.destroyed) return null;
1357
+ return matcher.bestMatchChecked(this.databaseOrThrow(), safeQuery, config);
1358
+ } catch (error) {
1359
+ handleError(this.events, "bestMatchChecked", error);
1360
+ }
1361
+ }
1268
1362
  async bestMatch(query, config) {
1269
1363
  const generation = this.recognitionGeneration;
1270
1364
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sorisdk/matcher",
3
- "version": "0.6.10",
3
+ "version": "0.6.11",
4
4
  "description": "Browser WASM SDK for audio fingerprint matching",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",