@sorisdk/matcher 0.1.4 → 0.4.0

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.
@@ -12,6 +12,7 @@ export class WasmMatcherDatabase {
12
12
  loadPackBytesAppend(bytes: Uint8Array): void;
13
13
  constructor();
14
14
  removeEntry(name: string): void;
15
+ setEntryVariantMetadata(name: string, encoded_metadata: string): void;
15
16
  readonly length: number;
16
17
  }
17
18
 
@@ -19,7 +20,9 @@ export class WasmMatcherEngine {
19
20
  free(): void;
20
21
  [Symbol.dispose](): void;
21
22
  bestMatch(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
23
+ bestMatchWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
22
24
  matchQuery(database: WasmMatcherDatabase, query: Uint8Array, config?: any | null): any;
25
+ matchQueryWithVariants(database: WasmMatcherDatabase, query: Uint8Array, pitch_query: any, config?: any | null, variant_config?: any | null): any;
23
26
  constructor();
24
27
  resetDistinctMatchState(): void;
25
28
  }
@@ -102,6 +102,20 @@ export class WasmMatcherDatabase {
102
102
  throw takeFromExternrefTable0(ret[0]);
103
103
  }
104
104
  }
105
+ /**
106
+ * @param {string} name
107
+ * @param {string} encoded_metadata
108
+ */
109
+ setEntryVariantMetadata(name, encoded_metadata) {
110
+ const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
111
+ const len0 = WASM_VECTOR_LEN;
112
+ const ptr1 = passStringToWasm0(encoded_metadata, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
113
+ const len1 = WASM_VECTOR_LEN;
114
+ const ret = wasm.wasmmatcherdatabase_setEntryVariantMetadata(this.__wbg_ptr, ptr0, len0, ptr1, len1);
115
+ if (ret[1]) {
116
+ throw takeFromExternrefTable0(ret[0]);
117
+ }
118
+ }
105
119
  }
106
120
  if (Symbol.dispose) WasmMatcherDatabase.prototype[Symbol.dispose] = WasmMatcherDatabase.prototype.free;
107
121
 
@@ -132,6 +146,24 @@ export class WasmMatcherEngine {
132
146
  }
133
147
  return takeFromExternrefTable0(ret[0]);
134
148
  }
