pi-kiosk-shared 2.2.98 → 2.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/README.md +102 -9
- package/dist/barcode/normalizeScanPayload.d.ts +13 -1
- package/dist/barcode/normalizeScanPayload.d.ts.map +1 -1
- package/dist/barcode/normalizeScanPayload.js +95 -2
- package/dist/barcode/normalizeScanPayload.js.map +1 -1
- package/dist/barcode-scanner.d.ts +4 -1
- package/dist/barcode-scanner.d.ts.map +1 -1
- package/dist/barcode-scanner.js +4 -1
- package/dist/barcode-scanner.js.map +1 -1
- package/dist/hooks/decodeStillImage.d.ts +13 -0
- package/dist/hooks/decodeStillImage.d.ts.map +1 -0
- package/dist/hooks/decodeStillImage.js +258 -0
- package/dist/hooks/decodeStillImage.js.map +1 -0
- package/dist/hooks/scannerPlatformProfile.d.ts +23 -0
- package/dist/hooks/scannerPlatformProfile.d.ts.map +1 -0
- package/dist/hooks/scannerPlatformProfile.js +86 -0
- package/dist/hooks/scannerPlatformProfile.js.map +1 -0
- package/dist/hooks/selectEngine.d.ts +6 -4
- package/dist/hooks/selectEngine.d.ts.map +1 -1
- package/dist/hooks/useBarcodeScanner.d.ts +9 -29
- package/dist/hooks/useBarcodeScanner.d.ts.map +1 -1
- package/dist/hooks/useBarcodeScanner.js +211 -90
- package/dist/hooks/useBarcodeScanner.js.map +1 -1
- package/dist/tenant-entitlements/applySimpleStateDependencyImplications.d.ts.map +1 -1
- package/dist/tenant-entitlements/applySimpleStateDependencyImplications.js +2 -14
- package/dist/tenant-entitlements/applySimpleStateDependencyImplications.js.map +1 -1
- package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.d.ts.map +1 -1
- package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.js +11 -9
- package/dist/tenant-entitlements/applyTenantScopeToSimpleStates.js.map +1 -1
- package/dist/tenant-entitlements/capabilityEntitlementRequirements.d.ts +2 -2
- package/dist/tenant-entitlements/capabilityEntitlementRequirements.d.ts.map +1 -1
- package/dist/tenant-entitlements/capabilityEntitlementRequirements.js +10 -11
- package/dist/tenant-entitlements/capabilityEntitlementRequirements.js.map +1 -1
- package/dist/tenant-entitlements/catalog.d.ts +1 -1
- package/dist/tenant-entitlements/catalog.d.ts.map +1 -1
- package/dist/tenant-entitlements/catalog.js +8 -34
- package/dist/tenant-entitlements/catalog.js.map +1 -1
- package/dist/tenant-entitlements/coreRequiredPolicyAxes.d.ts +6 -0
- package/dist/tenant-entitlements/coreRequiredPolicyAxes.d.ts.map +1 -0
- package/dist/tenant-entitlements/coreRequiredPolicyAxes.js +29 -0
- package/dist/tenant-entitlements/coreRequiredPolicyAxes.js.map +1 -0
- package/dist/tenant-entitlements/entitlementSimpleStateMapping.d.ts.map +1 -1
- package/dist/tenant-entitlements/entitlementSimpleStateMapping.js +11 -0
- package/dist/tenant-entitlements/entitlementSimpleStateMapping.js.map +1 -1
- package/dist/tenant-entitlements/index.d.ts +3 -0
- package/dist/tenant-entitlements/index.d.ts.map +1 -1
- package/dist/tenant-entitlements/index.js +2 -0
- package/dist/tenant-entitlements/index.js.map +1 -1
- package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.d.ts +17 -0
- package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.d.ts.map +1 -0
- package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.js +54 -0
- package/dist/tenant-entitlements/legacyAnalyticsBlockMigration.js.map +1 -0
- package/dist/tenant-entitlements/types.d.ts +4 -4
- package/dist/tenant-entitlements/types.d.ts.map +1 -1
- package/dist/tenant-entitlements/types.js +4 -8
- package/dist/tenant-entitlements/types.js.map +1 -1
- 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.
|
|
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.100**). 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.
|
|
77
|
+
**Honesty (SSOT):** Live monorepo source is `shared/package.json` **2.2.100** (ZBar WASM primary + still-image snap). Camera consumers (`admin-app`, `rpapp-customer`, `rpapp-pickup`) pin **`^2.2.100`**; `rpapp-kiosk` pins **`^2.2.100`** 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.
|
|
81
|
+
When frontends use a **file tarball** pin (instead of registry `^2.2.100`):
|
|
82
82
|
|
|
83
83
|
```json
|
|
84
|
-
"pi-kiosk-shared": "file:../shared/pi-kiosk-shared-2.2.
|
|
84
|
+
"pi-kiosk-shared": "file:../shared/pi-kiosk-shared-2.2.100.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.
|
|
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
|
-
|
|
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
|
-
**
|
|
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.
|
|
117
|
+
**Registry-only / Railway app-only clones:** with **registry `^2.2.100` 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.
|
|
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.2.100**.
|
|
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
|
|
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
|
-
*
|
|
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":"
|
|
1
|
+
{"version":3,"file":"normalizeScanPayload.js","sourceRoot":"","sources":["../../src/barcode/normalizeScanPayload.ts"],"names":[],"mappings":"AAMA,MAAM,kBAAkB,GAAG,YAAY,CAAC;AAExC,SAAS,aAAa,CAAC,KAAa;IAClC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAa;IACtC,IAAI,GAAG,GAAG,EAAE,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClC,SAAS;QACX,CAAC;QACD,GAAG,IAAI,IAAI,CAAC;IACd,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,qBAAqB,CAAC,UAAkB;IAC/C,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;QAChF,GAAG,IAAI,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO,CAAC,EAAE,GAAG,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAc;IAC3C,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACpD,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,qBAAqB,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC;AAC/C,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,OAAO,IAAI,MAAM,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,qBAAqB,CAAC,KAAK,CAAC,EAAE,CAAC;YACjC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACxC,IAAI,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAW;IAC9C,OAAO,iBAAiB,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,SAAS,kBAAkB,CAAC,aAA0C;IACpE,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,KAAK,KAAK,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAW,EACX,aAA0C;IAE1C,MAAM,SAAS,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAC;IAC5C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,EAAE,CAAC;QACvC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;YAC9C,MAAM,UAAU,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;YAC/C,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;gBACxB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,UAAU,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,4BAA4B;AAC5B,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC"}
|
|
@@ -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,
|
|
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"}
|
package/dist/barcode-scanner.js
CHANGED
|
@@ -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,
|
|
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"}
|