@dejadex/web 0.0.0-stage → 0.1.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.
Files changed (113) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +222 -2
  3. package/dist/errors.d.ts +8 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/errors.js +9 -0
  6. package/dist/errors.js.map +1 -0
  7. package/dist/geometry/homography.d.ts +75 -0
  8. package/dist/geometry/homography.d.ts.map +1 -0
  9. package/dist/geometry/homography.js +146 -0
  10. package/dist/geometry/homography.js.map +1 -0
  11. package/dist/identify/assets.d.ts +39 -0
  12. package/dist/identify/assets.d.ts.map +1 -0
  13. package/dist/identify/assets.js +167 -0
  14. package/dist/identify/assets.js.map +1 -0
  15. package/dist/identify/decode.d.ts +6 -0
  16. package/dist/identify/decode.d.ts.map +1 -0
  17. package/dist/identify/decode.js +53 -0
  18. package/dist/identify/decode.js.map +1 -0
  19. package/dist/identify/engine.d.ts +32 -0
  20. package/dist/identify/engine.d.ts.map +1 -0
  21. package/dist/identify/engine.js +127 -0
  22. package/dist/identify/engine.js.map +1 -0
  23. package/dist/identify/identifier.d.ts +91 -0
  24. package/dist/identify/identifier.d.ts.map +1 -0
  25. package/dist/identify/identifier.js +332 -0
  26. package/dist/identify/identifier.js.map +1 -0
  27. package/dist/identify/manifest.d.ts +60 -0
  28. package/dist/identify/manifest.d.ts.map +1 -0
  29. package/dist/identify/manifest.js +211 -0
  30. package/dist/identify/manifest.js.map +1 -0
  31. package/dist/identify/preprocess.d.ts +52 -0
  32. package/dist/identify/preprocess.d.ts.map +1 -0
  33. package/dist/identify/preprocess.js +195 -0
  34. package/dist/identify/preprocess.js.map +1 -0
  35. package/dist/identify/router.d.ts +60 -0
  36. package/dist/identify/router.d.ts.map +1 -0
  37. package/dist/identify/router.js +83 -0
  38. package/dist/identify/router.js.map +1 -0
  39. package/dist/identify/runtime.d.ts +69 -0
  40. package/dist/identify/runtime.d.ts.map +1 -0
  41. package/dist/identify/runtime.js +140 -0
  42. package/dist/identify/runtime.js.map +1 -0
  43. package/dist/identify/search.d.ts +35 -0
  44. package/dist/identify/search.d.ts.map +1 -0
  45. package/dist/identify/search.js +99 -0
  46. package/dist/identify/search.js.map +1 -0
  47. package/dist/identify/server.d.ts +60 -0
  48. package/dist/identify/server.d.ts.map +1 -0
  49. package/dist/identify/server.js +149 -0
  50. package/dist/identify/server.js.map +1 -0
  51. package/dist/identify/shard.d.ts +31 -0
  52. package/dist/identify/shard.d.ts.map +1 -0
  53. package/dist/identify/shard.js +146 -0
  54. package/dist/identify/shard.js.map +1 -0
  55. package/dist/identify/types.d.ts +107 -0
  56. package/dist/identify/types.d.ts.map +1 -0
  57. package/dist/identify/types.js +7 -0
  58. package/dist/identify/types.js.map +1 -0
  59. package/dist/identify/worker.d.ts +2 -0
  60. package/dist/identify/worker.d.ts.map +1 -0
  61. package/dist/identify/worker.js +13 -0
  62. package/dist/identify/worker.js.map +1 -0
  63. package/dist/identify/workerProtocol.d.ts +54 -0
  64. package/dist/identify/workerProtocol.d.ts.map +1 -0
  65. package/dist/identify/workerProtocol.js +113 -0
  66. package/dist/identify/workerProtocol.js.map +1 -0
  67. package/dist/index.d.ts +20 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +16 -0
  70. package/dist/index.js.map +1 -0
  71. package/dist/lease/verify.d.ts +69 -0
  72. package/dist/lease/verify.d.ts.map +1 -0
  73. package/dist/lease/verify.js +235 -0
  74. package/dist/lease/verify.js.map +1 -0
  75. package/dist/scanner.d.ts +10 -0
  76. package/dist/scanner.d.ts.map +1 -0
  77. package/dist/scanner.js +58 -0
  78. package/dist/scanner.js.map +1 -0
  79. package/dist/types.d.ts +180 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +2 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/usage/meter.d.ts +83 -0
  84. package/dist/usage/meter.d.ts.map +1 -0
  85. package/dist/usage/meter.js +111 -0
  86. package/dist/usage/meter.js.map +1 -0
  87. package/dist/usage/store.d.ts +40 -0
  88. package/dist/usage/store.d.ts.map +1 -0
  89. package/dist/usage/store.js +248 -0
  90. package/dist/usage/store.js.map +1 -0
  91. package/package.json +64 -4
  92. package/src/errors.ts +24 -0
  93. package/src/geometry/homography.ts +178 -0
  94. package/src/identify/assets.ts +189 -0
  95. package/src/identify/decode.ts +54 -0
  96. package/src/identify/engine.ts +167 -0
  97. package/src/identify/identifier.ts +431 -0
  98. package/src/identify/manifest.ts +240 -0
  99. package/src/identify/preprocess.ts +227 -0
  100. package/src/identify/router.ts +115 -0
  101. package/src/identify/runtime.ts +197 -0
  102. package/src/identify/search.ts +115 -0
  103. package/src/identify/server.ts +167 -0
  104. package/src/identify/shard.ts +154 -0
  105. package/src/identify/types.ts +122 -0
  106. package/src/identify/worker.ts +13 -0
  107. package/src/identify/workerProtocol.ts +136 -0
  108. package/src/index.ts +71 -0
  109. package/src/lease/verify.ts +252 -0
  110. package/src/scanner.ts +56 -0
  111. package/src/types.ts +196 -0
  112. package/src/usage/meter.ts +165 -0
  113. package/src/usage/store.ts +282 -0
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Downloads of the manifest, the model and the shard: time-bounded, checked
3
+ * against the manifest's SHA-256 and size, and kept in the Cache API under
4
+ * their digest (content-addressed). A new manifest version downloads only the
5
+ * files whose digest changed; a corrupted cache entry is noticed on read,
6
+ * dropped and fetched again.
7
+ *
8
+ * The last manifest that loaded is cached too, so the device engine starts
9
+ * offline from a complete cache.
10
+ */
11
+ import { ScannerError } from '../errors.js';
12
+ /** The Cache API cache the SDK owns. Bump the suffix if the key scheme changes. */
13
+ export const CACHE_NAME = 'dejadex-assets-v1';
14
+ /** Keys are synthetic URLs; nothing is ever fetched from this origin. */
15
+ const KEY_ORIGIN = 'https://cache.dejadex.invalid';
16
+ /** Smallest download speed assumed when bounding a file's download (bytes/s). */
17
+ const MIN_BYTES_PER_SECOND = 100_000;
18
+ const MIN_FILE_TIMEOUT_MS = 30_000;
19
+ export const MANIFEST_TIMEOUT_MS = 10_000;
20
+ export async function sha256Hex(data) {
21
+ const digest = new Uint8Array(await crypto.subtle.digest('SHA-256', data));
22
+ let out = '';
23
+ for (const b of digest)
24
+ out += b.toString(16).padStart(2, '0');
25
+ return out;
26
+ }
27
+ const fileKey = (sha256) => `${KEY_ORIGIN}/sha256/${sha256}`;
28
+ const manifestKey = (url) => {
29
+ // A signed URL changes its query every time; the manifest it names does not.
30
+ const u = new URL(url);
31
+ return `${KEY_ORIGIN}/manifest/${encodeURIComponent(u.origin + u.pathname)}`;
32
+ };
33
+ /** `AbortSignal.any` where the runtime has it, else a controller following both. */
34
+ export function anySignal(signals) {
35
+ const list = signals.filter((s) => s !== undefined);
36
+ if (list.length === 1)
37
+ return list[0];
38
+ if (typeof AbortSignal.any === 'function')
39
+ return AbortSignal.any(list);
40
+ const controller = new AbortController();
41
+ for (const s of list) {
42
+ if (s.aborted) {
43
+ controller.abort(s.reason);
44
+ break;
45
+ }
46
+ s.addEventListener('abort', () => controller.abort(s.reason), { once: true });
47
+ }
48
+ return controller.signal;
49
+ }
50
+ const errorText = (err) => (err instanceof Error ? err.message || err.name : String(err));
51
+ export function createAssetStore(options) {
52
+ const { fetch: doFetch, cache } = options;
53
+ async function verified(buffer, file) {
54
+ return buffer.byteLength === file.size && (await sha256Hex(buffer)) === file.sha256;
55
+ }
56
+ async function download(file, signal) {
57
+ const timeoutMs = options.fileTimeoutMs ?? Math.max(MIN_FILE_TIMEOUT_MS, Math.ceil((file.size / MIN_BYTES_PER_SECOND) * 1000));
58
+ const bound = anySignal([AbortSignal.timeout(timeoutMs), signal]);
59
+ try {
60
+ // The body is read inside this try, so the bound covers it too.
61
+ const res = await doFetch(file.url, { signal: bound, cache: 'no-store' });
62
+ if (!res.ok)
63
+ throw new ScannerError('NETWORK', `${file.url} answered ${res.status}`);
64
+ return await res.arrayBuffer();
65
+ }
66
+ catch (err) {
67
+ if (err instanceof ScannerError)
68
+ throw err;
69
+ throw new ScannerError('NETWORK', `download of ${file.url} failed: ${errorText(err)}`, { cause: err });
70
+ }
71
+ }
72
+ return {
73
+ async get(file, signal) {
74
+ const key = fileKey(file.sha256);
75
+ if (cache) {
76
+ const hit = await cache.match(key).catch(() => undefined);
77
+ if (hit) {
78
+ const buffer = await hit.arrayBuffer();
79
+ if (await verified(buffer, file))
80
+ return buffer;
81
+ await cache.delete(key).catch(() => false);
82
+ }
83
+ }
84
+ // One retry: a truncated or corrupted transfer gets a second chance.
85
+ for (let attempt = 1;; attempt++) {
86
+ const buffer = await download(file, signal);
87
+ if (await verified(buffer, file)) {
88
+ if (cache) {
89
+ await cache
90
+ .put(key, new Response(buffer.slice(0), { headers: { 'Content-Type': 'application/octet-stream' } }))
91
+ .catch(() => undefined); // a full disk only costs the next start a download
92
+ }
93
+ return buffer;
94
+ }
95
+ if (attempt >= 2) {
96
+ throw new ScannerError('MODEL_INTEGRITY', `${file.url} does not match its manifest checksum or size`);
97
+ }
98
+ }
99
+ },
100
+ async manifest(url, signal) {
101
+ const bound = anySignal([AbortSignal.timeout(MANIFEST_TIMEOUT_MS), signal]);
102
+ let networkError;
103
+ try {
104
+ const res = await doFetch(url, { signal: bound, cache: 'no-store' });
105
+ if (!res.ok)
106
+ throw new ScannerError('NETWORK', `manifest ${url} answered ${res.status}`);
107
+ const text = await res.text();
108
+ let doc;
109
+ try {
110
+ doc = JSON.parse(text);
111
+ }
112
+ catch {
113
+ throw new ScannerError('MODEL_INTEGRITY', `manifest ${url} is not JSON`);
114
+ }
115
+ if (cache) {
116
+ await cache.put(manifestKey(url), new Response(text, { headers: { 'Content-Type': 'application/json' } })).catch(() => undefined);
117
+ }
118
+ return doc;
119
+ }
120
+ catch (err) {
121
+ if (err instanceof ScannerError && err.code === 'MODEL_INTEGRITY')
122
+ throw err;
123
+ if (signal?.aborted)
124
+ throw err;
125
+ networkError = err;
126
+ }
127
+ // Offline (or the CDN is down): the last manifest that loaded from this URL.
128
+ const hit = cache ? await cache.match(manifestKey(url)).catch(() => undefined) : undefined;
129
+ if (hit)
130
+ return JSON.parse(await hit.text());
131
+ if (networkError instanceof ScannerError)
132
+ throw networkError;
133
+ throw new ScannerError('NETWORK', `manifest ${url} could not be fetched: ${errorText(networkError)}`, { cause: networkError });
134
+ },
135
+ async evictExcept(keep) {
136
+ if (!cache)
137
+ return 0;
138
+ const wanted = new Set(keep.map(fileKey));
139
+ let removed = 0;
140
+ for (const req of await cache.keys()) {
141
+ if (req.url.startsWith(`${KEY_ORIGIN}/sha256/`) && !wanted.has(req.url)) {
142
+ if (await cache.delete(req.url))
143
+ removed++;
144
+ }
145
+ }
146
+ return removed;
147
+ },
148
+ };
149
+ }
150
+ /** The page's Cache API cache, or null where there is none (or it is blocked). */
151
+ export async function openDefaultCache() {
152
+ try {
153
+ if (typeof caches === 'undefined')
154
+ return null;
155
+ const c = await caches.open(CACHE_NAME);
156
+ return {
157
+ match: (key) => c.match(key),
158
+ put: (key, response) => c.put(key, response),
159
+ delete: (key) => c.delete(key),
160
+ keys: () => c.keys(),
161
+ };
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ }
167
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/identify/assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { PixelImage } from './preprocess.js';
2
+ /** A crop this large is not one card; refuse it before allocating its pixels. */
3
+ export declare const MAX_CROP_PIXELS = 40000000;
4
+ export type CropDecoder = (crop: Blob) => Promise<PixelImage>;
5
+ export declare const decodeCrop: CropDecoder;
6
+ //# sourceMappingURL=decode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.d.ts","sourceRoot":"","sources":["../../src/identify/decode.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,iFAAiF;AACjF,eAAO,MAAM,eAAe,WAAa,CAAA;AAEzC,MAAM,MAAM,WAAW,GAAG,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;AAE7D,eAAO,MAAM,UAAU,EAAE,WAkBxB,CAAA"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Crop `Blob` → pixels, in a Worker or on the main thread.
3
+ *
4
+ * Decoded without colour management (`colorSpaceConversion: 'none'`) and
5
+ * without premultiplied alpha, as the Python reference (PIL) reads a JPEG: an
6
+ * embedded ICC profile would otherwise shift every value the gallery was
7
+ * embedded from. JPEG decoders still differ by a unit here and there between
8
+ * libjpeg builds; that difference is the decoder's, not the resize's.
9
+ */
10
+ import { ScannerError } from '../errors.js';
11
+ /** A crop this large is not one card; refuse it before allocating its pixels. */
12
+ export const MAX_CROP_PIXELS = 40_000_000;
13
+ export const decodeCrop = async (crop) => {
14
+ if (typeof createImageBitmap !== 'function')
15
+ throw new ScannerError('UNSUPPORTED_DEVICE', 'createImageBitmap is not available');
16
+ let bitmap;
17
+ try {
18
+ bitmap = await createImageBitmap(crop, { colorSpaceConversion: 'none', premultiplyAlpha: 'none' });
19
+ }
20
+ catch (err) {
21
+ throw new ScannerError('INVALID_OPTIONS', 'a crop could not be decoded as an image', { cause: err });
22
+ }
23
+ try {
24
+ const { width, height } = bitmap;
25
+ if (width * height > MAX_CROP_PIXELS)
26
+ throw new ScannerError('INVALID_OPTIONS', `a crop of ${width}x${height} is too large`);
27
+ const ctx = context2d(width, height);
28
+ ctx.drawImage(bitmap, 0, 0);
29
+ const image = ctx.getImageData(0, 0, width, height);
30
+ return { width, height, channels: 4, data: image.data };
31
+ }
32
+ finally {
33
+ bitmap.close();
34
+ }
35
+ };
36
+ function context2d(width, height) {
37
+ const opts = { willReadFrequently: true, alpha: false };
38
+ if (typeof OffscreenCanvas !== 'undefined') {
39
+ const ctx = new OffscreenCanvas(width, height).getContext('2d', opts);
40
+ if (ctx)
41
+ return ctx;
42
+ }
43
+ if (typeof document !== 'undefined') {
44
+ const canvas = document.createElement('canvas');
45
+ canvas.width = width;
46
+ canvas.height = height;
47
+ const ctx = canvas.getContext('2d', opts);
48
+ if (ctx)
49
+ return ctx;
50
+ }
51
+ throw new ScannerError('UNSUPPORTED_DEVICE', 'no 2D canvas to decode crops with');
52
+ }
53
+ //# sourceMappingURL=decode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decode.js","sourceRoot":"","sources":["../../src/identify/decode.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAG3C,iFAAiF;AACjF,MAAM,CAAC,MAAM,eAAe,GAAG,UAAU,CAAA;AAIzC,MAAM,CAAC,MAAM,UAAU,GAAgB,KAAK,EAAE,IAAI,EAAE,EAAE;IACpD,IAAI,OAAO,iBAAiB,KAAK,UAAU;QAAE,MAAM,IAAI,YAAY,CAAC,oBAAoB,EAAE,oCAAoC,CAAC,CAAA;IAC/H,IAAI,MAAmB,CAAA;IACvB,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,EAAE,EAAE,oBAAoB,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,CAAC,CAAA;IACpG,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,yCAAyC,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAA;IACtG,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAAA;QAChC,IAAI,KAAK,GAAG,MAAM,GAAG,eAAe;YAAE,MAAM,IAAI,YAAY,CAAC,iBAAiB,EAAE,aAAa,KAAK,IAAI,MAAM,eAAe,CAAC,CAAA;QAC5H,MAAM,GAAG,GAAG,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QACpC,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;QAC3B,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QACnD,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAA;IACzD,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,KAAK,EAAE,CAAA;IAChB,CAAC;AACH,CAAC,CAAA;AAID,SAAS,SAAS,CAAC,KAAa,EAAE,MAAc;IAC9C,MAAM,IAAI,GAAG,EAAE,kBAAkB,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAW,CAAA;IAChE,IAAI,OAAO,eAAe,KAAK,WAAW,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QACrE,IAAI,GAAG;YAAE,OAAO,GAAG,CAAA;IACrB,CAAC;IACD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QAC/C,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;QACpB,MAAM,CAAC,MAAM,GAAG,MAAM,CAAA;QACtB,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAoC,CAAA;QAC5E,IAAI,GAAG;YAAE,OAAO,GAAG,CAAA;IACrB,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,oBAAoB,EAAE,mCAAmC,CAAC,CAAA;AACnF,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { type AssetStore } from './assets.js';
2
+ import { type CropDecoder } from './decode.js';
3
+ import { type Manifest } from './manifest.js';
4
+ import { type RuntimeEnv, type RuntimeOptions } from './runtime.js';
5
+ import type { DeviceEngineInfo, IdentifyResult } from './types.js';
6
+ /** What the engine must load: plain data, so it crosses a Worker boundary. */
7
+ export interface EngineConfig {
8
+ readonly manifestUrl: string;
9
+ /** The lease's versions and shard; a manifest that differs is refused. */
10
+ readonly expect: {
11
+ readonly modelVersion: string;
12
+ readonly indexVersion: string;
13
+ readonly shard: string;
14
+ };
15
+ readonly runtime: RuntimeOptions;
16
+ }
17
+ export interface DeviceEngine {
18
+ readonly info: DeviceEngineInfo;
19
+ identify(crops: readonly Blob[], top: number): Promise<IdentifyResult[]>;
20
+ release(): Promise<void>;
21
+ }
22
+ export interface EngineDeps {
23
+ readonly assets: AssetStore;
24
+ readonly runtimeEnv: RuntimeEnv;
25
+ readonly decode: CropDecoder;
26
+ readonly now: () => number;
27
+ }
28
+ export declare function defaultEngineDeps(): Promise<EngineDeps>;
29
+ /** Checks the manifest against what the lease grants (versions are locked together). */
30
+ export declare function checkManifestAgainstLease(manifest: Manifest, expect: EngineConfig['expect']): void;
31
+ export declare function loadDeviceEngine(config: EngineConfig, deps: EngineDeps): Promise<DeviceEngine>;
32
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/identify/engine.ts"],"names":[],"mappings":"AAOA,OAAO,EAAsC,KAAK,UAAU,EAAkB,MAAM,aAAa,CAAA;AACjG,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,aAAa,CAAA;AAC1D,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE5D,OAAO,EAA4D,KAAK,UAAU,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAA;AAG7H,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAElE,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;IACzG,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAA;CACjC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAA;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;IACxE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAA;IAC3B,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAA;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAA;CAC3B;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,UAAU,CAAC,CAQ7D;AAED,wFAAwF;AACxF,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,GAAG,IAAI,CAUlG;AAED,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,YAAY,CAAC,CAqCpG"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * The on-device identification engine: manifest → model session + shard →
3
+ * crops → answers. Runs wherever it is constructed; `worker.ts` runs it in a
4
+ * dedicated Worker (the default), `identifier.ts` talks to it through the
5
+ * `DeviceEngine` interface either way.
6
+ */
7
+ import { ScannerError } from '../errors.js';
8
+ import { createAssetStore, openDefaultCache } from './assets.js';
9
+ import { decodeCrop } from './decode.js';
10
+ import { parseManifest } from './manifest.js';
11
+ import { preprocessCrop } from './preprocess.js';
12
+ import { createEmbedBackend, defaultRuntimeEnv } from './runtime.js';
13
+ import { decodeShard, parseIds } from './shard.js';
14
+ import { toResult, topK } from './search.js';
15
+ export async function defaultEngineDeps() {
16
+ const doFetch = (url, init) => fetch(url, init);
17
+ return {
18
+ assets: createAssetStore({ fetch: doFetch, cache: await openDefaultCache() }),
19
+ runtimeEnv: defaultRuntimeEnv(),
20
+ decode: decodeCrop,
21
+ now: () => performance.now(),
22
+ };
23
+ }
24
+ /** Checks the manifest against what the lease grants (versions are locked together). */
25
+ export function checkManifestAgainstLease(manifest, expect) {
26
+ if (manifest.shard !== expect.shard) {
27
+ throw new ScannerError('DOMAIN_NOT_ALLOWED', `manifest is for shard ${manifest.shard}, the lease grants ${expect.shard}`);
28
+ }
29
+ if (manifest.modelVersion !== expect.modelVersion || manifest.indexVersion !== expect.indexVersion) {
30
+ throw new ScannerError('MODEL_INTEGRITY', `manifest is model ${manifest.modelVersion} / index ${manifest.indexVersion}, the lease is ${expect.modelVersion} / ${expect.indexVersion}`);
31
+ }
32
+ }
33
+ export async function loadDeviceEngine(config, deps) {
34
+ const { assets } = deps;
35
+ const manifest = parseManifest(await assets.manifest(config.manifestUrl), config.manifestUrl);
36
+ checkManifestAgainstLease(manifest, config.expect);
37
+ const [indexBytes, idsBytes] = await Promise.all([assets.get(manifest.index), assets.get(manifest.ids)]);
38
+ let idsDoc;
39
+ try {
40
+ idsDoc = JSON.parse(new TextDecoder().decode(idsBytes));
41
+ }
42
+ catch {
43
+ throw new ScannerError('MODEL_INTEGRITY', 'ids sidecar is not JSON');
44
+ }
45
+ const expectShard = {
46
+ domain: manifest.shard,
47
+ modelVersion: manifest.modelVersion,
48
+ indexVersion: manifest.indexVersion,
49
+ rows: manifest.rows,
50
+ dim: manifest.dims,
51
+ };
52
+ const shard = decodeShard(indexBytes, parseIds(idsDoc, expectShard), expectShard);
53
+ const used = [manifest.index.sha256, manifest.ids.sha256];
54
+ const backend = await createEmbedBackend(manifest, async (model) => {
55
+ used.push(model.sha256);
56
+ return assets.get(model);
57
+ }, config.runtime, deps.runtimeEnv);
58
+ // Older versions' files go only once this one has loaded.
59
+ await assets.evictExcept(used).catch(() => 0);
60
+ const engine = makeEngine(manifest, shard, backend, deps);
61
+ await engine.warmUp();
62
+ return engine;
63
+ }
64
+ function makeEngine(manifest, shard, backend, deps) {
65
+ const { width, height, mean, std, orientations } = manifest.preprocessing;
66
+ const norm = { mean, std };
67
+ const plane = 3 * width * height;
68
+ const perCrop = orientations.length;
69
+ const thresholds = { notACardBelow: manifest.notACardBelow, confidentMargin: manifest.confidentMargin };
70
+ let perCropMs = Number.NaN;
71
+ let released = false;
72
+ const info = () => ({
73
+ backend: backend.backend,
74
+ precision: backend.precision,
75
+ threads: backend.threads,
76
+ perCropMs,
77
+ modelVersion: manifest.modelVersion,
78
+ indexVersion: manifest.indexVersion,
79
+ shard: manifest.shard,
80
+ rows: shard.rows,
81
+ });
82
+ async function run(images, top) {
83
+ if (released)
84
+ throw new ScannerError('INVALID_OPTIONS', 'the device engine was released');
85
+ const n = images.length;
86
+ if (n === 0)
87
+ return [];
88
+ const input = new Float32Array(n * perCrop * plane);
89
+ images.forEach((img, i) => preprocessCrop(img, { width, height }, norm, orientations, input, i * perCrop * plane));
90
+ const emb = await backend.embed(input, n * perCrop);
91
+ const dims = manifest.dims;
92
+ const k = Math.max(top, 2);
93
+ const results = [];
94
+ for (let i = 0; i < n; i++) {
95
+ const queries = emb.subarray(i * perCrop * dims, (i + 1) * perCrop * dims);
96
+ results.push(toResult(i, topK(shard.matrix, dims, queries, k), shard.ids, top, thresholds));
97
+ }
98
+ return results;
99
+ }
100
+ return {
101
+ get info() {
102
+ return info();
103
+ },
104
+ async identify(crops, top) {
105
+ const images = await Promise.all(crops.map((c) => deps.decode(c)));
106
+ const t0 = deps.now();
107
+ const results = await run(images, top);
108
+ // Keep the per-crop time current: a device that heats up slows down.
109
+ const ms = (deps.now() - t0) / Math.max(1, crops.length);
110
+ perCropMs = Number.isFinite(perCropMs) ? perCropMs * 0.7 + ms * 0.3 : ms;
111
+ return results;
112
+ },
113
+ async warmUp() {
114
+ // The first run pays for shader compilation and allocation; time the second.
115
+ const blank = { width, height, channels: 3, data: new Uint8Array(width * height * 3) };
116
+ await run([blank], 2);
117
+ const t0 = deps.now();
118
+ await run([blank], 2);
119
+ perCropMs = deps.now() - t0;
120
+ },
121
+ async release() {
122
+ released = true;
123
+ await backend.release();
124
+ },
125
+ };
126
+ }
127
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/identify/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ import { type Ed25519Verify, type LeasePublicKey } from '../lease/verify.js';
2
+ import type { DomainCode, Lease, LeaseGrant, LeaseRequest } from '../types.js';
3
+ import { type DrainResult } from '../usage/meter.js';
4
+ import { type UsageStore } from '../usage/store.js';
5
+ import { type DeviceEngine, type EngineConfig } from './engine.js';
6
+ import type { RuntimeOptions } from './runtime.js';
7
+ import { type ServerFetch } from './server.js';
8
+ import { type DeviceEngineInfo, type IdentificationMode, type IdentifierStatus, type IdentifyCallOptions, type IdentifyOutcome } from './types.js';
9
+ import { type WorkerLike } from './workerProtocol.js';
10
+ import type { UsageBatch } from '../types.js';
11
+ export interface IdentifierOptions {
12
+ /** The shard to identify against; the lease must grant it. */
13
+ readonly domain: DomainCode;
14
+ /** The service's lease-signing public keys (JWK `x`, optionally with `kid`). */
15
+ readonly leasePublicKeys: readonly LeasePublicKey[];
16
+ /** The first lease, as the host's backend returned it. Later ones go through `setLease`. */
17
+ readonly lease?: LeaseGrant;
18
+ /** Base URL of the identify route, `{serverUrl}/v1/identify`. Omit or null: the device only. */
19
+ readonly serverUrl?: string | null;
20
+ /** Default `auto`. `local` never sends an image. */
21
+ readonly identification?: IdentificationMode;
22
+ /** `auto` uses the server when the device takes longer than this per crop (no WebGPU). Default 300. */
23
+ readonly slowDeviceBudgetMs?: number;
24
+ /** The server's deadline while the device engine is not loaded yet. Default 8000. */
25
+ readonly serverTimeoutMs?: number;
26
+ readonly runtime?: RuntimeOptions;
27
+ /**
28
+ * Where the device engine runs: `worker` (default where `Worker` exists), `inline`
29
+ * (this thread; tests, or a host that already runs the SDK in its own Worker), or a
30
+ * factory for a Worker the host builds from `@dejadex/web/identify-worker`.
31
+ */
32
+ readonly engine?: 'worker' | 'inline' | (() => WorkerLike);
33
+ /** Download and warm the device engine as soon as a lease is set. Default true. */
34
+ readonly preload?: boolean;
35
+ /** The page origin the lease must allow. Default `location.origin`. */
36
+ readonly origin?: string | null;
37
+ /**
38
+ * Default IndexedDB (`dejadex-usage`), memory where it is unavailable. The
39
+ * default store is the identifier's own: `destroy()` closes it. A store passed
40
+ * here is the host's: `destroy()` leaves it open, and the host closes it.
41
+ */
42
+ readonly usageStore?: UsageStore;
43
+ /** Background failures (a device engine that failed to load). Never called from `identify`. */
44
+ readonly onError?: (error: Error) => void;
45
+ /** @internal Test seams. */
46
+ readonly fetch?: ServerFetch;
47
+ /** @internal */
48
+ readonly now?: () => number;
49
+ /** @internal */
50
+ readonly online?: () => boolean;
51
+ /** @internal */
52
+ readonly loadEngine?: (config: EngineConfig) => Promise<DeviceEngine>;
53
+ /** @internal */
54
+ readonly verify?: Ed25519Verify;
55
+ }
56
+ export interface Identifier {
57
+ /** At most 9 crops (JPEG for the server path). Always resolves. */
58
+ identify(crops: readonly Blob[], options?: IdentifyCallOptions): Promise<IdentifyOutcome>;
59
+ /** Verifies and installs a lease (rejects with a `ScannerError` when it is not valid here). */
60
+ setLease(grant: LeaseGrant): Promise<Lease>;
61
+ /** The identifier's own session id, used when a call names none. */
62
+ readonly session: string;
63
+ /** Starts a new default session: cards seen before are counted again. */
64
+ newSession(): string;
65
+ /** Loads (or awaits) the device engine for the current lease. */
66
+ prepare(): Promise<DeviceEngineInfo>;
67
+ status(): IdentifierStatus;
68
+ /** Hands queued usage to `deliver` (the host forwards it to its backend). See `UsageMeter.drain`. */
69
+ drainUsage(deliver: (batch: UsageBatch) => Promise<void>): Promise<DrainResult>;
70
+ pendingUsage(): Promise<number>;
71
+ /**
72
+ * The body the host sends its backend to renew: the current lease and the
73
+ * last report `seq` of it already delivered (null before the first lease).
74
+ * Drain usage first, or the service refuses the renewal (USAGE_UNREPORTED).
75
+ *
76
+ * `previousLeaseId` names another lease to renew instead: one the host
77
+ * stored but has not set here (it expired, or this page never identified).
78
+ * Null asks as a device with no lease.
79
+ */
80
+ leaseRequest(previousLeaseId?: string | null): Promise<LeaseRequest>;
81
+ /**
82
+ * Releases the device engine and its Worker, and closes the usage store the
83
+ * identifier opened itself (never one passed as `options.usageStore`). Later
84
+ * calls resolve `unavailable`, and the usage methods reject once that store
85
+ * is closed. Drain usage first: what is still queued stays on the device
86
+ * until a later identifier drains it or the host deletes `dejadex-usage`.
87
+ */
88
+ destroy(): Promise<void>;
89
+ }
90
+ export declare function createIdentifier(options: IdentifierOptions): Identifier;
91
+ //# sourceMappingURL=identifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identifier.d.ts","sourceRoot":"","sources":["../../src/identify/identifier.ts"],"names":[],"mappings":"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"}