pi-kiosk-shared 2.2.99 → 2.3.2

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 (60) hide show
  1. package/README.md +102 -9
  2. package/dist/barcode/normalizeScanPayload.d.ts +13 -1
  3. package/dist/barcode/normalizeScanPayload.d.ts.map +1 -1
  4. package/dist/barcode/normalizeScanPayload.js +95 -2
  5. package/dist/barcode/normalizeScanPayload.js.map +1 -1
  6. package/dist/barcode-scanner.d.ts +4 -1
  7. package/dist/barcode-scanner.d.ts.map +1 -1
  8. package/dist/barcode-scanner.js +4 -1
  9. package/dist/barcode-scanner.js.map +1 -1
  10. package/dist/hooks/decodeStillImage.d.ts +13 -0
  11. package/dist/hooks/decodeStillImage.d.ts.map +1 -0
  12. package/dist/hooks/decodeStillImage.js +258 -0
  13. package/dist/hooks/decodeStillImage.js.map +1 -0
  14. package/dist/hooks/scannerPlatformProfile.d.ts +23 -0
  15. package/dist/hooks/scannerPlatformProfile.d.ts.map +1 -0
  16. package/dist/hooks/scannerPlatformProfile.js +86 -0
  17. package/dist/hooks/scannerPlatformProfile.js.map +1 -0
  18. package/dist/hooks/selectEngine.d.ts +6 -4
  19. package/dist/hooks/selectEngine.d.ts.map +1 -1
  20. package/dist/hooks/useBarcodeScanner.d.ts +8 -36
  21. package/dist/hooks/useBarcodeScanner.d.ts.map +1 -1
  22. package/dist/hooks/useBarcodeScanner.js +198 -88
  23. package/dist/hooks/useBarcodeScanner.js.map +1 -1
  24. package/dist/sentry/initSentry.d.ts.map +1 -1
  25. package/dist/sentry/initSentry.js +5 -5
  26. package/dist/sentry/initSentry.js.map +1 -1
  27. package/dist/tenant-entitlements/applySimpleStateDependencyImplications.d.ts.map +1 -1
  28. package/dist/tenant-entitlements/applySimpleStateDependencyImplications.js +2 -14
  29. package/dist/tenant-entitlements/applySimpleStateDependencyImplications.js.map +1 -1
  30. package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.d.ts.map +1 -1
  31. package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.js +21 -9
  32. package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.js.map +1 -1
  33. package/dist/tenant-entitlements/capabilityEntitlementRequirements.d.ts +2 -2
  34. package/dist/tenant-entitlements/capabilityEntitlementRequirements.d.ts.map +1 -1
  35. package/dist/tenant-entitlements/capabilityEntitlementRequirements.js +10 -11
  36. package/dist/tenant-entitlements/capabilityEntitlementRequirements.js.map +1 -1
  37. package/dist/tenant-entitlements/catalog.d.ts +1 -1
  38. package/dist/tenant-entitlements/catalog.d.ts.map +1 -1
  39. package/dist/tenant-entitlements/catalog.js +8 -34
  40. package/dist/tenant-entitlements/catalog.js.map +1 -1
  41. package/dist/tenant-entitlements/coreRequiredPolicyAxes.d.ts +6 -0
  42. package/dist/tenant-entitlements/coreRequiredPolicyAxes.d.ts.map +1 -0
  43. package/dist/tenant-entitlements/coreRequiredPolicyAxes.js +29 -0
  44. package/dist/tenant-entitlements/coreRequiredPolicyAxes.js.map +1 -0
  45. package/dist/tenant-entitlements/entitlementSimpleStateMapping.d.ts.map +1 -1
  46. package/dist/tenant-entitlements/entitlementSimpleStateMapping.js +11 -0
  47. package/dist/tenant-entitlements/entitlementSimpleStateMapping.js.map +1 -1
  48. package/dist/tenant-entitlements/index.d.ts +3 -0
  49. package/dist/tenant-entitlements/index.d.ts.map +1 -1
  50. package/dist/tenant-entitlements/index.js +2 -0
  51. package/dist/tenant-entitlements/index.js.map +1 -1
  52. package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.d.ts +17 -0
  53. package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.d.ts.map +1 -0
  54. package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.js +54 -0
  55. package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.js.map +1 -0
  56. package/dist/tenant-entitlements/types.d.ts +4 -4
  57. package/dist/tenant-entitlements/types.d.ts.map +1 -1
  58. package/dist/tenant-entitlements/types.js +4 -8
  59. package/dist/tenant-entitlements/types.js.map +1 -1
  60. package/package.json +2 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@ Shared types, API contracts, and error classes for the Pi Kiosk system.
9
9
 
10
10
  ## Temporary retention
11
11
 
12
- `up-backend` keeps production `react` / `react-dom` until cold/start paths consume a Node-safe published or overlaid main barrel. Do **not** drop those deps until that consume path is proven on the version actually installed (registry tarball from `npm view`, or monorepo overlay of sibling `../shared` **2.2.97**). Same caveat as `up-backend/docs/DEPLOYMENT/DEPLOY_SEPARATE_REPOS.md`.
12
+ `up-backend` keeps production `react` / `react-dom` until cold/start paths consume a Node-safe published or overlaid main barrel. Do **not** drop those deps until that consume path is proven on the version actually installed (registry tarball from `npm view`, or monorepo overlay of sibling `../shared` **2.3.0**). Same caveat as `up-backend/docs/DEPLOYMENT/DEPLOY_SEPARATE_REPOS.md`.
13
13
 
14
14
  ## Installation
15
15
 
