web-sdk-pp-detection 0.1.1 → 0.3.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.
package/dist/index.d.ts CHANGED
@@ -141,6 +141,7 @@ interface PPDetectionProgressEvent {
141
141
  readonly fallback?: PPDetectionFallback;
142
142
  }
143
143
  interface CreatePPDetectionOptions {
144
+ readonly allowExperimental?: boolean;
144
145
  readonly allowFallback?: boolean;
145
146
  readonly backend?: BackendPreference;
146
147
  readonly cache?: boolean | "memory" | "indexeddb";
@@ -228,6 +229,12 @@ interface PPDetectionFallback {
228
229
  readonly variantId: string;
229
230
  }
230
231
  interface PPDetectionRuntimeInfo {
232
+ readonly runtimeVersion?: string | null;
233
+ readonly environment?: Readonly<{
234
+ userAgent: string | null;
235
+ platform: string | null;
236
+ capturedAt: string;
237
+ }>;
231
238
  readonly requestedBackend: BackendPreference;
232
239
  readonly backend: Backend;
233
240
  readonly precision: Precision;
@@ -252,6 +259,7 @@ interface PPDetectionModelSourceInfo {
252
259
  readonly sha256: string;
253
260
  }
254
261
  interface PPDetectionLoadTimings {
262
+ readonly modelSource?: "network" | "cache" | "memory";
255
263
  readonly modelDownloadMs?: number;
256
264
  readonly modelCacheReadMs?: number;
257
265
  readonly integrityMs?: number;
@@ -394,6 +402,7 @@ declare class PPDetectionError extends Error {
394
402
  }
395
403
 
396
404
  interface SelectExecutionOptions {
405
+ readonly allowExperimental?: boolean;
397
406
  readonly backend?: BackendPreference;
398
407
  readonly precision?: Precision;
399
408
  readonly executionMode?: ExecutionMode;
@@ -418,6 +427,9 @@ interface OrtInferenceSession {
418
427
  interface OrtModule {
419
428
  readonly env: {
420
429
  readonly wasm?: Record<string, unknown>;
430
+ readonly versions?: {
431
+ readonly web?: string;
432
+ };
421
433
  };
422
434
  readonly InferenceSession: {
423
435
  create(model: ArrayBuffer, options: Record<string, unknown>): Promise<OrtInferenceSession>;
@@ -425,6 +437,7 @@ interface OrtModule {
425
437
  readonly Tensor?: new (type: string, data: unknown, dims: readonly number[]) => unknown;
426
438
  }
427
439
  interface OrtSessionHandle {
440
+ readonly runtimeVersion?: string | null;
428
441
  readonly plan: ExecutionPlan;
429
442
  readonly sessionMs: number;
430
443
  run(feeds: Record<string, unknown>, options?: {
@@ -522,11 +535,16 @@ interface CacheEstimate {
522
535
  readonly entries: number;
523
536
  }
524
537
  interface ModelCache {
538
+ readonly scope?: object;
525
539
  get(key: string): Promise<ArrayBuffer | undefined>;
526
540
  put(key: string, bytes: ArrayBuffer): Promise<void>;
527
541
  clearCurrent(key: string): Promise<void>;
528
542
  clearAll(): Promise<void>;
529
543
  estimate(): Promise<CacheEstimate>;
544
+ list?(): Promise<readonly {
545
+ key: string;
546
+ bytes: number;
547
+ }[]>;
530
548
  close?(): Promise<void> | void;
531
549
  }
532
550
 
@@ -559,6 +577,7 @@ interface LoadedManagedModel {
559
577
  declare class ModelManager {
560
578
  private readonly fetcher?;
561
579
  private readonly cache;
580
+ private readonly coordinator;
562
581
  private readonly lifecycle;
563
582
  private readonly activeLoads;
564
583
  private currentKey?;
@@ -569,8 +588,8 @@ declare class ModelManager {
569
588
  load(options: LoadManagedModelOptions): Promise<LoadedManagedModel>;
570
589
  private loadActive;
571
590
  estimate(): Promise<CacheEstimate>;
572
- getCacheEstimate(): Promise<CacheEstimate>;
573
- clearCurrentModelCache(): Promise<void>;
591
+ getCacheEstimate(model?: ModelIdentity): Promise<CacheEstimate>;
592
+ clearCurrentModelCache(model?: ModelIdentity): Promise<void>;
574
593
  clearAllCache(): Promise<void>;
575
594
  dispose(): Promise<void>;
576
595
  }
@@ -583,6 +602,10 @@ declare class MemoryModelCache implements ModelCache {
583
602
  clearAll(): Promise<void>;
584
603
  estimate(): Promise<CacheEstimate>;
585
604
  close(): Promise<void>;
605
+ list(): Promise<readonly {
606
+ key: string;
607
+ bytes: number;
608
+ }[]>;
586
609
  }
587
610
 
588
611
  interface IndexedDBModelCacheOptions {
@@ -590,6 +613,7 @@ interface IndexedDBModelCacheOptions {
590
613
  readonly databaseName?: string;
591
614
  }
592
615
  declare class IndexedDBModelCache implements ModelCache {
616
+ readonly scope: object;
593
617
  private readonly factory;
594
618
  private readonly databaseName;
595
619
  private database?;
@@ -600,6 +624,10 @@ declare class IndexedDBModelCache implements ModelCache {
600
624
  clearAll(): Promise<void>;
601
625
  estimate(): Promise<CacheEstimate>;
602
626
  close(): Promise<void>;
627
+ list(): Promise<readonly {
628
+ key: string;
629
+ bytes: number;
630
+ }[]>;
603
631
  private open;
604
632
  private request;
605
633
  }
@@ -607,7 +635,7 @@ declare class IndexedDBModelCache implements ModelCache {
607
635
  declare global {
608
636
  var __PPDETECTION_SCRIPT_URL__: string | undefined;
609
637
  }
610
- declare const CURRENT_SDK_VERSION = "0.1.1";
638
+ declare const CURRENT_SDK_VERSION = "0.3.0";
611
639
 
612
640
  declare function probePPDetectionCapabilities(options?: CapabilityProbeOptions): DetectionCapabilities;
613
641
 
package/dist/index.js CHANGED
@@ -622,7 +622,11 @@ var PPDetectionDetectorImplementation = class {
622
622
  original: { width: decoded.width, height: decoded.height }
623
623
  },
624
624
  model: this.model,
625
- runtime: this.runtime,
625
+ runtime: {
626
+ ...this.runtime,
627
+ fallbacks: this.runtime.fallbacks.map((fallback) => ({ ...fallback })),
628
+ ...this.runtime.environment ? { environment: { ...this.runtime.environment } } : {}
629
+ },
626
630
  timings: {
627
631
  decodeMs: decoded.decodeMs,
628
632
  preprocessMs,
@@ -1115,8 +1119,84 @@ function adaptModelManifest(manifest) {
1115
1119
  });
1116
1120
  }
1117
1121
 
1122
+ // src/cache/coordination.ts
1123
+ var CacheCoordinator = class {
1124
+ caches = /* @__PURE__ */ new Map();
1125
+ generation = 0;
1126
+ keys = /* @__PURE__ */ new Map();
1127
+ pending = Promise.resolve();
1128
+ guard(key) {
1129
+ const generation = this.generation;
1130
+ const keyGeneration = this.keys.get(key) ?? 0;
1131
+ this.keys.set(key, keyGeneration);
1132
+ return () => generation === this.generation && keyGeneration === (this.keys.get(key) ?? 0);
1133
+ }
1134
+ run(operation) {
1135
+ const pending = this.pending.then(operation);
1136
+ this.pending = pending.catch(() => void 0);
1137
+ return pending;
1138
+ }
1139
+ clear(key, matches) {
1140
+ if (matches) {
1141
+ for (const [candidate, generation] of this.keys) {
1142
+ if (matches(candidate)) this.keys.set(candidate, generation + 1);
1143
+ }
1144
+ } else if (key === void 0) this.generation += 1;
1145
+ else this.keys.set(key, (this.keys.get(key) ?? 0) + 1);
1146
+ return this.run(async () => {
1147
+ const outcomes = await Promise.allSettled(
1148
+ [...this.caches.keys()].map(async (cache) => {
1149
+ if (matches && cache.list) {
1150
+ for (const entry of await cache.list()) {
1151
+ if (matches(entry.key)) await cache.clearCurrent(entry.key);
1152
+ }
1153
+ } else if (key === void 0) await cache.clearAll();
1154
+ else await cache.clearCurrent(key);
1155
+ })
1156
+ );
1157
+ const failure = outcomes.find((outcome) => outcome.status === "rejected");
1158
+ if (failure?.status === "rejected") throw failure.reason;
1159
+ });
1160
+ }
1161
+ unregister(cache) {
1162
+ const count = this.caches.get(cache) ?? 0;
1163
+ if (count > 1) {
1164
+ this.caches.set(cache, count - 1);
1165
+ return false;
1166
+ }
1167
+ this.caches.delete(cache);
1168
+ return true;
1169
+ }
1170
+ };
1171
+ var coordinators = /* @__PURE__ */ new WeakMap();
1172
+ function coordinateCache(cache) {
1173
+ const scope = cache.scope ?? cache;
1174
+ let coordinator = coordinators.get(scope);
1175
+ if (!coordinator) {
1176
+ coordinator = new CacheCoordinator();
1177
+ coordinators.set(scope, coordinator);
1178
+ }
1179
+ coordinator.caches.set(cache, (coordinator.caches.get(cache) ?? 0) + 1);
1180
+ return coordinator;
1181
+ }
1182
+ var databaseScopes = /* @__PURE__ */ new WeakMap();
1183
+ function databaseScope(factory, name) {
1184
+ let scopes = databaseScopes.get(factory);
1185
+ if (!scopes) {
1186
+ scopes = /* @__PURE__ */ new Map();
1187
+ databaseScopes.set(factory, scopes);
1188
+ }
1189
+ let scope = scopes.get(name);
1190
+ if (!scope) {
1191
+ scope = {};
1192
+ scopes.set(name, scope);
1193
+ }
1194
+ return scope;
1195
+ }
1196
+
1118
1197
  // src/cache/indexeddb-cache.ts
1119
1198
  var IndexedDBModelCache = class {
1199
+ scope;
1120
1200
  factory;
1121
1201
  databaseName;
1122
1202
  database;
@@ -1125,6 +1205,7 @@ var IndexedDBModelCache = class {
1125
1205
  if (!factory) throw new Error("\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 IndexedDB");
1126
1206
  this.factory = factory;
1127
1207
  this.databaseName = options.databaseName ?? "web-sdk-pp-detection-models-v1";
1208
+ this.scope = databaseScope(factory, this.databaseName);
1128
1209
  }
1129
1210
  async get(key) {
1130
1211
  const record3 = await this.request(
@@ -1178,6 +1259,24 @@ var IndexedDBModelCache = class {
1178
1259
  (await this.database).close();
1179
1260
  this.database = void 0;
1180
1261
  }
1262
+ async list() {
1263
+ const database = await this.open();
1264
+ return await new Promise((resolve, reject) => {
1265
+ const transaction = database.transaction("models", "readonly");
1266
+ const request = transaction.objectStore("models").openCursor();
1267
+ const entries = [];
1268
+ request.onerror = () => reject(request.error ?? new Error("\u8BFB\u53D6\u7F13\u5B58\u5BB9\u91CF\u5931\u8D25"));
1269
+ request.onsuccess = () => {
1270
+ const cursor = request.result;
1271
+ if (!cursor) return;
1272
+ const record3 = cursor.value;
1273
+ entries.push({ key: record3.key, bytes: record3.size });
1274
+ cursor.continue();
1275
+ };
1276
+ transaction.oncomplete = () => resolve(entries);
1277
+ transaction.onabort = () => reject(transaction.error ?? new Error("\u7F13\u5B58\u5BB9\u91CF\u4E8B\u52A1\u4E2D\u6B62"));
1278
+ });
1279
+ }
1181
1280
  open() {
1182
1281
  if (this.database) return this.database;
1183
1282
  this.database = new Promise((resolve, reject) => {
@@ -1247,6 +1346,11 @@ var MemoryModelCache = class {
1247
1346
  this.entries.clear();
1248
1347
  return Promise.resolve();
1249
1348
  }
1349
+ list() {
1350
+ return Promise.resolve(
1351
+ [...this.entries].map(([key, bytes]) => ({ key, bytes: bytes.byteLength }))
1352
+ );
1353
+ }
1250
1354
  };
1251
1355
 
1252
1356
  // src/cache/model-cache.ts
@@ -1254,9 +1358,11 @@ var TieredModelCache = class {
1254
1358
  constructor(memory, persistent) {
1255
1359
  this.memory = memory;
1256
1360
  this.persistent = persistent;
1361
+ this.scope = persistent?.scope ?? persistent ?? memory;
1257
1362
  }
1258
1363
  memory;
1259
1364
  persistent;
1365
+ scope;
1260
1366
  async get(key) {
1261
1367
  const memoryValue = await this.memory.get(key);
1262
1368
  if (memoryValue) return memoryValue;
@@ -1279,6 +1385,13 @@ var TieredModelCache = class {
1279
1385
  estimate() {
1280
1386
  return this.persistent?.estimate() ?? this.memory.estimate();
1281
1387
  }
1388
+ async list() {
1389
+ const entries = /* @__PURE__ */ new Map();
1390
+ for (const cache of [this.memory, this.persistent]) {
1391
+ for (const entry of await cache?.list?.() ?? []) entries.set(entry.key, entry);
1392
+ }
1393
+ return [...entries.values()];
1394
+ }
1282
1395
  async close() {
1283
1396
  await this.memory.close?.();
1284
1397
  await this.persistent?.close?.();
@@ -1488,6 +1601,14 @@ function resolveModelAsset(selection, manifest) {
1488
1601
 
1489
1602
  // src/model/model-manager.ts
1490
1603
  var SDK_CACHE_NAMESPACE = "web-sdk-pp-detection:cache-v1";
1604
+ function matchesModel(key, model) {
1605
+ try {
1606
+ const parts = JSON.parse(key);
1607
+ return Array.isArray(parts) && parts[0] === SDK_CACHE_NAMESPACE && (model === void 0 || parts[1] === model.id && parts[2] === model.version);
1608
+ } catch {
1609
+ return false;
1610
+ }
1611
+ }
1491
1612
  function now2() {
1492
1613
  return globalThis.performance?.now() ?? Date.now();
1493
1614
  }
@@ -1511,6 +1632,7 @@ function throwIfAborted4(signal) {
1511
1632
  var ModelManager = class {
1512
1633
  fetcher;
1513
1634
  cache;
1635
+ coordinator;
1514
1636
  lifecycle = new AbortController();
1515
1637
  activeLoads = /* @__PURE__ */ new Set();
1516
1638
  currentKey;
@@ -1519,6 +1641,7 @@ var ModelManager = class {
1519
1641
  constructor(options = {}) {
1520
1642
  this.fetcher = options.fetcher;
1521
1643
  this.cache = createCache(options.cache);
1644
+ this.coordinator = coordinateCache(this.cache);
1522
1645
  }
1523
1646
  cacheKey(variant3, source2, model = { id: "unknown", version: "unknown" }) {
1524
1647
  return JSON.stringify([
@@ -1557,16 +1680,24 @@ var ModelManager = class {
1557
1680
  const variant3 = resolveModelVariant(manifest, options.variantId);
1558
1681
  const sourceKind2 = options.sourceKind ?? "auto";
1559
1682
  const sources = resolveModelSources(variant3, sourceKind2);
1683
+ const guards = new Map(
1684
+ sources.map((source2) => {
1685
+ const key = this.cacheKey(variant3, source2, manifest.model);
1686
+ return [key, this.coordinator.guard(key)];
1687
+ })
1688
+ );
1560
1689
  const failures = [];
1561
1690
  let lastError;
1562
1691
  for (const source2 of sources) {
1563
1692
  throwIfAborted4(options.signal);
1564
1693
  const asset = { model: manifest.model, variant: variant3, source: source2 };
1565
1694
  const cacheKey = this.cacheKey(variant3, source2, manifest.model);
1695
+ this.currentKey = cacheKey;
1696
+ const canMutate = guards.get(cacheKey);
1566
1697
  const cacheStarted = now2();
1567
1698
  let cached;
1568
1699
  try {
1569
- cached = await this.cache.get(cacheKey);
1700
+ cached = await this.coordinator.run(() => this.cache.get(cacheKey));
1570
1701
  } catch (error) {
1571
1702
  if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
1572
1703
  throwIfAborted4(options.signal);
@@ -1577,7 +1708,6 @@ var ModelManager = class {
1577
1708
  const integrityStarted = now2();
1578
1709
  try {
1579
1710
  await verifyModelIntegrity(cached, source2, options.signal);
1580
- this.currentKey = cacheKey;
1581
1711
  return {
1582
1712
  bytes: cached,
1583
1713
  manifest,
@@ -1591,7 +1721,9 @@ var ModelManager = class {
1591
1721
  } catch (error) {
1592
1722
  if (error instanceof PPDetectionError && error.code === "ABORTED") throw error;
1593
1723
  try {
1594
- await this.cache.clearCurrent(cacheKey);
1724
+ await this.coordinator.run(async () => {
1725
+ if (canMutate()) await this.cache.clearCurrent(cacheKey);
1726
+ });
1595
1727
  } catch (clearError) {
1596
1728
  if (clearError instanceof PPDetectionError && clearError.code === "ABORTED")
1597
1729
  throw clearError;
@@ -1627,11 +1759,12 @@ var ModelManager = class {
1627
1759
  }
1628
1760
  throwIfAborted4(options.signal);
1629
1761
  try {
1630
- await this.cache.put(cacheKey, loaded.bytes);
1762
+ await this.coordinator.run(async () => {
1763
+ if (canMutate()) await this.cache.put(cacheKey, loaded.bytes);
1764
+ });
1631
1765
  } catch {
1632
1766
  }
1633
1767
  throwIfAborted4(options.signal);
1634
- this.currentKey = cacheKey;
1635
1768
  return {
1636
1769
  bytes: loaded.bytes,
1637
1770
  manifest,
@@ -1656,16 +1789,44 @@ var ModelManager = class {
1656
1789
  estimate() {
1657
1790
  return this.getCacheEstimate();
1658
1791
  }
1659
- getCacheEstimate() {
1660
- return this.cache.estimate();
1792
+ getCacheEstimate(model) {
1793
+ return this.coordinator.run(async () => {
1794
+ if (!this.cache.list) {
1795
+ if (model)
1796
+ throw new PPDetectionError(
1797
+ "CAPABILITY_UNSUPPORTED",
1798
+ "\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u4F30\u7B97"
1799
+ );
1800
+ return this.cache.estimate();
1801
+ }
1802
+ const unique = /* @__PURE__ */ new Map();
1803
+ for (const cache of this.coordinator.caches.keys()) {
1804
+ for (const entry of await cache.list?.() ?? []) unique.set(entry.key, entry);
1805
+ }
1806
+ const entries = [...unique.values()].filter((entry) => matchesModel(entry.key, model));
1807
+ return {
1808
+ bytes: entries.reduce((sum, entry) => sum + entry.bytes, 0),
1809
+ entries: entries.length
1810
+ };
1811
+ });
1661
1812
  }
1662
- async clearCurrentModelCache() {
1813
+ async clearCurrentModelCache(model) {
1814
+ if (model) {
1815
+ if (!model.id || !model.version)
1816
+ throw new PPDetectionError("INVALID_MANIFEST", "\u7F13\u5B58\u6A21\u578B\u8EAB\u4EFD\u5FC5\u987B\u5305\u542B id \u548C version");
1817
+ if (!this.cache.list)
1818
+ throw new PPDetectionError(
1819
+ "CAPABILITY_UNSUPPORTED",
1820
+ "\u81EA\u5B9A\u4E49\u7F13\u5B58\u9700\u5B9E\u73B0 list \u624D\u80FD\u6309\u6A21\u578B\u6E05\u7406"
1821
+ );
1822
+ await this.coordinator.clear(void 0, (key) => matchesModel(key, model));
1823
+ return;
1824
+ }
1663
1825
  if (!this.currentKey) return;
1664
- await this.cache.clearCurrent(this.currentKey);
1826
+ await this.coordinator.clear(this.currentKey);
1665
1827
  }
1666
1828
  async clearAllCache() {
1667
- await this.cache.clearAll();
1668
- this.currentKey = void 0;
1829
+ await this.coordinator.clear(void 0, (key) => matchesModel(key));
1669
1830
  }
1670
1831
  async dispose() {
1671
1832
  if (this.disposePromise) return await this.disposePromise;
@@ -1674,7 +1835,7 @@ var ModelManager = class {
1674
1835
  this.disposePromise = (async () => {
1675
1836
  await Promise.all([...this.activeLoads]);
1676
1837
  this.currentKey = void 0;
1677
- await this.cache.close?.();
1838
+ if (this.coordinator.unregister(this.cache)) await this.cache.close?.();
1678
1839
  })();
1679
1840
  await this.disposePromise;
1680
1841
  }
@@ -1688,7 +1849,7 @@ function mapError(error, phase) {
1688
1849
  if (error instanceof PPDetectionError) return error;
1689
1850
  const message = error instanceof Error ? error.message : String(error);
1690
1851
  const details = { phase, causeMessage: message };
1691
- if (/abort|cancel/i.test(message))
1852
+ if (error instanceof Error && error.name === "AbortError")
1692
1853
  return new PPDetectionError("ABORTED", "\u63A8\u7406\u5DF2\u53D6\u6D88", details, { cause: error });
1693
1854
  if (/memory|out.of.memory|allocation/i.test(message))
1694
1855
  return new PPDetectionError("OUT_OF_MEMORY", "\u8FD0\u884C\u65F6\u5185\u5B58\u4E0D\u8DB3", details, { cause: error });
@@ -1733,6 +1894,7 @@ async function createOrtSession(modelBytes, plan, options = {}) {
1733
1894
  return {
1734
1895
  plan,
1735
1896
  sessionMs,
1897
+ runtimeVersion: ort.env.versions?.web ?? null,
1736
1898
  async run(feeds, runOptions = {}) {
1737
1899
  if (disposed) throw new PPDetectionError("DISPOSED", "\u4F1A\u8BDD\u5DF2\u91CA\u653E", { phase: "run" });
1738
1900
  if (runOptions.signal?.aborted)
@@ -1869,13 +2031,19 @@ function selectExecutionPlan(options, capabilities, manifest) {
1869
2031
  if (executionMode === "worker" && !capabilities.worker) {
1870
2032
  fail("CAPABILITY_UNSUPPORTED", "\u8BF7\u6C42\u7684 worker \u4E0D\u53EF\u7528", { executionMode });
1871
2033
  }
1872
- const variant3 = manifest.variants?.find(
1873
- (candidate) => candidate.precision === requestedPrecision && candidate.status !== "labs" && candidate.status !== "blocked"
1874
- );
2034
+ const matchingVariants = manifest.variants?.filter(
2035
+ (candidate) => candidate.precision === requestedPrecision && candidate.status !== "blocked"
2036
+ ) ?? [];
2037
+ const variant3 = matchingVariants.find((candidate) => candidate.status !== "labs") ?? (options.allowExperimental === true ? matchingVariants.find((candidate) => candidate.status === "labs") : void 0);
1875
2038
  if (!variant3) {
1876
- fail("MODEL_INCOMPATIBLE", `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u7A33\u5B9A\u53D8\u4F53`, {
1877
- requestedPrecision
1878
- });
2039
+ fail(
2040
+ "MODEL_INCOMPATIBLE",
2041
+ options.allowExperimental === true ? `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u53D8\u4F53` : `manifest \u6CA1\u6709\u53EF\u7528\u7684 ${requestedPrecision} \u7A33\u5B9A\u53D8\u4F53`,
2042
+ {
2043
+ requestedPrecision,
2044
+ allowExperimental: options.allowExperimental === true
2045
+ }
2046
+ );
1879
2047
  }
1880
2048
  const candidates = candidatesForBackend(requestedBackend, capabilities).filter(
1881
2049
  (backend) => variant3.backends.includes(backend)
@@ -2051,7 +2219,7 @@ var WorkerBridge = class {
2051
2219
  };
2052
2220
 
2053
2221
  // src/index.ts
2054
- var CURRENT_SDK_VERSION = "0.1.1";
2222
+ var CURRENT_SDK_VERSION = "0.3.0";
2055
2223
  function probePPDetectionCapabilities(options = {}) {
2056
2224
  return probeCapabilities(options);
2057
2225
  }
@@ -2146,6 +2314,7 @@ function workerUrl() {
2146
2314
  return new URL("./inference.worker.js", import.meta.url);
2147
2315
  }
2148
2316
  async function createPPDetection(options = {}) {
2317
+ const loadStartedAt = now4();
2149
2318
  if (options.model === void 0 && options.manifest === void 0)
2150
2319
  throw new PPDetectionError("INVALID_MANIFEST", "\u521B\u5EFA PPDetection \u5B9E\u4F8B\u9700\u8981 manifest \u6216 model");
2151
2320
  const capabilities = probeCapabilities();
@@ -2165,6 +2334,7 @@ async function createPPDetection(options = {}) {
2165
2334
  options.onProgress?.({ phase: "manifest", status: "complete" });
2166
2335
  const plan = selectExecutionPlan(
2167
2336
  {
2337
+ allowExperimental: options.allowExperimental,
2168
2338
  backend: options.backend,
2169
2339
  precision: options.precision === "auto" ? void 0 : options.precision,
2170
2340
  executionMode: options.executionMode,
@@ -2178,7 +2348,6 @@ async function createPPDetection(options = {}) {
2178
2348
  });
2179
2349
  let executor;
2180
2350
  try {
2181
- const loadStartedAt = now4();
2182
2351
  options.onProgress?.({ phase: "model", status: "start" });
2183
2352
  let modelBytes;
2184
2353
  let actualSource;
@@ -2190,10 +2359,17 @@ async function createPPDetection(options = {}) {
2190
2359
  throw new PPDetectionError("MODEL_SOURCE_UNAVAILABLE", "\u6A21\u578B\u53D8\u4F53\u6CA1\u6709\u53EF\u7528\u6765\u6E90", {
2191
2360
  variantId: variant3.id
2192
2361
  });
2362
+ const integrityStartedAt = now4();
2193
2363
  await verifyModelIntegrity(memoryData, source2, options.signal);
2364
+ const integrityMs = now4() - integrityStartedAt;
2194
2365
  modelBytes = memoryData;
2195
2366
  actualSource = source2;
2196
- loadTimings = { sessionMs: 0, totalMs: now4() - loadStartedAt, integrityMs: 0 };
2367
+ loadTimings = {
2368
+ sessionMs: 0,
2369
+ totalMs: now4() - loadStartedAt,
2370
+ integrityMs,
2371
+ modelSource: "memory"
2372
+ };
2197
2373
  } else {
2198
2374
  const loaded = await modelManager.load({
2199
2375
  manifest: runtimeManifest,
@@ -2205,20 +2381,26 @@ async function createPPDetection(options = {}) {
2205
2381
  modelBytes = loaded.bytes;
2206
2382
  variant3 = loaded.variant;
2207
2383
  actualSource = loaded.source;
2208
- loadTimings = { ...loaded.timings, sessionMs: 0, totalMs: now4() - loadStartedAt };
2384
+ loadTimings = {
2385
+ ...loaded.timings,
2386
+ sessionMs: 0,
2387
+ totalMs: now4() - loadStartedAt,
2388
+ modelSource: loaded.fromCache ? "cache" : "network"
2389
+ };
2209
2390
  }
2210
2391
  options.onProgress?.({ phase: "model", status: "complete" });
2211
2392
  const fallbacks = [];
2212
2393
  const createExecutorForPlan = async (candidatePlan) => {
2213
2394
  options.onProgress?.({ phase: "session", status: "start" });
2214
2395
  let bridge;
2396
+ const sessionStartedAt = now4();
2215
2397
  try {
2216
2398
  if (candidatePlan.executionMode === "worker") {
2217
2399
  if (typeof Worker !== "function")
2218
2400
  throw new PPDetectionError("CAPABILITY_UNSUPPORTED", "\u5F53\u524D\u73AF\u5883\u4E0D\u652F\u6301 Worker");
2219
2401
  const worker = new Worker(workerUrl(), { type: "module" });
2220
2402
  bridge = new WorkerBridge(worker);
2221
- await bridge.load(modelBytes.slice(0), candidatePlan, {
2403
+ const metadata = await bridge.load(modelBytes.slice(0), candidatePlan, {
2222
2404
  onProgress: (event) => options.onProgress?.({
2223
2405
  phase: "session",
2224
2406
  status: event.status
@@ -2228,6 +2410,7 @@ async function createPPDetection(options = {}) {
2228
2410
  numThreads: options.ort?.wasm?.numThreads
2229
2411
  }
2230
2412
  });
2413
+ runtimeVersion = typeof metadata === "object" && metadata !== null && "runtimeVersion" in metadata && typeof metadata.runtimeVersion === "string" ? metadata.runtimeVersion : null;
2231
2414
  const activeBridge = bridge;
2232
2415
  options.onProgress?.({ phase: "session", status: "complete" });
2233
2416
  return {
@@ -2245,7 +2428,7 @@ async function createPPDetection(options = {}) {
2245
2428
  wasmPaths: options.ort?.wasm?.paths,
2246
2429
  numThreads: options.ort?.wasm?.numThreads
2247
2430
  });
2248
- sessionMs = session.sessionMs;
2431
+ runtimeVersion = session.runtimeVersion ?? null;
2249
2432
  options.onProgress?.({ phase: "session", status: "complete" });
2250
2433
  return {
2251
2434
  run(input, signal) {
@@ -2269,10 +2452,13 @@ async function createPPDetection(options = {}) {
2269
2452
  { phase: "create", causeMessage: message },
2270
2453
  { cause: error }
2271
2454
  );
2455
+ } finally {
2456
+ sessionMs += now4() - sessionStartedAt;
2272
2457
  }
2273
2458
  };
2274
2459
  let selectedPlan = plan;
2275
2460
  let sessionMs = 0;
2461
+ let runtimeVersion = null;
2276
2462
  let selectedCandidateIndex = -1;
2277
2463
  for (const [candidateIndex, candidate] of plan.candidates.entries()) {
2278
2464
  const candidatePlan = {
@@ -2314,6 +2500,12 @@ async function createPPDetection(options = {}) {
2314
2500
  }
2315
2501
  if (!executor) throw new PPDetectionError("SESSION_CREATE_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u68C0\u6D4B Session");
2316
2502
  const runtime = {
2503
+ runtimeVersion,
2504
+ environment: {
2505
+ userAgent: globalThis.navigator?.userAgent ?? null,
2506
+ platform: globalThis.navigator?.platform ?? null,
2507
+ capturedAt: (/* @__PURE__ */ new Date()).toISOString()
2508
+ },
2317
2509
  requestedBackend: options.backend ?? "auto",
2318
2510
  backend: selectedPlan.actualBackend,
2319
2511
  precision: selectedPlan.actualPrecision,
@@ -2324,8 +2516,9 @@ async function createPPDetection(options = {}) {
2324
2516
  let activeExecutor = executor;
2325
2517
  const fallbackExecutor = {
2326
2518
  async run(input, signal) {
2519
+ const attemptInput = runtime.mode === "worker" && options.allowFallback === true && selectedCandidateIndex < plan.candidates.length - 1 ? { ...input, data: input.data.slice() } : input;
2327
2520
  try {
2328
- return await activeExecutor.run(input, signal);
2521
+ return await activeExecutor.run(attemptInput, signal);
2329
2522
  } catch (error) {
2330
2523
  if (options.allowFallback !== true || selectedCandidateIndex < 0 || selectedCandidateIndex >= plan.candidates.length - 1) {
2331
2524
  throw error;
@@ -2350,6 +2543,7 @@ async function createPPDetection(options = {}) {
2350
2543
  runtime.backend = nextPlan.actualBackend;
2351
2544
  runtime.precision = nextPlan.actualPrecision;
2352
2545
  runtime.mode = nextPlan.executionMode;
2546
+ runtime.runtimeVersion = runtimeVersion;
2353
2547
  const fallback = {
2354
2548
  cause: mapped.cause ?? mapped,
2355
2549
  code: mapped.code,
@@ -2381,6 +2575,7 @@ async function createPPDetection(options = {}) {
2381
2575
  disposeResources: () => modelManager.dispose()
2382
2576
  });
2383
2577
  await detector.load({ signal: options.signal });
2578
+ loadTimings.totalMs = now4() - loadStartedAt;
2384
2579
  options.onProgress?.({ phase: "ready", status: "complete" });
2385
2580
  return detector;
2386
2581
  } catch (error) {
@@ -2397,8 +2592,11 @@ async function createPPDetection(options = {}) {
2397
2592
  }
2398
2593
  async function clearModelCache() {
2399
2594
  const manager = new ModelManager();
2400
- await manager.clearAllCache();
2401
- await manager.dispose();
2595
+ try {
2596
+ await manager.clearAllCache();
2597
+ } finally {
2598
+ await manager.dispose();
2599
+ }
2402
2600
  }
2403
2601
 
2404
2602
  export { CURRENT_SDK_VERSION, IndexedDBModelCache, MemoryModelCache, ModelManager, PPDetectionError, adaptModelManifest, clearModelCache, createOrtSession, createPPDetection, loadModelAsset, parseDetectionManifest, parseModelManifest, probeCapabilities, probePPDetectionCapabilities, resolveModelAsset, selectExecutionPlan };