149
+ /**
150
+ * @param {WasmMatcherDatabase} database
151
+ * @param {Uint8Array} query
152
+ * @param {any} pitch_query
153
+ * @param {any | null} [config]
154
+ * @param {any | null} [variant_config]
155
+ * @returns {any}
156
+ */
157
+ bestMatchWithVariants(database, query, pitch_query, config, variant_config) {
158
+ _assertClass(database, WasmMatcherDatabase);
159
+ const ptr0 = passArray8ToWasm0(query, wasm.__wbindgen_malloc);
160
+ const len0 = WASM_VECTOR_LEN;
161
+ const ret = wasm.wasmmatcherengine_bestMatchWithVariants(this.__wbg_ptr, database.__wbg_ptr, ptr0, len0, pitch_query, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(variant_config) ? 0 : addToExternrefTable0(variant_config));
162
+ if (ret[2]) {
163
+ throw takeFromExternrefTable0(ret[1]);
164
+ }
165
+ return takeFromExternrefTable0(ret[0]);
166
+ }
135
167
  /**
136
168
  * @param {WasmMatcherDatabase} database
137
169
  * @param {Uint8Array} query
@@ -148,6 +180,24 @@ export class WasmMatcherEngine {
148
180
  }
149
181
  return takeFromExternrefTable0(ret[0]);
150
182
  }
183
+ /**
184
+ * @param {WasmMatcherDatabase} database
185
+ * @param {Uint8Array} query
186
+ * @param {any} pitch_query
187
+ * @param {any | null} [config]
188
+ * @param {any | null} [variant_config]
189
+ * @returns {any}
190
+ */
191
+ matchQueryWithVariants(database, query, pitch_query, config, variant_config) {
192
+ _assertClass(database, WasmMatcherDatabase);
193
+ const ptr0 = passArray8ToWasm0(query, wasm.__wbindgen_malloc);
194
+ const len0 = WASM_VECTOR_LEN;
195
+ const ret = wasm.wasmmatcherengine_matchQueryWithVariants(this.__wbg_ptr, database.__wbg_ptr, ptr0, len0, pitch_query, isLikeNone(config) ? 0 : addToExternrefTable0(config), isLikeNone(variant_config) ? 0 : addToExternrefTable0(variant_config));
196
+ if (ret[2]) {
197
+ throw takeFromExternrefTable0(ret[1]);
198
+ }
199
+ return takeFromExternrefTable0(ret[0]);
200
+ }
151
201
  constructor() {
152
202
  const ret = wasm.wasmmatcherengine_new();
153
203
  this.__wbg_ptr = ret >>> 0;
Binary file
@@ -12,8 +12,11 @@ export const wasmmatcherdatabase_loadPackBytes: (a: number, b: number, c: number
12
12
  export const wasmmatcherdatabase_loadPackBytesAppend: (a: number, b: number, c: number) => [number, number];
13
13
  export const wasmmatcherdatabase_new: () => number;
14
14
  export const wasmmatcherdatabase_removeEntry: (a: number, b: number, c: number) => [number, number];
15
+ export const wasmmatcherdatabase_setEntryVariantMetadata: (a: number, b: number, c: number, d: number, e: number) => [number, number];
15
16
  export const wasmmatcherengine_bestMatch: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
17
+ export const wasmmatcherengine_bestMatchWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
16
18
  export const wasmmatcherengine_matchQuery: (a: number, b: number, c: number, d: number, e: number) => [number, number, number];
19
+ export const wasmmatcherengine_matchQueryWithVariants: (a: number, b: number, c: number, d: number, e: any, f: number, g: number) => [number, number, number];
17
20
  export const wasmmatcherengine_new: () => number;
18
21
  export const wasmmatcherengine_resetDistinctMatchState: (a: number) => void;
19
22
  export const init_matcher_wasm: () => void;
package/dist/index.d.ts CHANGED
@@ -8,6 +8,33 @@ interface MatchConfig {
8
8
  scoreThreshold?: number;
9
9
  gapThreshold?: number;
10
10
  }
11
+ interface VariantMatchQuery {
12
+ estimatedPitchRatio?: number | null;
13
+ pitchConfidence?: number | null;
14
+ }
15
+ interface VariantMatchConfig {
16
+ minConfidence?: number;
17
+ defaultToleranceRatio?: number;
18
+ minRatioMargin?: number;
19
+ candidateLimit?: number;
20
+ }
21
+ type VariantDecisionStatus = "variant" | "variant_unknown" | "no_variant";
22
+ interface VariantDecision {
23
+ status: VariantDecisionStatus;
24
+ reason?: string | null;
25
+ canonicalMaterialId?: string | null;
26
+ matchedEntryName?: string | null;
27
+ variantId?: string | null;
28
+ variantLabel?: string | null;
29
+ nominalRatio?: number | null;
30
+ estimatedPitchRatio?: number | null;
31
+ pitchConfidence?: number | null;
32
+ ratioError?: number | null;
33
+ pitchScore?: number | null;
34
+ toleranceMinRatio?: number | null;
35
+ toleranceMaxRatio?: number | null;
36
+ candidateCount: number;
37
+ }
11
38
  interface EntryMetadata {
12
39
  id?: string | null;
13
40
  name?: string | null;
@@ -22,6 +49,10 @@ interface MatchResult {
22
49
  position: number;
23
50
  afpType: AudioFingerprintType;
24
51
  }
52
+ interface VariantMatch {
53
+ matchResult: MatchResult;
54
+ variant: VariantDecision;
55
+ }
25
56
  type PackSource = Uint8Array | ArrayBuffer | Blob | Response | Request | string | URL;
26
57
  type MatcherSessionEventMap = {
27
58
  ready: {
@@ -134,11 +165,14 @@ declare class MatcherSession {
134
165
  loadPack(source: PackSource): Promise<void>;
135
166
  addEntry(name: string, afpType: AudioFingerprintType, fingerprint: Uint8Array): Promise<void>;
136
167
  addOrReplace(name: string, afpType: AudioFingerprintType, fingerprint: Uint8Array, metadata?: EntryMetadata | null): Promise<void>;
168
+ setEntryVariantMetadata(name: string, encodedMetadata: string): Promise<void>;
137
169
  removeEntry(name: string): Promise<void>;
138
170
  clear(): Promise<void>;
139
171
  getEntryMetadata(name: string): Promise<EntryMetadata | null>;
140
172
  match(query: Uint8Array, config?: MatchConfig): Promise<MatchResult[]>;
141
173
  bestMatch(query: Uint8Array, config?: MatchConfig): Promise<MatchResult | null>;
174
+ matchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch[]>;
175
+ bestMatchWithVariants(query: Uint8Array, pitchQuery: VariantMatchQuery, config?: MatchConfig, variantConfig?: VariantMatchConfig): Promise<VariantMatch | null>;
142
176
  destroy(): void;
143
177
  private bootstrap;
144
178
  private ensureLiveReady;
@@ -151,4 +185,4 @@ declare function __setMatcherWasmModuleForTests(moduleLike: unknown): void;
151
185
  declare function __setMatcherEmbeddedPackForTests(bytes: Uint8Array): void;
152
186
  declare function __resetMatcherWasmForTests(): void;
153
187
 
154
- export { AudioFingerprintType, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, type MatchConfig, type MatchResult, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
188
+ export { AudioFingerprintType, type EntryMetadata, type InitMatcherOptions, type InitMatcherResult, type MatchConfig, type MatchResult, type MatcherEphemeralAuthOptions, type MatcherEphemeralAuthResult, type MatcherEphemeralKeyExchangeMetadata, type MatcherEphemeralKeyExchangeOptions, MatcherSession, type MatcherSessionEventMap, type MatcherSessionEventName, type MatcherSessionListener, type MatcherSessionOptions, type PackSource, type VariantDecision, type VariantDecisionStatus, type VariantMatch, type VariantMatchConfig, type VariantMatchQuery, __resetMatcherWasmForTests, __setMatcherEmbeddedPackForTests, __setMatcherWasmModuleForTests, initMatcher };
package/dist/index.js CHANGED
@@ -358,6 +358,13 @@ var WrappedDatabase = class {
358
358
  }
359
359
  return normalizeEntryMetadata(raw);
360
360
  }
361
+ setEntryVariantMetadata(name, encodedMetadata) {
362
+ const fn = getCallable(this.inner, ["setEntryVariantMetadata", "set_entry_variant_metadata"]);
363
+ if (!fn) {
364
+ throw new Error("WASM database is missing setEntryVariantMetadata-compatible method");
365
+ }
366
+ fn.call(this.inner, name, encodedMetadata);
367
+ }
361
368
  destroy() {
362
369
  const fn = getCallable(this.inner, ["destroy", "free"]);
363
370
  if (fn) {
@@ -403,6 +410,31 @@ var WrappedMatcher = class {
403
410
  }
404
411
  return normalizeResult(raw);
405
412
  }
413
+ findMatchesWithVariants(database, query, pitchQuery, config, variantConfig) {
414
+ const fn = getCallable(this.inner, [
415
+ "findMatchesWithVariants",
416
+ "matchQueryWithVariants",
417
+ "match_query_with_variants"
418
+ ]);
419
+ if (!fn) {
420
+ throw new Error("WASM matcher is missing findMatchesWithVariants-compatible method");
421
+ }
422
+ const dbRecord = typeof database.raw === "function" ? database.raw() : database;
423
+ const rawResults = fn.call(this.inner, dbRecord, query, pitchQuery, config, variantConfig);
424
+ return normalizeVariantMatches(rawResults ?? []);
425
+ }
426
+ bestMatchWithVariants(database, query, pitchQuery, config, variantConfig) {
427
+ const fn = getCallable(this.inner, [
428
+ "bestMatchWithVariants",
429
+ "best_match_with_variants"
430
+ ]);
431
+ if (!fn) {
432
+ throw new Error("WASM matcher is missing bestMatchWithVariants-compatible method");
433
+ }
434
+ const dbRecord = typeof database.raw === "function" ? database.raw() : database;
435
+ const raw = fn.call(this.inner, dbRecord, query, pitchQuery, config, variantConfig);
436
+ return raw && typeof raw === "object" ? normalizeVariantMatch(raw) : null;
437
+ }
406
438
  resetDistinctMatchState() {
407
439
  const fn = getCallable(this.inner, ["resetDistinctMatchState", "reset_distinct_match_state"]);
408
440
  fn?.call(this.inner);
@@ -423,7 +455,35 @@ function normalizeResult(raw) {
423
455
  afpType
424
456
  };
425
457
  }
458
+ function normalizeVariantMatch(raw) {
459
+ const rawMatch = raw.matchResult ?? raw.match;
460
+ const rawVariant = raw.variant ?? {};
461
+ return {
462
+ matchResult: normalizeResult(rawMatch),
463
+ variant: {
464
+ ...rawVariant,
465
+ status: String(rawVariant.status),
466
+ candidateCount: Number(rawVariant.candidateCount ?? 0)
467
+ }
468
+ };
469
+ }
470
+ function isMapLike(raw) {
471
+ return raw instanceof Map || Object.prototype.toString.call(raw) === "[object Map]" && typeof raw.entries === "function";
472
+ }
473
+ function toEntryMetadataRecord(raw) {
474
+ if (isMapLike(raw)) {
475
+ const entries = [];
476
+ for (const [key, value] of raw.entries()) {
477
+ if (typeof key === "string") {
478
+ entries.push([key, value]);
479
+ }
480
+ }
481
+ return Object.fromEntries(entries);
482
+ }
483
+ return raw;
484
+ }
426
485
  function normalizeEntryMetadata(raw) {
486
+ const metadata = toEntryMetadataRecord(raw);
427
487
  const {
428
488
  _id: legacyId,
429
489
  id,
@@ -433,7 +493,7 @@ function normalizeEntryMetadata(raw) {
433
493
  name,
434
494
  image,
435
495
  ...rest
436
- } = raw;
496
+ } = metadata;
437
497
  return {
438
498
  ...rest,
439
499
  ...id !== void 0 || legacyId !== void 0 ? { id: String(id ?? legacyId) } : {},
@@ -446,6 +506,9 @@ function normalizeEntryMetadata(raw) {
446
506
  function normalizeResults(rawResults) {
447
507
  return rawResults.filter((item) => typeof item === "object" && item !== null).map((item) => normalizeResult(item));
448
508
  }
509
+ function normalizeVariantMatches(rawResults) {
510
+ return rawResults.filter((item) => typeof item === "object" && item !== null).map((item) => normalizeVariantMatch(item));
511
+ }
449
512
  function isInternallyDuplicatedBestMatch(result) {
450
513
  return Boolean(
451
514
  result && typeof result === "object" && result[INTERNAL_DUPLICATE_MATCH_FLAG] === true
@@ -603,6 +666,16 @@ var MatcherSession = class {
603
666
  handleError(this.events, "addOrReplace", error);
604
667
  }
605
668
  }
669
+ async setEntryVariantMetadata(name, encodedMetadata) {
670
+ try {
671
+ await this.ensureLiveReady();
672
+ this.databaseOrThrow().setEntryVariantMetadata(name, encodedMetadata);
673
+ this.distinctMatchDeduper.reset();
674
+ this.matcher?.resetDistinctMatchState?.();
675
+ } catch (error) {
676
+ handleError(this.events, "setEntryVariantMetadata", error);
677
+ }
678
+ }
606
679
  async removeEntry(name) {
607
680
  try {
608
681
  await this.ensureLiveReady();
@@ -658,6 +731,50 @@ var MatcherSession = class {
658
731
  handleError(this.events, "bestMatch", error);
659
732
  }
660
733
  }
734
+ async matchWithVariants(query, pitchQuery, config, variantConfig) {
735
+ try {
736
+ await this.ensureLiveReady();
737
+ const safeQuery = toUint8ArrayCopy(query);
738
+ this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
739
+ const results = this.matcherOrThrow().findMatchesWithVariants(
740
+ this.databaseOrThrow(),
741
+ safeQuery,
742
+ pitchQuery,
743
+ config,
744
+ variantConfig
745
+ );
746
+ this.emitDistinctMatchEvent(
747
+ results[0]?.matchResult ?? null,
748
+ safeQuery.byteLength,
749
+ results.map((result) => result.matchResult)
750
+ );
751
+ return results;
752
+ } catch (error) {
753
+ handleError(this.events, "matchWithVariants", error);
754
+ }
755
+ }
756
+ async bestMatchWithVariants(query, pitchQuery, config, variantConfig) {
757
+ try {
758
+ await this.ensureLiveReady();
759
+ const safeQuery = toUint8ArrayCopy(query);
760
+ this.events.emit("querystart", { queryByteLength: safeQuery.byteLength });
761
+ const best = this.matcherOrThrow().bestMatchWithVariants(
762
+ this.databaseOrThrow(),
763
+ safeQuery,
764
+ pitchQuery,
765
+ config,
766
+ variantConfig
767
+ );
768
+ this.emitDistinctMatchEvent(
769
+ best?.matchResult ?? null,
770
+ safeQuery.byteLength,
771
+ best ? [best.matchResult] : []
772
+ );
773
+ return best;
774
+ } catch (error) {
775
+ handleError(this.events, "bestMatchWithVariants", error);
776
+ }
777
+ }
661
778
  destroy() {
662
779
  if (this.destroyed) {
663
780
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sorisdk/matcher",
3
- "version": "0.1.4",
3
+ "version": "0.4.0",
4
4
  "description": "Browser WASM SDK for audio fingerprint matching",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",