@@ -74,14 +74,14 @@ Frontends import React modules from `/ui`. The **target** main barrel (local ove
74
74
 
75
75
  ## Local monorepo overlay
76
76
 
77
- **Honesty (SSOT):** Live monorepo source is `shared/package.json` **2.2.97** (ZBar WASM primary). All five consumers (`admin-app`, `rpapp-kiosk`, `rpapp-customer`, `rpapp-pickup`, `up-backend`) currently pin **`^2.2.97`** from npm registry in `package.json` and lockfiles (post-publish). Sibling overlay via `ensureDist.mjs` still runs when `../shared` exists (preferred local dev). Optional Strategy A tarball bootstrap below applies when you intentionally pin `file:../shared/pi-kiosk-shared-<version>.tgz` instead — see [Consumer lock tarball bootstrap](#consumer-lock-tarball-bootstrap).
77
+ **Honesty (SSOT):** Live monorepo source is `shared/package.json` **2.3.0** (ZBar WASM primary + still-image snap). Camera consumers (`admin-app`, `rpapp-customer`, `rpapp-pickup`) pin **`^2.3.0`**; `rpapp-kiosk` pins **`^2.3.0`** for contract sync (HID wedge only — no camera hook). `up-backend` may lag registry until next publish — overlay via `ensureDist.mjs` when sibling `../shared` exists.
78
78
 
79
79
  ### Consumer lock tarball bootstrap (Strategy A — optional)
80
80
 
81
- When frontends use a **file tarball** pin (instead of registry `^2.2.97`):
81
+ When frontends use a **file tarball** pin (instead of registry `^2.3.0`):
82
82
 
83
83
  ```json
84
- "pi-kiosk-shared": "file:../shared/pi-kiosk-shared-2.2.97.tgz"
84
+ "pi-kiosk-shared": "file:../shared/pi-kiosk-shared-2.3.0.tgz"
85
85
  ```
86
86
 
87
87
  The tarball is **gitignored** (`*.tgz` in repo root `.gitignore`) and is **not** in VCS. A fresh clone has no pack file until you build it:
@@ -93,7 +93,9 @@ npm run publish:local # build:clean + npm pack → shared/pi-kiosk-shared-<ver
93
93
 
94
94
  **Strict `npm ci` in a consumer** (file-pin layout; lockfile-only install without relying on overlay alone):
95
95
 
96
- 1. Run `publish:local` in `shared` (step above).
96
+ 1. From repo root, run bootstrap SSOT (repack tarball + ephemeral lock integrity patch):
97
+ `node scripts/workspace/bootstrap-pi-kiosk-shared-tarball.mjs`
98
+ — wraps `shared` `publish:local` and patches consumer `package-lock.json` hashes in-place. **Do not commit** patched integrity fields.
97
99
  2. **Stop local dev servers** on Windows if they lock `node_modules` binaries (common `npm ci` failure).
98
100
  3. From a consumer (`admin-app`, `rpapp-customer`, `rpapp-pickup`, `rpapp-kiosk`):
99
101
 
@@ -103,13 +105,16 @@ $env:HUSKY='0'
103
105
  npm ci
104
106
  ```
105
107
 
106
- After repacking, refresh lock integrity in **every** consumer if tarball bytes changed: `$env:HUSKY='0'; npm install pi-kiosk-shared@file:../shared/pi-kiosk-shared-<version>.tgz` (updates `package-lock.json` `integrity`).
108
+ **Version bump — committed lock refresh (manual):** when `shared/package.json` version changes, run in **every** consumer and commit the result:
109
+ `$env:HUSKY='0'; npm install pi-kiosk-shared@file:../shared/pi-kiosk-shared-<version>.tgz` (updates committed `package-lock.json` pins and `integrity`).
107
110
 
108
- **CI without sibling `shared/` source (file-pin layout):** pipeline must run `cd shared && npm run publish:local` before consumer `npm ci`.
111
+ **Ephemeral lock patch (bootstrap SSOT):** `node scripts/workspace/bootstrap-pi-kiosk-shared-tarball.mjs` repacks the tarball and **patches consumer `package-lock.json` integrity in-place** so the next `npm ci` succeeds. Patches are local-only — **do not commit** bootstrap-dirtied hashes. Run bootstrap **before** local consumer `npm ci` when using file-pin layout. Bootstrap does **not** replace version-bump lock refresh; on bump still run the manual install above in each consumer and commit new pins.
112
+
113
+ **CI without sibling `shared/` source (file-pin layout):** every consumer workflow job runs [`.github/actions/bootstrap-pi-kiosk-shared-tarball`](../.github/actions/bootstrap-pi-kiosk-shared-tarball/action.yml) (SSOT: `node scripts/workspace/bootstrap-pi-kiosk-shared-tarball.mjs` → `shared npm ci && npm run publish:local` + lock integrity patch) before consumer `npm ci`. Root `cert:install:all` and `scripts/workspace/bootstrap.mjs` call the same script. See `hardening-gates.yml`, `retail-v1-cert.yml`, `rpapp-pickup-e2e.yml`.
109
114
 
110
115
  **Local dev (preferred):** sibling `../shared` + `postinstall`/`prepare` overlay via `ensureDist.mjs` — tarball optional when the monorepo layout is complete and overlay runs on install.
111
116
 
112
- **Registry-only / Railway app-only clones:** with **registry `^2.2.97` pins** (current monorepo `package.json`/locks), bare **`npm ci` succeeds** on app-only checkouts — no sibling tarball required. If you use **monorepo `file:` tarball pins** instead, bare `npm ci` **fails** on app-only clones (missing pack file). **`prebuildShared.mjs` runs on `predev` / `prebuild` only** — npm has already finished `install`/`ci` by then, and the script does **not** rewrite `package.json` or `package-lock.json` (it side-installs `pi-kiosk-shared` into `node_modules` with `--no-save`, which does **not** make a later strict `npm ci` succeed on `file:` pins). App-only options for `file:` pins: **(a)** run `cd shared && npm run publish:local` and ensure `../shared/pi-kiosk-shared-<version>.tgz` exists on disk before consumer `npm ci` (see [Consumer lock tarball bootstrap](#consumer-lock-tarball-bootstrap)), or **(b)** commit app-only `package.json`/lock with registry pins (same as current monorepo). Confirm with `npm view pi-kiosk-shared version` at deploy time.
117
+ **Registry-only / Railway app-only clones:** with **registry `^2.3.0` pins** (current monorepo `package.json`/locks), bare **`npm ci` succeeds** on app-only checkouts — no sibling tarball required. If you use **monorepo `file:` tarball pins** instead, bare `npm ci` **fails** on app-only clones (missing pack file). **`prebuildShared.mjs` runs on `predev` / `prebuild` only** — npm has already finished `install`/`ci` by then, and the script does **not** rewrite `package.json` or `package-lock.json` (it side-installs `pi-kiosk-shared` into `node_modules` with `--no-save`, which does **not** make a later strict `npm ci` succeed on `file:` pins). App-only options for `file:` pins: **(a)** run `cd shared && npm run publish:local` and ensure `../shared/pi-kiosk-shared-<version>.tgz` exists on disk before consumer `npm ci` (see [Consumer lock tarball bootstrap](#consumer-lock-tarball-bootstrap)), or **(b)** commit app-only `package.json`/lock with registry pins (same as current monorepo). Confirm with `npm view pi-kiosk-shared version` at deploy time.
113
118
 
114
119
  **Documented cold path (monorepo):** `npm ci` / `npm install` in each app runs lifecycle hooks → `scripts/overlaySharedIfPresent.mjs` → `shared/scripts/ensureDist.mjs` when sibling `../shared` exists:
115
120
 
@@ -146,7 +151,7 @@ npm run gate:main-barrel-node-safe
146
151
 
147
152
  From `shared/`, prove the documented cold path:
148
153
 
149
- 1. **DIAGNOSTIC** — `npm pack` a known Node-safe registry tarball (`COLD_VERSION` in `prove-pi-kiosk-shared-cold-overlay.mjs`, currently `2.2.82` — intentional historical diagnostic fixture, not the live pin) in a temp dir and assert COLD_BAD markers (never installs into a consumer with `--ignore-scripts`). Live monorepo source is `shared/package.json` **2.2.97**.
154
+ 1. **DIAGNOSTIC** — `npm pack` a known Node-safe registry tarball (`COLD_VERSION` in `prove-pi-kiosk-shared-cold-overlay.mjs`, currently `2.2.82` — intentional historical diagnostic fixture, not the live pin) in a temp dir and assert COLD_BAD markers (never installs into a consumer with `--ignore-scripts`). Live monorepo source is `shared/package.json` **2.3.0**.
150
155
  2. **PASS** — wipe that consumer’s `node_modules/pi-kiosk-shared`, then `npm install` with **scripts on** (no package args) so `prepare` / `postinstall` overlays during install; assert Node-safe barrel + `NODE_IMPORT_OK`.
151
156
 
152
157
  ```bash
@@ -156,6 +161,94 @@ npm run prove:cold-overlay -- --all # serial across all consumers (never paral
156
161
 
157
162
  Do **not** use `--ignore-scripts` then hand `npm run prepare` as the heal. Evidence is overwritten as markdown under `.cursor/artifacts/pi-kiosk-shared-cold-overlay-proof*.md` (not `.log`).
158
163
 
164
+ ## Barcode camera scanner (runbook)
165
+
166
+ Cross-app live camera + still-image decode lives in `pi-kiosk-shared/barcode-scanner` (`useBarcodeScanner`, snap APIs, ZBar WASM boot). Backend barcode contracts and admin/pickup routes: [up-backend/docs/BARCODE/BARCODE_IMPLEMENTATION.md](../up-backend/docs/BARCODE/BARCODE_IMPLEMENTATION.md).
167
+
168
+ Human QA subset (fixture EAN `8593807360153`): [docs/SCANNER_QA.md](./docs/SCANNER_QA.md). Manual UI skill: [rpapp-manual-ui-testing](../../.cursor/skills/rpapp-manual-ui-testing/SKILL.md).
169
+
170
+ ### Boot — `initZbarWasm` first import
171
+
172
+ Each camera surface must configure ZBar WASM **before** any `useBarcodeScanner` session:
173
+
174
+ | App | Boot module | Entry |
175
+ |-----|-------------|-------|
176
+ | `admin-app` | `src/initZbarWasm.ts` | first import in `src/main.tsx` |
177
+ | `rpapp-customer` | `src/initZbarWasm.ts` | first import in `src/main.tsx` |
178
+ | `rpapp-pickup` | `src/initZbarWasm.ts` | first import in `src/main.tsx` |
179
+
180
+ Pattern (all three apps):
181
+
182
+ ```ts
183
+ import './initZbarWasm'; // must be first import in main.tsx
184
+ ```
185
+
186
+ `initZbarWasm.ts` calls `setZbarWasmUrl(wasmUrl)` with Vite-resolved `@undecaf/zbar-wasm/dist/zbar.wasm?url` so the binary is same-origin and CSP-safe. **Do not** lazy-load this module on first scan — boot failure surfaces as `degradedMode` or `zbarBootstrapFailed` copy.
187
+
188
+ ### Decode engines
189
+
190
+ | Engine | Role |
191
+ |--------|------|
192
+ | **`zbar-wasm`** | Primary when boot succeeds — ZBar C/C++ WASM (`@undecaf/zbar-wasm`); retail EAN/UPC/Code128 + QR per `formatProfile`. |
193
+ | **`zxing`** | Parallel assist (`@zxing/browser`) starting on **frame 0** while ZBar runs; also primary live path in **degraded** boot. Distance optical zoom still waits 1.5s (`SCANNER_DISTANCE_ZOOM_DELAY_MS`). |
194
+ | **`native-detector`** | Chromium `BarcodeDetector` parallel assist only — never the sole engine. |
195
+
196
+ Engine type: `ScannerEngine` in `selectEngine.ts`. Selection is internal to `useBarcodeScanner`; surfaces read `engine`, `zxingAssistActive`, and **`degradedMode`** — not engine alone.
197
+
198
+ ### `degradedMode` contract (G-P4)
199
+
200
+ `UseBarcodeScannerReturn.degradedMode: boolean`:
201
+
202
+ | Value | Meaning |
203
+ |-------|---------|
204
+ | `false` | ZBar WASM URL configured and ZBar loop started — full mode. `@zxing` / native may still run as **assist** (`zxingAssistActive === true` does **not** imply degraded). |
205
+ | `true` | ZBar URL unset or ZBar boot failed — live decode continues on @zxing (+ native assist when available). |
206
+
207
+ **UI rule:** show `runningDegraded` i18n when **`degradedMode === true`**. Do **not** infer degraded from `engine === 'zxing'` or `zxingAssistActive` alone.
208
+
209
+ ### Still-image snap APIs
210
+
211
+ Exported from `pi-kiosk-shared/barcode-scanner` (`decodeStillImage.ts`):
212
+
213
+ | API | Use |
214
+ |-----|-----|
215
+ | `decodeBarcodeFromImageFile(file, formatProfile?)` | File upload / gallery pick — preprocessing (grayscale, Otsu, optional 2× upscale) then ZBar → @zxing → native. |
216
+ | `decodeBarcodeFromVideoFrame(video, formatProfile?, canvas?)` | Preview-frame grab (admin/customer/pickup snap CTAs) — same engine cascade on a single frame. |
217
+
218
+ Both return `StillDecodeResult | null` (`payload`, `engine`). Payloads pass through `prepareScanPayloadForEmit` (trim, control chars, GTIN check digit, UPC-A↔EAN-13). Golden CI fixture: `shared/src/__tests__/fixtures/ean-8593807360153.png`.
219
+
220
+ ### WASM network-only precache policy (v1)
221
+
222
+ **`zbar.wasm` is network-only** — not precached by the service worker.
223
+
224
+ - Vite PWA `globPatterns` in admin, customer, and pickup intentionally omit `*.wasm` (e.g. `**/*.{js,css,html,ico,png,svg,woff2}` only).
225
+ - WASM is fetched on the **first scan session** after `initZbarWasm` sets the URL — not at install time.
226
+ - **Deploy acceptance:** DevTools → Application → Cache Storage → no precached `zbar.wasm`; Network tab → HTTP **200** on first camera open per origin.
227
+ - Single WASM source: `@undecaf/zbar-wasm` via `setZbarWasmUrl` — no second WASM from a polyfill package.
228
+
229
+ ### Ops failure recovery
230
+
231
+ | Symptom | Likely cause | Recovery |
232
+ |---------|--------------|----------|
233
+ | `cameraZbarBootstrapFailed` / `scannerZbarBootstrapFailed` | WASM 404, CSP block, or corrupt cache | Hard refresh (Ctrl+F5); Network tab → `zbar.wasm` **200**; clear site data if SW served stale shell. |
234
+ | `degradedMode: true`, slow decode | ZBar boot failed | Same as above; user can still decode via @zxing or **preview/file snap**. |
235
+ | Camera permission denied | `NotAllowedError` | Manual entry; preview hidden on deny (row 14 matrix). |
236
+ | Blank preview, no error | Insecure context (`http://` non-localhost) | Serve over HTTPS or localhost. |
237
+
238
+ ### Deploy checklist (camera apps)
239
+
240
+ Applies to **admin-app**, **rpapp-customer**, **rpapp-pickup** (kiosk HID wedge unchanged).
241
+
242
+ 1. Bump `shared/package.json`; `npm run build` in `shared`; publish per project workflow.
243
+ 2. Bump `pi-kiosk-shared` in admin, customer, pickup; redeploy all three on Railway.
244
+ 3. Verify built bundle exports `decodeBarcodeFromImageFile` / `decodeBarcodeFromVideoFrame` (grep `dist` or smoke import).
245
+ 4. **CSP per origin** — meta `Content-Security-Policy` must allow WASM compile:
246
+ - `script-src` includes `'wasm-unsafe-eval'` (all three apps).
247
+ - Admin also keeps `'unsafe-inline'` for bootstrap scripts; production `connect-src` rewritten from `VITE_API_URL` / `VITE_WS_URL`.
248
+ - Deploy HTTP headers may tighten further; WASM MIME must be `application/wasm`.
249
+ 5. **`zbar.wasm` HTTP 200** on each deployed origin (admin, customer, pickup) — open scanner once, confirm in Network.
250
+ 6. **No precached `zbar.wasm`** in Cache Storage (network-only policy above).
251
+
159
252
  ## Development
160
253
 
161
254
  ```bash
@@ -1,5 +1,17 @@
1
1
  /**
2
- * Client-side pre-trim only — server BarcodeNormalizationService remains authoritative.
2
+ * Client-side scan payload sanitize + retail GTIN pre-check.
3
+ * Server BarcodeNormalizationService remains authoritative on assign/lookup.
4
+ */
5
+ import type { BarcodeScannerFormatProfile } from '../hooks/scannerFormats.js';
6
+ /**
7
+ * Trim + strip ASCII control chars (client pre-trim only).
3
8
  */
4
9
  export declare function normalizeScanPayload(raw: string): string;
10
+ /**
11
+ * Prepare a decode payload for emit. Returns null when retail GTIN check fails
12
+ * (caller should continue scanning without onDecode).
13
+ */
14
+ export declare function prepareScanPayloadForEmit(raw: string, formatProfile: BarcodeScannerFormatProfile): string | null;
15
+ /** @internal test export */
16
+ export declare function isGtinCheckDigitValidForTest(digits: string): boolean;
5
17
  //# sourceMappingURL=normalizeScanPayload.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeScanPayload.d.ts","sourceRoot":"","sources":["../../src/barcode/normalizeScanPayload.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD"}
1
+ {"version":3,"file":"normalizeScanPayload.d.ts","sourceRoot":"","sources":["../../src/barcode/normalizeScanPayload.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,4BAA4B,CAAC;AAsE9E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAExD;AAMD;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,2BAA2B,GACzC,MAAM,GAAG,IAAI,CAsBf;AAED,4BAA4B;AAC5B,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEpE"}
@@ -1,7 +1,100 @@
1
+ const GTIN_DIGIT_PATTERN = /^[\d\s-]+$/;
2
+ function extractDigits(value) {
3
+ return value.replace(/\D/g, '');
4
+ }
5
+ function stripControlChars(value) {
6
+ let out = '';
7
+ for (const char of value) {
8
+ const code = char.charCodeAt(0);
9
+ if (code <= 0x1f || code === 0x7f) {
10
+ continue;
11
+ }
12
+ out += char;
13
+ }
14
+ return out;
15
+ }
16
+ function computeGtinCheckDigit(bodyDigits) {
17
+ let sum = 0;
18
+ for (let i = 0; i < bodyDigits.length; i += 1) {
19
+ const digit = Number.parseInt(bodyDigits[bodyDigits.length - 1 - i] ?? '0', 10);
20
+ sum += digit * (i % 2 === 0 ? 3 : 1);
21
+ }
22
+ return (10 - (sum % 10)) % 10;
23
+ }
24
+ function isValidGtinCheckDigit(digits) {
25
+ if (digits.length < 8 || digits.length > 14) {
26
+ return false;
27
+ }
28
+ const body = digits.slice(0, -1);
29
+ const check = Number.parseInt(digits.slice(-1), 10);
30
+ if (Number.isNaN(check)) {
31
+ return false;
32
+ }
33
+ return computeGtinCheckDigit(body) === check;
34
+ }
35
+ function normalizeGtinDigits(digits) {
36
+ if (!isValidGtinCheckDigit(digits)) {
37
+ return null;
38
+ }
39
+ if (digits.length === 8) {
40
+ return digits;
41
+ }
42
+ if (digits.length === 12) {
43
+ return `0${digits}`;
44
+ }
45
+ if (digits.length === 13) {
46
+ return digits;
47
+ }
48
+ if (digits.length === 14) {
49
+ const ean13 = digits.slice(1);
50
+ if (isValidGtinCheckDigit(ean13)) {
51
+ return ean13;
52
+ }
53
+ return digits;
54
+ }
55
+ if (digits.length < 13) {
56
+ const padded = digits.padStart(13, '0');
57
+ if (isValidGtinCheckDigit(padded)) {
58
+ return padded;
59
+ }
60
+ }
61
+ return digits;
62
+ }
1
63
  /**
2
- * Client-side pre-trim only — server BarcodeNormalizationService remains authoritative.
64
+ * Trim + strip ASCII control chars (client pre-trim only).
3
65
  */
4
66
  export function normalizeScanPayload(raw) {
5
- return raw.trim();
67
+ return stripControlChars(raw.trim());
68
+ }
69
+ function shouldValidateGtin(formatProfile) {
70
+ return formatProfile === 'retail' || formatProfile === 'all';
71
+ }
72
+ /**
73
+ * Prepare a decode payload for emit. Returns null when retail GTIN check fails
74
+ * (caller should continue scanning without onDecode).
75
+ */
76
+ export function prepareScanPayloadForEmit(raw, formatProfile) {
77
+ const sanitized = normalizeScanPayload(raw);
78
+ if (sanitized.length === 0) {
79
+ return null;
80
+ }
81
+ if (!shouldValidateGtin(formatProfile)) {
82
+ return sanitized;
83
+ }
84
+ if (GTIN_DIGIT_PATTERN.test(sanitized)) {
85
+ const digits = extractDigits(sanitized);
86
+ if (digits.length >= 8 && digits.length <= 14) {
87
+ const normalized = normalizeGtinDigits(digits);
88
+ if (normalized === null) {
89
+ return null;
90
+ }
91
+ return normalized;
92
+ }
93
+ }
94
+ return sanitized;
95
+ }
96
+ /** @internal test export */
97
+ export function isGtinCheckDigitValidForTest(digits) {
98
+ return isValidGtinCheckDigit(digits);
6
99
  }
7
100
  //# sourceMappingURL=normalizeScanPayload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalizeScanPayload.js","sourceRoot":"","sources":["../../src/barcode/normalizeScanPayload.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC;AACpB,CAAC"}
1
+ {"version":3,"file":"normalizeScanPayload.js","sourceRoot":"","sources":["../../src/barcode/normalizeScanPayload.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
- export { useBarcodeScanner, type UseBarcodeScannerMessages, type UseBarcodeScannerOptions, type UseBarcodeScannerReturn, type ScannerStatus, SCANNER_DISTANCE_ASSIST_DELAY_MS, SCANNER_DISTANCE_ZOOM_DELAY_MS, SCANNER_ZBAR_ZXING_ASSIST_DELAY_MS, } from './hooks/useBarcodeScanner.js';
1
+ export { useBarcodeScanner, type UseBarcodeScannerMessages, type UseBarcodeScannerOptions, type UseBarcodeScannerReturn, type ScannerStatus, SCANNER_DISTANCE_ASSIST_DELAY_MS, SCANNER_DISTANCE_ZOOM_DELAY_MS, SCANNER_ZBAR_ZXING_ASSIST_DELAY_MS, SCANNER_POST_DECODE_COOLDOWN_MS, } from './hooks/useBarcodeScanner.js';
2
2
  export type { ScannerEngine } from './hooks/selectEngine.js';
3
3
  export { resolveScannerFormatConfig, type BarcodeScannerFormatProfile, type ScannerFormatConfig, } from './hooks/scannerFormats.js';
4
4
  export { SCANNER_VIDEO_CONSTRAINTS, SCANNER_VIDEO_CONSTRAINT_FALLBACKS, openScannerMediaStream, applyScannerTrackEnhancements, applyScannerDistanceZoom, resolvePreferredOpticalZoom, SCANNER_OPTICAL_ZOOM_POLICY, } from './hooks/scannerCameraConstraints.js';
5
5
  export { setZbarWasmUrl, isZbarWasmUrlConfigured, } from './hooks/zbarWasmEngine.js';
6
+ export { resolveScannerPlatformProfile, type ScannerPlatformProfile, type ScannerPlatformProfileId, } from './hooks/scannerPlatformProfile.js';
7
+ export { decodeBarcodeFromImageFile, decodeBarcodeFromVideoFrame, preprocessStillImageData, type StillDecodeResult, } from './hooks/decodeStillImage.js';
8
+ export { normalizeScanPayload, prepareScanPayloadForEmit, } from './barcode/normalizeScanPayload.js';
6
9
  //# sourceMappingURL=barcode-scanner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"barcode-scanner.d.ts","sourceRoot":"","sources":["../src/barcode-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,gCAAgC,EAChC,8BAA8B,EAC9B,kCAAkC,GACnC,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"barcode-scanner.d.ts","sourceRoot":"","sources":["../src/barcode-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,aAAa,EAClB,gCAAgC,EAChC,8BAA8B,EAC9B,kCAAkC,EAClC,+BAA+B,GAChC,MAAM,8BAA8B,CAAC;AACtC,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,0BAA0B,EAC1B,KAAK,2BAA2B,EAChC,KAAK,mBAAmB,GACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,6BAA6B,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,GAC9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,EACxB,KAAK,iBAAiB,GACvB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAC"}
@@ -1,5 +1,8 @@
1
- export { useBarcodeScanner, SCANNER_DISTANCE_ASSIST_DELAY_MS, SCANNER_DISTANCE_ZOOM_DELAY_MS, SCANNER_ZBAR_ZXING_ASSIST_DELAY_MS, } from './hooks/useBarcodeScanner.js';
1
+ export { useBarcodeScanner, SCANNER_DISTANCE_ASSIST_DELAY_MS, SCANNER_DISTANCE_ZOOM_DELAY_MS, SCANNER_ZBAR_ZXING_ASSIST_DELAY_MS, SCANNER_POST_DECODE_COOLDOWN_MS, } from './hooks/useBarcodeScanner.js';
2
2
  export { resolveScannerFormatConfig, } from './hooks/scannerFormats.js';
3
3
  export { SCANNER_VIDEO_CONSTRAINTS, SCANNER_VIDEO_CONSTRAINT_FALLBACKS, openScannerMediaStream, applyScannerTrackEnhancements, applyScannerDistanceZoom, resolvePreferredOpticalZoom, SCANNER_OPTICAL_ZOOM_POLICY, } from './hooks/scannerCameraConstraints.js';
4
4
  export { setZbarWasmUrl, isZbarWasmUrlConfigured, } from './hooks/zbarWasmEngine.js';
5
+ export { resolveScannerPlatformProfile, } from './hooks/scannerPlatformProfile.js';
6
+ export { decodeBarcodeFromImageFile, decodeBarcodeFromVideoFrame, preprocessStillImageData, } from './hooks/decodeStillImage.js';
7
+ export { normalizeScanPayload, prepareScanPayloadForEmit, } from './barcode/normalizeScanPayload.js';
5
8
  //# sourceMappingURL=barcode-scanner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"barcode-scanner.js","sourceRoot":"","sources":["../src/barcode-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAKjB,gCAAgC,EAChC,8BAA8B,EAC9B,kCAAkC,GACnC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,0BAA0B,GAG3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"barcode-scanner.js","sourceRoot":"","sources":["../src/barcode-scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAKjB,gCAAgC,EAChC,8BAA8B,EAC9B,kCAAkC,EAClC,+BAA+B,GAChC,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACL,0BAA0B,GAG3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,yBAAyB,EACzB,kCAAkC,EAClC,sBAAsB,EACtB,6BAA6B,EAC7B,wBAAwB,EACxB,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,cAAc,EACd,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,6BAA6B,GAG9B,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,0BAA0B,EAC1B,2BAA2B,EAC3B,wBAAwB,GAEzB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { BarcodeScannerFormatProfile } from './scannerFormats.js';
2
+ import './scannerNativeTypes.js';
3
+ export interface StillDecodeResult {
4
+ readonly payload: string;
5
+ readonly engine: 'zbar-wasm' | 'zxing' | 'native-detector';
6
+ }
7
+ /**
8
+ * Grayscale → contrast stretch → Otsu binarization → optional 2× upscale.
9
+ */
10
+ export declare function preprocessStillImageData(source: ImageData, upscale: boolean): ImageData;
11
+ export declare function decodeBarcodeFromImageFile(file: File, formatProfile?: BarcodeScannerFormatProfile): Promise<StillDecodeResult | null>;
12
+ export declare function decodeBarcodeFromVideoFrame(video: HTMLVideoElement, formatProfile?: BarcodeScannerFormatProfile, canvas?: HTMLCanvasElement): Promise<StillDecodeResult | null>;
13
+ //# sourceMappingURL=decodeStillImage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decodeStillImage.d.ts","sourceRoot":"","sources":["../../src/hooks/decodeStillImage.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAWvE,OAAO,yBAAyB,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,GAAG,iBAAiB,CAAC;CAC5D;AAgCD;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,SAAS,CAyDvF;AAkJD,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,IAAI,EACV,aAAa,GAAE,2BAAsC,GACpD,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAOnC;AAED,wBAAsB,2BAA2B,CAC/C,KAAK,EAAE,gBAAgB,EACvB,aAAa,GAAE,2BAAsC,EACrD,MAAM,CAAC,EAAE,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAyBnC"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * Still-image decode (file upload + video frame grab) with preprocessing pipeline.
3
+ */
4
+ import { scanImageData } from '@undecaf/zbar-wasm';
5
+ import { prepareScanPayloadForEmit } from '../barcode/normalizeScanPayload.js';
6
+ import { resolveScannerFormatConfig } from './scannerFormats.js';
7
+ import { resolveScannerPlatformProfile } from './scannerPlatformProfile.js';
8
+ import { buildMaxSensitivityScanPasses } from './scannerScanPasses.js';
9
+ import { createConfiguredZbarScanner, isZbarWasmUrlConfigured, } from './zbarWasmEngine.js';
10
+ import { createSensitiveZxingReader, } from './zxingJsSensitiveDecode.js';
11
+ import './scannerNativeTypes.js';
12
+ function otsuThreshold(histogram, total) {
13
+ let sum = 0;
14
+ for (let i = 0; i < 256; i += 1) {
15
+ sum += i * (histogram[i] ?? 0);
16
+ }
17
+ let sumB = 0;
18
+ let wB = 0;
19
+ let maxVar = 0;
20
+ let threshold = 0;
21
+ for (let t = 0; t < 256; t += 1) {
22
+ wB += histogram[t] ?? 0;
23
+ if (wB === 0) {
24
+ continue;
25
+ }
26
+ const wF = total - wB;
27
+ if (wF === 0) {
28
+ break;
29
+ }
30
+ sumB += t * (histogram[t] ?? 0);
31
+ const mB = sumB / wB;
32
+ const mF = (sum - sumB) / wF;
33
+ const varBetween = wB * wF * (mB - mF) * (mB - mF);
34
+ if (varBetween > maxVar) {
35
+ maxVar = varBetween;
36
+ threshold = t;
37
+ }
38
+ }
39
+ return threshold;
40
+ }
41
+ /**
42
+ * Grayscale → contrast stretch → Otsu binarization → optional 2× upscale.
43
+ */
44
+ export function preprocessStillImageData(source, upscale) {
45
+ const { width, height, data } = source;
46
+ const outW = upscale ? width * 2 : width;
47
+ const outH = upscale ? height * 2 : height;
48
+ const out = new ImageData(outW, outH);
49
+ const histogram = new Uint32Array(256);
50
+ let min = 255;
51
+ let max = 0;
52
+ for (let i = 0; i < data.length; i += 4) {
53
+ const r = data[i] ?? 0;
54
+ const g = data[i + 1] ?? 0;
55
+ const b = data[i + 2] ?? 0;
56
+ const gray = Math.round(0.299 * r + 0.587 * g + 0.114 * b);
57
+ min = Math.min(min, gray);
58
+ max = Math.max(max, gray);
59
+ histogram[gray] = (histogram[gray] ?? 0) + 1;
60
+ }
61
+ const range = Math.max(1, max - min);
62
+ const threshold = otsuThreshold(histogram, width * height);
63
+ const writePixel = (x, y, gray) => {
64
+ const stretched = Math.round(((gray - min) / range) * 255);
65
+ const bin = stretched > threshold ? 255 : 0;
66
+ const idx = (y * outW + x) * 4;
67
+ out.data[idx] = bin;
68
+ out.data[idx + 1] = bin;
69
+ out.data[idx + 2] = bin;
70
+ out.data[idx + 3] = 255;
71
+ };
72
+ if (!upscale) {
73
+ for (let y = 0; y < height; y += 1) {
74
+ for (let x = 0; x < width; x += 1) {
75
+ const i = (y * width + x) * 4;
76
+ const gray = Math.round(0.299 * (data[i] ?? 0) + 0.587 * (data[i + 1] ?? 0) + 0.114 * (data[i + 2] ?? 0));
77
+ writePixel(x, y, gray);
78
+ }
79
+ }
80
+ return out;
81
+ }
82
+ for (let y = 0; y < outH; y += 1) {
83
+ for (let x = 0; x < outW; x += 1) {
84
+ const sx = Math.min(width - 1, Math.floor(x / 2));
85
+ const sy = Math.min(height - 1, Math.floor(y / 2));
86
+ const i = (sy * width + sx) * 4;
87
+ const gray = Math.round(0.299 * (data[i] ?? 0) + 0.587 * (data[i + 1] ?? 0) + 0.114 * (data[i + 2] ?? 0));
88
+ writePixel(x, y, gray);
89
+ }
90
+ }
91
+ return out;
92
+ }
93
+ function decodeCanvasWithZxing(sourceCanvas, workCanvas, formatProfile) {
94
+ const formatConfig = resolveScannerFormatConfig(formatProfile);
95
+ const reader = createSensitiveZxingReader(formatConfig.zxingFormats);
96
+ const width = sourceCanvas.width;
97
+ const height = sourceCanvas.height;
98
+ if (width < 2 || height < 2) {
99
+ return null;
100
+ }
101
+ const ctx = workCanvas.getContext('2d', { willReadFrequently: true });
102
+ if (ctx === null) {
103
+ return null;
104
+ }
105
+ ctx.imageSmoothingEnabled = false;
106
+ const passes = buildMaxSensitivityScanPasses(width, height);
107
+ for (const pass of passes) {
108
+ const outW = Math.max(2, Math.floor(pass.sw * pass.scale));
109
+ const outH = Math.max(2, Math.floor(pass.sh * pass.scale));
110
+ workCanvas.width = outW;
111
+ workCanvas.height = outH;
112
+ ctx.clearRect(0, 0, outW, outH);
113
+ ctx.drawImage(sourceCanvas, pass.sx, pass.sy, pass.sw, pass.sh, 0, 0, outW, outH);
114
+ try {
115
+ const result = reader.decodeFromCanvas(workCanvas);
116
+ const text = result.getText();
117
+ if (text.length > 0) {
118
+ return text;
119
+ }
120
+ }
121
+ catch {
122
+ // try next pass
123
+ }
124
+ }
125
+ return null;
126
+ }
127
+ async function decodeImageDataWithEngines(imageData, canvas, formatProfile) {
128
+ const formatConfig = resolveScannerFormatConfig(formatProfile);
129
+ const platform = resolveScannerPlatformProfile();
130
+ const ctx = canvas.getContext('2d', { willReadFrequently: true });
131
+ if (ctx === null) {
132
+ return null;
133
+ }
134
+ const attempts = [imageData];
135
+ attempts.push(preprocessStillImageData(imageData, false));
136
+ if (platform.id === 'ios-safari' || platform.id === 'crios-ios' || platform.preferPreviewSnap) {
137
+ attempts.push(preprocessStillImageData(imageData, true));
138
+ }
139
+ const workCanvas = typeof document !== 'undefined' ? document.createElement('canvas') : canvas;
140
+ for (const attempt of attempts) {
141
+ canvas.width = attempt.width;
142
+ canvas.height = attempt.height;
143
+ ctx.putImageData(attempt, 0, 0);
144
+ if (platform.nativeEnabled && typeof window !== 'undefined' && window.BarcodeDetector !== undefined) {
145
+ try {
146
+ const supported = await window.BarcodeDetector.getSupportedFormats();
147
+ const needs = formatConfig.nativeFormats;
148
+ const ok = needs.every((f) => supported.includes(f));
149
+ if (ok) {
150
+ const detector = new window.BarcodeDetector({ formats: [...formatConfig.nativeFormats] });
151
+ const results = await detector.detect(canvas);
152
+ const first = results[0];
153
+ if (first !== undefined) {
154
+ const prepared = prepareScanPayloadForEmit(first.rawValue, formatProfile);
155
+ if (prepared !== null) {
156
+ return { payload: prepared, engine: 'native-detector' };
157
+ }
158
+ }
159
+ }
160
+ }
161
+ catch {
162
+ // continue
163
+ }
164
+ }
165
+ if (isZbarWasmUrlConfigured()) {
166
+ try {
167
+ const scanner = await createConfiguredZbarScanner(formatProfile);
168
+ try {
169
+ const symbols = await scanImageData(attempt, scanner);
170
+ for (const symbol of symbols) {
171
+ const text = symbol.decode().trim();
172
+ if (text.length > 0) {
173
+ const prepared = prepareScanPayloadForEmit(text, formatProfile);
174
+ if (prepared !== null) {
175
+ return { payload: prepared, engine: 'zbar-wasm' };
176
+ }
177
+ }
178
+ }
179
+ }
180
+ finally {
181
+ scanner.destroy();
182
+ }
183
+ }
184
+ catch {
185
+ // continue
186
+ }
187
+ }
188
+ const zxingText = decodeCanvasWithZxing(canvas, workCanvas, formatProfile);
189
+ if (zxingText !== null) {
190
+ const prepared = prepareScanPayloadForEmit(zxingText, formatProfile);
191
+ if (prepared !== null) {
192
+ return { payload: prepared, engine: 'zxing' };
193
+ }
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ async function loadImageFromFile(file) {
199
+ if (typeof document === 'undefined') {
200
+ throw new Error('decodeBarcodeFromImageFile requires a browser document');
201
+ }
202
+ const url = URL.createObjectURL(file);
203
+ try {
204
+ const img = await new Promise((resolve, reject) => {
205
+ const el = new Image();
206
+ el.onload = () => resolve(el);
207
+ el.onerror = () => reject(new Error('Failed to load image'));
208
+ el.src = url;
209
+ });
210
+ const canvas = document.createElement('canvas');
211
+ canvas.width = img.naturalWidth;
212
+ canvas.height = img.naturalHeight;
213
+ const ctx = canvas.getContext('2d');
214
+ if (ctx === null) {
215
+ throw new Error('Canvas 2d unavailable');
216
+ }
217
+ ctx.drawImage(img, 0, 0);
218
+ return ctx.getImageData(0, 0, canvas.width, canvas.height);
219
+ }
220
+ finally {
221
+ URL.revokeObjectURL(url);
222
+ }
223
+ }
224
+ export async function decodeBarcodeFromImageFile(file, formatProfile = 'retail') {
225
+ if (typeof document === 'undefined') {
226
+ return null;
227
+ }
228
+ const imageData = await loadImageFromFile(file);
229
+ const canvas = document.createElement('canvas');
230
+ return decodeImageDataWithEngines(imageData, canvas, formatProfile);
231
+ }
232
+ export async function decodeBarcodeFromVideoFrame(video, formatProfile = 'retail', canvas) {
233
+ const width = video.videoWidth;
234
+ const height = video.videoHeight;
235
+ if (width < 2 || height < 2) {
236
+ return null;
237
+ }
238
+ const decodeCanvas = canvas ?? (typeof document !== 'undefined' ? document.createElement('canvas') : null);
239
+ if (decodeCanvas === null) {
240
+ return null;
241
+ }
242
+ const ctx = decodeCanvas.getContext('2d', { willReadFrequently: true });
243
+ if (ctx === null) {
244
+ return null;
245
+ }
246
+ decodeCanvas.width = width;
247
+ decodeCanvas.height = height;
248
+ ctx.drawImage(video, 0, 0, width, height);
249
+ let imageData;
250
+ try {
251
+ imageData = ctx.getImageData(0, 0, width, height);
252
+ }
253
+ catch {
254
+ return null;
255
+ }
256
+ return decodeImageDataWithEngines(imageData, decodeCanvas, formatProfile);
257
+ }
258
+ //# sourceMappingURL=decodeStillImage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decodeStillImage.js","sourceRoot":"","sources":["../../src/hooks/decodeStillImage.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAE/E,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AACjE,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,0BAA0B,GAC3B,MAAM,6BAA6B,CAAC;AACrC,OAAO,yBAAyB,CAAC;AAOjC,SAAS,aAAa,CAAC,SAAsB,EAAE,KAAa;IAC1D,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,EAAE,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACxB,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YACb,SAAS;QACX,CAAC;QACD,MAAM,EAAE,GAAG,KAAK,GAAG,EAAE,CAAC;QACtB,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YACb,MAAM;QACR,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,EAAE,GAAG,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QACnD,IAAI,UAAU,GAAG,MAAM,EAAE,CAAC;YACxB,MAAM,GAAG,UAAU,CAAC;YACpB,SAAS,GAAG,CAAC,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAiB,EAAE,OAAgB;IAC1E,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACzC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC3C,MAAM,GAAG,GAAG,IAAI,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,MAAM,SAAS,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IACvC,IAAI,GAAG,GAAG,GAAG,CAAC;IACd,IAAI,GAAG,GAAG,CAAC,CAAC;IAEZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QACvB,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC;QAC3D,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QAC1B,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,EAAE,KAAK,GAAG,MAAM,CAAC,CAAC;IAE3D,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,CAAS,EAAE,IAAY,EAAQ,EAAE;QAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;QAC3D,MAAM,GAAG,GAAG,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;QACpB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;QACxB,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IAC1B,CAAC,CAAC;IAEF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC9B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACrB,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CACjF,CAAC;gBACF,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACnD,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACrB,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CACjF,CAAC;YACF,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,qBAAqB,CAC5B,YAA+B,EAC/B,UAA6B,EAC7B,aAA0C;IAE1C,MAAM,YAAY,GAAG,0BAA0B,CAAC,aAAa,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,0BAA0B,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;IACrE,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC;IACjC,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,GAAG,CAAC,qBAAqB,GAAG,KAAK,CAAC;IAClC,MAAM,MAAM,GAAG,6BAA6B,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC5D,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC;QACxB,UAAU,CAAC,MAAM,GAAG,IAAI,CAAC;QACzB,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAChC,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAClF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;YAC9B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB;QAClB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,0BAA0B,CACvC,SAAoB,EACpB,MAAyB,EACzB,aAA0C;IAE1C,MAAM,YAAY,GAAG,0BAA0B,CAAC,aAAa,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,6BAA6B,EAAE,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IAClE,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAgB,CAAC,SAAS,CAAC,CAAC;IAC1C,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC;IAC1D,IAAI,QAAQ,CAAC,EAAE,KAAK,YAAY,IAAI,QAAQ,CAAC,EAAE,KAAK,WAAW,IAAI,QAAQ,CAAC,iBAAiB,EAAE,CAAC;QAC9F,QAAQ,CAAC,IAAI,CAAC,wBAAwB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAE/F,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC7B,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC/B,GAAG,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAEhC,IAAI,QAAQ,CAAC,aAAa,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACpG,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,mBAAmB,EAAE,CAAC;gBACrE,MAAM,KAAK,GAAG,YAAY,CAAC,aAAa,CAAC;gBACzC,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;gBACrD,IAAI,EAAE,EAAE,CAAC;oBACP,MAAM,QAAQ,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;oBAC1F,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC9C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;oBACzB,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,MAAM,QAAQ,GAAG,yBAAyB,CAAC,KAAK,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;wBAC1E,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;4BACtB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC;wBAC1D,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW;YACb,CAAC;QACH,CAAC;QAED,IAAI,uBAAuB,EAAE,EAAE,CAAC;YAC9B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,2BAA2B,CAAC,aAAa,CAAC,CAAC;gBACjE,IAAI,CAAC;oBACH,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;oBACtD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;wBAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;wBACpC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;4BACpB,MAAM,QAAQ,GAAG,yBAAyB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;4BAChE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gCACtB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;4BACpD,CAAC;wBACH,CAAC;oBACH,CAAC;gBACH,CAAC;wBAAS,CAAC;oBACT,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW;YACb,CAAC;QACH,CAAC;QAED,MAAM,SAAS,GAAG,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;QAC3E,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,yBAAyB,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;YACrE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;gBACtB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YAChD,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,IAAU;IACzC,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,MAAM,GAAG,GAAG,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,IAAI,OAAO,CAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAClE,MAAM,EAAE,GAAG,IAAI,KAAK,EAAE,CAAC;YACvB,EAAE,CAAC,MAAM,GAAG,GAAS,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YACpC,EAAE,CAAC,OAAO,GAAG,GAAS,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC;YACnE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC;QAChC,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACzB,OAAO,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;YAAS,CAAC;QACT,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,IAAU,EACV,gBAA6C,QAAQ;IAErD,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACpC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAChD,MAAM,MAAM,GAAG,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;IAChD,OAAO,0BAA0B,CAAC,SAAS,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,KAAuB,EACvB,gBAA6C,QAAQ,EACrD,MAA0B;IAE1B,MAAM,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;IAC/B,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC;IACjC,IAAI,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,YAAY,GAChB,MAAM,IAAI,CAAC,OAAO,QAAQ,KAAK,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxF,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,YAAY,CAAC,KAAK,GAAG,KAAK,CAAC;IAC3B,YAAY,CAAC,MAAM,GAAG,MAAM,CAAC;IAC7B,GAAG,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,SAAoB,CAAC;IACzB,IAAI,CAAC;QACH,SAAS,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,0BAA0B,CAAC,SAAS,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;AAC5E,CAAC"}